Skip to content

feat: support dash core 22 protocols#291

Open
HashEngineering wants to merge 8 commits intomasterfrom
feat/dash-core-22
Open

feat: support dash core 22 protocols#291
HashEngineering wants to merge 8 commits intomasterfrom
feat/dash-core-22

Conversation

@HashEngineering
Copy link
Collaborator

@HashEngineering HashEngineering commented Feb 12, 2026

Issue being fixed or feature implemented

  • dsq inv
  • compressed headers

What was done?

How Has This Been Tested?

Breaking Changes

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • New Features

    • Compressed block headers support (headers2/getheaders2/sendheaders2) with on-wire compression and context-aware handling.
    • Per-peer and global toggles to enable compressed header downloads; peers can advertise support.
    • Duplicate-detection for CoinJoin queue inventory items.
    • New benchmark tool to measure header download performance.
  • Bug Fixes

    • Inventory parsing updated to recognize CoinJoin queue items.
  • Refactor

    • Protocol/version constants updated to support new header features.

@HashEngineering HashEngineering self-assigned this Feb 12, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds DIP-0025 compressed header support (new compressed header classes, messages, serializer entries, peer/peergroup integration and flags), introduces a CoinJoinQueue inventory type and queue checks, updates protocol/version constants, and adds a headers-download benchmark example.

Changes

Cohort / File(s) Summary
Compressed header core
core/src/main/java/org/bitcoinj/core/CompressedBlockHeader.java, core/src/main/java/org/bitcoinj/core/CompressedHeaderContext.java
Add CompressedBlockHeader and CompressedHeaderContext to encode/decode DIP-0025 compressed headers and maintain per-stream version/previous-header state.
Compressed header messages & serializer
core/src/main/java/org/bitcoinj/core/Headers2Message.java, core/src/main/java/org/bitcoinj/core/GetHeaders2Message.java, core/src/main/java/org/bitcoinj/core/SendHeaders2Message.java, core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java
Introduce headers2/getheaders2/sendheaders2 message classes and register them in BitcoinSerializer; instantiate new message classes in factory.
Peer / PeerGroup integration
core/src/main/java/org/bitcoinj/core/Peer.java, core/src/main/java/org/bitcoinj/core/PeerGroup.java
Add useCompressedHeaders flag and API, detect peer support via version flags, prefer GetHeaders2/Headers2 flows when enabled/supported, adapt header request/download logic and processing entrypoints.
Inventory / CoinJoin queue
core/src/main/java/org/bitcoinj/core/InventoryItem.java, core/src/main/java/org/bitcoinj/core/ListMessage.java, core/src/main/java/org/bitcoinj/coinjoin/CoinJoinQueue.java, core/src/main/java/org/bitcoinj/coinjoin/CoinJoinBaseManager.java, core/src/main/java/org/bitcoinj/coinjoin/CoinJoinClientQueueManager.java, core/src/main/java/org/bitcoinj/coinjoin/utils/CoinJoinManager.java
Add InventoryItem.Type.CoinJoinQueue (code 32) and map it in ListMessage; add CoinJoinQueue.getHash(); expose alreadyHave(InventoryItem) in CoinJoinBaseManager and delegate/use it in CoinJoinManager pre-processing to request unseen CoinJoinQueue items.
Protocol versions & version message
core/src/main/java/org/bitcoinj/core/NetworkParameters.java, core/src/main/java/org/bitcoinj/core/VersionMessage.java
Add protocol version constants (NO_LEGACY_ISLOCK_PROTO_VERSION, DSQ_INV_VERSION, INCREASE_MAX_HEADERS2_VERSION) and update CURRENT; introduce NODE_HEADERS_COMPRESSED service flag and isCompressedHeadersSupported() plus string reporting.
Example / benchmark
examples/src/main/java/org/bitcoinj/examples/DownloadHeadersBenchmark.java
Add CLI benchmark to compare header-download performance for v1 vs v2 (compressed) headers with progress tracking and reporting.

Sequence Diagram(s)

