diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 03:16:00 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 03:16:00 +0000 |
| commit | 60538c5a935787521a5ad8c2a7dce52b5f250f3a (patch) | |
| tree | b78632a9d3b15b7a99567fe3c213d0bb00fd1731 /src/compactblock.h | |
| parent | 152c859b18b222a7c11a82b5485110e902267d40 (diff) | |
compactblock: import BIP152-style compact block relay from upstream 1.2.23
Brings src/compactblock.{cpp,h} and test_compactblock.cpp across verbatim
from the upstream-v1.2.23 tag, and gives them a build.
1.2.23 is not a newer release of the line this fork ships. It is a
lightweight tag (7ab5a8c) cut 2026-07-30, on a lineage that upstream/main
(e0be70e, 2026-08-20) does not contain and no remote branch carries -- 272
commits that are not ours, against 376 of ours that are not theirs, from a
merge-base at 56df983. Merging that tag would have deleted 18 files from
src/, selftest.{cpp,h} and the whole 14-test suite among them, so the
content is ported file by file instead.
Compact block relay is the piece worth having: a peer that advertises
sendcmpct gets 48-bit keyed short IDs per transaction rather than the
transaction bytes, and reconstructs the block from its own mempool. It is
purely additive on the wire -- a peer that never sends sendcmpct keeps
receiving full block messages, so no coordinated upgrade is needed.
This commit is the primitives and their unit test only. Nothing calls
BuildCompactBlock or TryReconstructBlock yet: the P2P message handlers
(sendcmpct/cmpctblock/getblocktxn/blocktxn) live in 1.2.23's main.cpp and
are deliberately left for a follow-up, so that no attacker-controlled bytes
reach this code until that wiring is reviewed on its own.
The files needed no API adaptation -- mapTransactions, BEGIN/END and
IMPLEMENT_SERIALIZE are unchanged across the divergence, and it compiled
clean first time. Upstream never wired test_compactblock.cpp into its own
Makefile, so it had never been built there; it links standalone the way
test_bip32 does, supplying its own mapTransactions and GetAppDir to keep
main.cpp out of the link. TEST_LIBS is overridable for the same reason
NODE_LIBS is: the pinned dep tree ships libsodium as a static archive only.
All 17 checks in test_compactblock pass, all 14 selftests still pass, and
the consensus constants are unchanged against e4f898c.
Diffstat (limited to 'src/compactblock.h')
| -rw-r--r-- | src/compactblock.h | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/src/compactblock.h b/src/compactblock.h new file mode 100644 index 0000000..745ee3b --- /dev/null +++ b/src/compactblock.h | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | // Copyright (c) 2026 Bitflash developers | ||
| 2 | // Distributed under the MIT/X11 software license. | ||
| 3 | // | ||
| 4 | // Compact block relay (BIP152-style). Instead of sending a peer's full set | ||
| 5 | // of transactions for a block they're about to receive -- almost all of | ||
| 6 | // which they already have in their mempool from normal tx relay -- send a | ||
| 7 | // short (48-bit, keyed-hash) ID per transaction instead. A peer that already | ||
| 8 | // has every referenced transaction reconstructs the full block locally from | ||
| 9 | // its mempool without any of the transaction bytes crossing the wire at | ||
| 10 | // all; only genuinely missing transactions (typically just the coinbase) | ||
| 11 | // need a follow-up round trip. | ||
| 12 | // | ||
| 13 | // Compatibility: this is purely additive. A peer that never sends | ||
| 14 | // "sendcmpct" is never sent a compact block -- it keeps getting full | ||
| 15 | // "block" messages exactly as before (see the getdata handler in | ||
| 16 | // main.cpp). Old, unupgraded nodes on the live network are completely | ||
| 17 | // unaffected; ProcessMessage's unknown-command handling means they simply | ||
| 18 | // never see the new message types. No coordinated network upgrade needed. | ||
| 19 | // | ||
| 20 | // Deliberately scoped down from full BIP152: no high-bandwidth (unsolicited | ||
| 21 | // push) mode, no differential-index encoding for prefilled transactions, | ||
| 22 | // only the current chain tip is ever offered in compact form (an old block | ||
| 23 | // is unlikely to still overlap much with any peer's current mempool, so the | ||
| 24 | // bandwidth win doesn't apply -- full "block" is sent for those, same as | ||
| 25 | // today). Only the coinbase is prefilled; every other transaction position | ||
| 26 | // carries a short ID. | ||
| 27 | |||
| 28 | #ifndef BITFLASH_COMPACTBLOCK_H | ||
| 29 | #define BITFLASH_COMPACTBLOCK_H | ||
| 30 | |||
| 31 | // Requires CBlock/CTransaction already visible -- #include "headers.h" (or | ||
| 32 | // main.h) before this file. Deliberately doesn't include main.h itself: | ||
| 33 | // main.h has no include guard (matches this codebase's existing headers, it | ||
| 34 | // relies on being pulled in exactly once via headers.h), so a second | ||
| 35 | // #include "main.h" here would break every translation unit that already | ||
| 36 | // brought it in via headers.h first. | ||
| 37 | #include <vector> | ||
| 38 | |||
| 39 | // The compact representation of a block: header fields, a per-block nonce | ||
| 40 | // (keys the short-ID hash so IDs can't be precomputed/ground across blocks), | ||
| 41 | // the coinbase in full, and a 48-bit short ID for every other transaction | ||
| 42 | // in block order. | ||
| 43 | class CCmpctBlock | ||
| 44 | { | ||
| 45 | public: | ||
| 46 | // header | ||
| 47 | int nVersion; | ||
| 48 | uint256 hashPrevBlock; | ||
| 49 | uint256 hashMerkleRoot; | ||
| 50 | unsigned int nTime; | ||
| 51 | unsigned int nBits; | ||
| 52 | unsigned int nNonce; | ||
| 53 | |||
| 54 | // Keys the short-ID hash for this block specifically -- chosen fresh | ||
| 55 | // per block (not reused from nNonce, which is public PoW data an | ||
| 56 | // attacker already knows and could otherwise grind against). | ||
| 57 | uint64 shortIdNonce; | ||
| 58 | |||
| 59 | // Sent in full. In this implementation: always exactly the coinbase, | ||
| 60 | // at index 0. | ||
| 61 | std::vector<unsigned int> vPrefilledIndex; | ||
| 62 | std::vector<CTransaction> vPrefilledTx; | ||
| 63 | |||
| 64 | // One short ID per NON-prefilled transaction, in ascending block-index | ||
| 65 | // order (i.e. the Nth entry here belongs to the Nth block position that | ||
| 66 | // isn't in vPrefilledIndex). | ||
| 67 | std::vector<uint64> vShortTxId; | ||
| 68 | |||
| 69 | IMPLEMENT_SERIALIZE | ||
| 70 | ( | ||
| 71 | READWRITE(this->nVersion); | ||
| 72 | nVersion = this->nVersion; | ||
| 73 | READWRITE(hashPrevBlock); | ||
| 74 | READWRITE(hashMerkleRoot); | ||
| 75 | READWRITE(nTime); | ||
| 76 | READWRITE(nBits); | ||
| 77 | READWRITE(nNonce); | ||
| 78 | READWRITE(shortIdNonce); | ||
| 79 | READWRITE(vPrefilledIndex); | ||
| 80 | READWRITE(vPrefilledTx); | ||
| 81 | READWRITE(vShortTxId); | ||
| 82 | ) | ||
| 83 | |||
| 84 | uint256 HeaderHash() const; | ||
| 85 | }; | ||
| 86 | |||
| 87 | // Compute the 48-bit short ID for a transaction under a given block's | ||
| 88 | // short-ID nonce. Keyed (via the block's hashPrevBlock + shortIdNonce) so | ||
| 89 | // IDs from one block can't be precomputed before that block exists, and | ||
| 90 | // differ block to block even for the same transaction. | ||
| 91 | uint64 ComputeShortTxId(const uint256& hashPrevBlock, uint64 shortIdNonce, const uint256& txid); | ||
| 92 | |||
| 93 | // Build the compact form of `block` (which must have vtx[0] as the | ||
| 94 | // coinbase, as every block on this chain does). | ||
| 95 | bool BuildCompactBlock(const CBlock& block, CCmpctBlock& out); | ||
| 96 | |||
| 97 | // Attempt to reconstruct a full block from a received compact block using | ||
| 98 | // the local mempool. On full success, returns true and fills `outBlock`. | ||
| 99 | // On partial success (some transactions not found locally), returns false | ||
| 100 | // and fills `vMissingIndex` with the block-position indices that still need | ||
| 101 | // to be requested via "getblocktxn" -- outBlock is left partially built | ||
| 102 | // (coinbase and any resolved positions filled; caller must not use it until | ||
| 103 | // FillMissingAndFinish completes it). | ||
| 104 | bool TryReconstructBlock(const CCmpctBlock& cmpct, CBlock& outBlock, std::vector<unsigned int>& vMissingIndex); | ||
| 105 | |||
| 106 | // Complete a partially-reconstructed block (from a prior TryReconstructBlock | ||
| 107 | // that returned false) using the transactions received in a "blocktxn" | ||
| 108 | // response. vTxn must be in the same order as the indices this node | ||
| 109 | // requested via "getblocktxn" for this block. | ||
| 110 | bool FillMissingAndFinish(CBlock& outBlock, const std::vector<unsigned int>& vMissingIndex, | ||
| 111 | const std::vector<CTransaction>& vTxn); | ||
| 112 | |||
| 113 | #endif | ||