sequenceDiagram
    participant PeerGroup
    participant Peer
    participant RemotePeer
    participant CompressedHeaderContext
    participant Blockchain

    PeerGroup->>Peer: setUseCompressedHeaders(true)
    Peer->>RemotePeer: Send GetHeaders2Message(locator, stopHash)
    RemotePeer-->>Peer: Send Headers2Message(compressed headers)
    Peer->>CompressedHeaderContext: parse first header (full) & update context
    Peer->>CompressedHeaderContext: parse subsequent CompressedBlockHeader entries
    CompressedHeaderContext-->>Peer: provide reconstructed Block headers
    Peer->>Blockchain: hand off Headers-equivalent for processing
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐰 I nibble code in moonlit queues,
compressed hops and hashed parquetues,
peers trade smaller header bites,
CoinJoin whispers through the nights,
I benchmark hops and munch delights. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.87% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (12 files):

⚔️ core/build.gradle (content)
⚔️ core/src/main/java/org/bitcoinj/coinjoin/CoinJoinBaseManager.java (content)
⚔️ core/src/main/java/org/bitcoinj/coinjoin/CoinJoinClientQueueManager.java (content)
⚔️ core/src/main/java/org/bitcoinj/coinjoin/CoinJoinQueue.java (content)
⚔️ core/src/main/java/org/bitcoinj/coinjoin/utils/CoinJoinManager.java (content)
⚔️ core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java (content)
⚔️ core/src/main/java/org/bitcoinj/core/InventoryItem.java (content)
⚔️ core/src/main/java/org/bitcoinj/core/ListMessage.java (content)
⚔️ core/src/main/java/org/bitcoinj/core/NetworkParameters.java (content)
⚔️ core/src/main/java/org/bitcoinj/core/Peer.java (content)
⚔️ core/src/main/java/org/bitcoinj/core/PeerGroup.java (content)
⚔️ core/src/main/java/org/bitcoinj/core/VersionMessage.java (content)

These conflicts must be resolved before merging into master.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: support dash core 22 protocols' accurately reflects the main objective of the PR, which adds support for Dash Core 22 protocols including DSQ inventory messages and compressed headers support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/dash-core-22
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch feat/dash-core-22
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@core/src/main/java/org/bitcoinj/core/CompressedBlockHeader.java`:
- Around line 25-42: Update the CompressedBlockHeader class Javadoc to correct
the encoding of bits 0-2: replace the current "Bits 0-2: Version handling (index
0-6 into last 7 distinct versions, or 7 = new version follows)" with text that
states "Bits 0-2: Version handling (0 = new version follows in the stream; 1–7 =
index into the table of last 7 distinct versions at value-1, i.e. 1 maps to
index 0, 7 maps to index 6)". Ensure the corrected wording appears in the class
comment for CompressedBlockHeader and keep the DIP-0025 reference intact.

In `@core/src/main/java/org/bitcoinj/core/Peer.java`:
- Around line 858-867: processHeaders2 currently converts a Headers2Message to a
HeadersMessage and calls processHeaders(), but that uses
HeadersMessage.MAX_HEADERS (2000) whereas Headers2Message.MAX_HEADERS is 8000,
causing incorrect continuation logic; fix by making processHeaders take an
explicit maxHeaders parameter (or add an overload/processHeadersWithMax) and
change processHeaders2 to call it with Headers2Message.MAX_HEADERS, and update
the continuation checks inside processHeaders (the logic that compares header
count to MAX_HEADERS) to use the passed maxHeaders value instead of the
hardcoded HeadersMessage.MAX_HEADERS.
- Around line 2038-2046: The call to
VersionMessage.isCompressedHeadersSupported() fails because that method doesn't
exist; either add that method to VersionMessage or change
peerSupportsCompressedHeaders() to perform the support check directly. To fix:
implement VersionMessage.isCompressedHeadersSupported() (e.g., have it inspect
the version number or service flags inside VersionMessage and return true when
the peer advertises compressed headers), or update
Peer.peerSupportsCompressedHeaders() to use
getPeerVersionMessage().getProtocolVersion() (or the appropriate service-flag
accessor on VersionMessage) and compare against the compressed-headers
protocol/version constant; ensure you reference getPeerVersionMessage() and
VersionMessage in your change so callers remain the same.

In `@examples/src/main/java/org/bitcoinj/examples/DownloadHeadersBenchmark.java`:
- Around line 76-84: The switch over the string variable network currently falls
through to MainNetParams.get() for any unrecognized value, which can silently
run against mainnet; change the handling in the switch (or replace it with an
explicit lookup) so that only "testnet" maps to TestNet3Params.get() and
"mainnet" maps to MainNetParams.get(), and add an explicit error path for other
values that prints a clear error (including the invalid network string) and
exits/throws rather than defaulting to mainnet; update any references to params,
TestNet3Params.get(), and MainNetParams.get() accordingly.
- Around line 126-133: The progress callback in method progress currently logs
every time blocksSoFar % 10000 == 0 which includes the initial call with
blocksSoFar == 0 and near-zero elapsed causing headersPerSec to be NaN or
misleading; update the condition to require blocksSoFar > 0 in addition to
blocksSoFar % 10000 == 0 so the printf (which uses startTime and computes
headersPerSec from blocksSoFar / (elapsed / 1000.0)) only runs for positive
header counts, avoiding division-by-zero/NaN output while still updating
headerCount[0].
- Around line 136-165: Remove the duplicate report and dead code by keeping the
precise callback-based reporting in the ProgressTracker.doneHeaderDownload()
override and deleting the post-await reporting and unused variables in the main
thread: remove the second call to printReport(...) that occurs after
progressTracker.await() (and the unused endTime and redundant totalTimeMs
calculation there), relying on headerDoneTime and startTime computed in
doneHeaderDownload() to print the benchmark; locate symbols doneHeaderDownload,
printReport, progressTracker.await(), endTime, headerDoneTime and startTime to
remove the correct lines.
🧹 Nitpick comments (7)
examples/src/main/java/org/bitcoinj/examples/DownloadHeadersBenchmark.java (1)

60-60: Integer.parseInt can throw NumberFormatException on non-numeric input.

For a CLI tool, an uncaught NumberFormatException produces an unfriendly stack trace. A try-catch with a user-friendly error message would be more polished.

core/src/main/java/org/bitcoinj/core/PeerGroup.java (1)

1912-1912: Compressed headers setting is only propagated to newly connected peers.

Calling setUseCompressedHeaders(true) after peers are already connected won't update those existing peers. This matches the pattern for vBloomFilteringEnabled, but worth noting — callers should set this before starting the PeerGroup for consistent behavior.

core/src/main/java/org/bitcoinj/coinjoin/utils/CoinJoinManager.java (1)

90-90: Dropping final from context weakens immutability guarantees.

The context field was previously private final, ensuring it's set once in the constructor and never reassigned. Removing final opens the door to accidental reassignment. If there's no need to reassign context after construction, consider keeping it final.

core/src/main/java/org/bitcoinj/core/GetHeaders2Message.java (1)

48-61: equals/hashCode implementation is functional but could be simplified.

In equals, the locator.size() == other.locator.size() check is redundant when followed by locator.equals(other.locator), since equals on a list already checks size. In hashCode, consider using Objects.hash(version, stopHash, locator) for consistency with the broader codebase.

These are minor nits — the implementation is correct.

core/src/main/java/org/bitcoinj/core/Headers2Message.java (2)

111-111: log.info during parsing is too noisy for production.

This line will fire for every headers2 message received. With potentially frequent header downloads, this will pollute production logs. Downgrade to log.debug.

Proposed fix
-        log.info("Parsing headers2 message: numHeaders={}, payloadLength={}", numHeaders, payload.length);
+        log.debug("Parsing headers2 message: numHeaders={}, payloadLength={}", numHeaders, payload.length);

48-50: Remove commented-out debug artifact.

Line 50 contains //public static final int MAX_HEADERS_8000 = 2000; which appears to be a leftover from experimentation with a confusing name (the constant is named 8000 but valued 2000). Clean this up to avoid confusion.

core/src/main/java/org/bitcoinj/core/CompressedBlockHeader.java (1)

127-137: Remove debug comment block.

This block contains a raw example block dump that appears to be a debugging artifact. It should be removed before merging.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/src/main/java/org/bitcoinj/core/Peer.java (1)

19-19: ⚠️ Potential issue | 🟡 Minor

Duplicate import of com.google.common.collect.Lists.

Lists is imported at both line 19 and line 51.

Proposed fix
-import com.google.common.collect.Lists;
-
 import org.bitcoinj.coinjoin.CoinJoin;

Remove the first import at line 19, keeping the one at line 51 which is grouped with the other Guava imports.

Also applies to: 51-51

🤖 Fix all issues with AI agents
In `@core/src/main/java/org/bitcoinj/core/CompressedBlockHeader.java`:
- Around line 146-153: The code in CompressedBlockHeader's fallback branch
currently calls readUint32() when a version table index is missing, which will
consume bytes belonging to subsequent fields and corrupt parsing; change this to
throw a ProtocolException instead of reading from the stream. Locate the else
branch that checks context.getVersionTableSize() and inspects versionBits,
remove the readUint32() call and context.saveVersionAsMostRecent(version) there,
and throw a new ProtocolException with a clear message that the version table
index (include tableIndex, versionBits and table size via
context.getVersionTableSize()) is unavailable/invalid. Ensure the thrown
exception type is org.bitcoinj.core.ProtocolException so callers can handle it
appropriately.

In `@core/src/main/java/org/bitcoinj/core/Peer.java`:
- Around line 858-864: The current getMaxHeaders() uses isUseCompressedHeaders()
instead of the actual received message type, which can cause processHeaders to
assume the wrong limit; change processHeaders to accept a maxHeaders parameter
and call it with the correct constant from the caller: when handling a
HeadersMessage pass HeadersMessage.MAX_HEADERS, and when handling a
Headers2Message (or in processHeaders2 after conversion) pass
Headers2Message.MAX_HEADERS; update all callers (processMessage,
processHeaders2, etc.) to supply the appropriate MAX_HEADERS constant and remove
reliance on getMaxHeaders()/isUseCompressedHeaders() inside processHeaders.
🧹 Nitpick comments (3)
core/src/main/java/org/bitcoinj/core/CompressedBlockHeader.java (3)

109-119: Remove debug block comment containing raw block data.

This leftover debug note (lines 109–119) with a specific block hash, version, timestamps, etc. should be cleaned up before merge.


103-106: Empty parse() override is fragile.

The Message base class calls parse() in some constructors. The parsing constructor (line 70–79) deliberately bypasses this by calling super(params) instead of super(params, payload, offset), then manually sets fields and calls parseWithContext. This works but is subtle — if anyone later calls the standard Message constructor that triggers parse(), it will silently produce an uninitialized header. A defensive approach would be to throw in parse():

`@Override`
protected void parse() throws ProtocolException {
    throw new UnsupportedOperationException("Use parseWithContext() instead");
}

254-261: Local variable offset shadows Message.offset field.

Line 256 declares long offset = time - context.getPreviousTimestamp() which shadows the inherited this.offset field. This doesn't cause a bug here, but is a readability hazard — consider renaming to tsOffset or timeDelta.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/src/main/java/org/bitcoinj/core/VersionMessage.java (1)

356-359: ⚠️ Potential issue | 🟠 Major

Bug (pre-existing): NODE_COMPACT_FILTERS bit is never cleared — wrong constant used on line 358.

The NODE_COMPACT_FILTERS branch clears NODE_NETWORK_LIMITED instead of NODE_COMPACT_FILTERS, so compact-filters peers will always show a spurious "remaining" entry. Since you're already touching this method, this is a good opportunity to fix it.

🐛 Proposed fix
         if ((services & VersionMessage.NODE_COMPACT_FILTERS) == VersionMessage.NODE_COMPACT_FILTERS) {
             a.add("COMPACT_FILTERS");
-            services &= ~VersionMessage.NODE_NETWORK_LIMITED;
+            services &= ~VersionMessage.NODE_COMPACT_FILTERS;
         }

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.

1 participant