upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-09-05 03:16:00 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-05 03:16:00 +0000
commit60538c5a935787521a5ad8c2a7dce52b5f250f3a (patch)
treeb78632a9d3b15b7a99567fe3c213d0bb00fd1731 /src
parent152c859b18b222a7c11a82b5485110e902267d40 (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')
-rw-r--r--src/Makefile24
-rw-r--r--src/compactblock.cpp155
-rw-r--r--src/compactblock.h113
-rw-r--r--src/test_compactblock.cpp133
4 files changed, 421 insertions, 4 deletions
diff --git a/src/Makefile b/src/Makefile
index b931007..888110f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -55,7 +55,8 @@ LIBS = -L$(SECP_LIB) -lsecp256k1 \
55HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \ 55HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \
56 key.h bignum.h script.h db.h base58.h net.h nostr.h \ 56 key.h bignum.h script.h db.h base58.h net.h nostr.h \
57 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h \ 57 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h \
58 selftest.h walletcmd.h wallet_sqlite.h compat.h sockcount.h proxy.h tor.h font_roboto.h 58 selftest.h walletcmd.h wallet_sqlite.h compat.h sockcount.h proxy.h tor.h font_roboto.h \
59 compactblock.h
59 60
60IMGUI_OBJS = obj/imgui.o obj/imgui_draw.o obj/imgui_tables.o \ 61IMGUI_OBJS = obj/imgui.o obj/imgui_draw.o obj/imgui_tables.o \
61 obj/imgui_widgets.o obj/imgui_impl_glfw.o obj/imgui_impl_opengl3.o 62 obj/imgui_widgets.o obj/imgui_impl_glfw.o obj/imgui_impl_opengl3.o
@@ -64,7 +65,7 @@ NODE_OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o \
64 obj/sha.o obj/nostr.o obj/randomx_pow.o \ 65 obj/sha.o obj/nostr.o obj/randomx_pow.o \
65 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \ 66 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \
66 obj/selftest.o obj/bip32.o obj/walletcmd.o obj/wallet_sqlite.o \ 67 obj/selftest.o obj/bip32.o obj/walletcmd.o obj/wallet_sqlite.o \
67 obj/proxy.o obj/tor.o 68 obj/proxy.o obj/tor.o obj/compactblock.o
68 69
69GUI_OBJS = obj/gui.o obj/main_gui.o 70GUI_OBJS = obj/gui.o obj/main_gui.o
70 71
@@ -194,13 +195,28 @@ fuzz-script-smoke: fuzz-script
194 ./fuzz-script -runs=128 ../work/fuzz-corpus/script 195 ./fuzz-script -runs=128 ../work/fuzz-corpus/script
195 196
196clean: 197clean:
197 rm -rf obj obj-node obj-fuzz bitflash bitflash-node fuzz-net-message fuzz-script test_bip32 198 rm -rf obj obj-node obj-fuzz bitflash bitflash-node fuzz-net-message fuzz-script test_bip32 test_compactblock
198 199
199test_bip32: test_bip32.cpp bip32.cpp bip32.h bip39_english.h 200test_bip32: test_bip32.cpp bip32.cpp bip32.h bip39_english.h
200 $(CXX) $(CXXFLAGS) -o $@ test_bip32.cpp bip32.cpp -lssl -lcrypto 201 $(CXX) $(CXXFLAGS) -o $@ test_bip32.cpp bip32.cpp -lssl -lcrypto
201 202
202tests: test_bip32 bitflash-node 203# Upstream shipped test_compactblock.cpp with no build rule at all, so it was
204# never compiled there. It links the standalone way test_bip32 does: it supplies
205# its own mapTransactions and GetAppDir so main.cpp stays out of the link, and
206# only needs sha.cpp/util.cpp for Hash() and the logging helpers.
207#
208# TEST_LIBS is overridable for the same reason NODE_LIBS is: the pinned dep tree
209# ships libsodium as a static archive only, so a build against it passes
210# TEST_LIBS="... -l:libsodium.a ...".
211TEST_LIBS ?= -lsodium -lssl -lcrypto -lpthread
212
213test_compactblock: test_compactblock.cpp compactblock.cpp compactblock.h $(HEADERS)
214 $(CXX) $(CXXFLAGS) -o $@ test_compactblock.cpp compactblock.cpp sha.cpp util.cpp \
215 $(TEST_LIBS)
216
217tests: test_bip32 test_compactblock bitflash-node
203 ./test_bip32 218 ./test_bip32
219 ./test_compactblock
204 ./bitflash-node -selftest=wallet-keypool 220 ./bitflash-node -selftest=wallet-keypool
205 ./bitflash-node -selftest=wallet-hd 221 ./bitflash-node -selftest=wallet-hd
206 ./bitflash-node -selftest=wallet-format 222 ./bitflash-node -selftest=wallet-format
diff --git a/src/compactblock.cpp b/src/compactblock.cpp
new file mode 100644
index 0000000..3999ab7
--- /dev/null
+++ b/src/compactblock.cpp
@@ -0,0 +1,155 @@
1// Copyright (c) 2026 Bitflash developers
2// Distributed under the MIT/X11 software license.
3//
4// See compactblock.h.
5
6#include "headers.h"
7#include "compactblock.h"
8#include <sodium.h>
9#include <map>
10
11uint256 CCmpctBlock::HeaderHash() const
12{
13 // Same technique CBlock::GetHash() uses: the header fields are laid out
14 // contiguously and in the same order here as in CBlock, so hashing the
15 // raw member range gives the identical block-identity hash.
16 return Hash(BEGIN(nVersion), END(nNonce));
17}
18
19uint64 ComputeShortTxId(const uint256& hashPrevBlock, uint64 shortIdNonce, const uint256& txid)
20{
21 // Derive a 16-byte SipHash key from (hashPrevBlock, shortIdNonce), so
22 // short IDs are specific to this block: they can't be precomputed
23 // before the block (and its freshly-chosen nonce) exist, and the same
24 // transaction gets a different short ID in every block it appears in
25 // (relevant across reorgs/re-announcements).
26 unsigned char keyMaterial[32 + sizeof(uint64)];
27 memcpy(keyMaterial, BEGIN(hashPrevBlock), 32);
28 memcpy(keyMaterial + 32, &shortIdNonce, sizeof(uint64));
29 uint256 keyHash = Hash((const char*)keyMaterial, (const char*)keyMaterial + sizeof(keyMaterial));
30
31 unsigned char out[crypto_shorthash_BYTES]; // 8 bytes
32 crypto_shorthash(out, (const unsigned char*)BEGIN(txid), 32, (const unsigned char*)BEGIN(keyHash));
33
34 uint64 id = 0;
35 memcpy(&id, out, sizeof(uint64));
36 return id & 0xFFFFFFFFFFFFULL; // low 48 bits -- matches the wire field width
37}
38
39bool BuildCompactBlock(const CBlock& block, CCmpctBlock& out)
40{
41 if (block.vtx.empty())
42 return false; // every real block has at least the coinbase
43
44 out.nVersion = block.nVersion;
45 out.hashPrevBlock = block.hashPrevBlock;
46 out.hashMerkleRoot = block.hashMerkleRoot;
47 out.nTime = block.nTime;
48 out.nBits = block.nBits;
49 out.nNonce = block.nNonce;
50
51 unsigned char nonceBytes[sizeof(uint64)];
52 RAND_bytes(nonceBytes, sizeof(nonceBytes));
53 memcpy(&out.shortIdNonce, nonceBytes, sizeof(uint64));
54
55 out.vPrefilledIndex.clear();
56 out.vPrefilledTx.clear();
57 out.vShortTxId.clear();
58
59 // Only the coinbase is prefilled (index 0) -- see file comment in
60 // compactblock.h for why that's enough for this chain's scale.
61 out.vPrefilledIndex.push_back(0);
62 out.vPrefilledTx.push_back(block.vtx[0]);
63
64 for (size_t i = 1; i < block.vtx.size(); i++)
65 out.vShortTxId.push_back(ComputeShortTxId(out.hashPrevBlock, out.shortIdNonce, block.vtx[i].GetHash()));
66
67 return true;
68}
69
70bool TryReconstructBlock(const CCmpctBlock& cmpct, CBlock& outBlock, std::vector<unsigned int>& vMissingIndex)
71{
72 vMissingIndex.clear();
73
74 size_t nTotal = cmpct.vPrefilledIndex.size() + cmpct.vShortTxId.size();
75 if (cmpct.vPrefilledIndex.size() != cmpct.vPrefilledTx.size() || nTotal == 0)
76 return false; // malformed
77
78 outBlock.SetNull();
79 outBlock.nVersion = cmpct.nVersion;
80 outBlock.hashPrevBlock = cmpct.hashPrevBlock;
81 outBlock.hashMerkleRoot = cmpct.hashMerkleRoot;
82 outBlock.nTime = cmpct.nTime;
83 outBlock.nBits = cmpct.nBits;
84 outBlock.nNonce = cmpct.nNonce;
85 outBlock.vtx.assign(nTotal, CTransaction());
86
87 std::vector<bool> vFilled(nTotal, false);
88 for (size_t i = 0; i < cmpct.vPrefilledIndex.size(); i++)
89 {
90 unsigned int idx = cmpct.vPrefilledIndex[i];
91 if (idx >= nTotal)
92 return false; // malformed
93 outBlock.vtx[idx] = cmpct.vPrefilledTx[i];
94 vFilled[idx] = true;
95 }
96
97 // Index every mempool tx by its short ID under this block's keying, so
98 // resolving each block position is a map lookup rather than a linear
99 // scan of the mempool per short ID.
100 std::map<uint64, const CTransaction*> mapShortIdToTx;
101 for (std::map<uint256, CTransaction>::const_iterator mi = mapTransactions.begin();
102 mi != mapTransactions.end(); ++mi)
103 {
104 uint64 sid = ComputeShortTxId(cmpct.hashPrevBlock, cmpct.shortIdNonce, mi->first);
105 // A short-ID collision between two distinct mempool transactions is
106 // possible (48 bits, birthday bound ~2^24 mempool entries) though
107 // very unlikely at this chain's scale; on collision, prefer keeping
108 // neither mapped rather than risk resolving to the wrong tx --
109 // that position just falls through to getblocktxn like a genuine
110 // miss, which is always correct, only occasionally not optimal.
111 if (mapShortIdToTx.count(sid))
112 mapShortIdToTx[sid] = NULL;
113 else
114 mapShortIdToTx[sid] = &mi->second;
115 }
116
117 size_t shortIdPos = 0;
118 for (size_t idx = 0; idx < nTotal; idx++)
119 {
120 if (vFilled[idx])
121 continue;
122 if (shortIdPos >= cmpct.vShortTxId.size())
123 return false; // malformed: more unfilled positions than short IDs
124 uint64 sid = cmpct.vShortTxId[shortIdPos++];
125
126 std::map<uint64, const CTransaction*>::const_iterator found = mapShortIdToTx.find(sid);
127 if (found != mapShortIdToTx.end() && found->second != NULL)
128 {
129 outBlock.vtx[idx] = *found->second;
130 vFilled[idx] = true;
131 }
132 else
133 {
134 vMissingIndex.push_back(idx);
135 }
136 }
137
138 return vMissingIndex.empty();
139}
140
141bool FillMissingAndFinish(CBlock& outBlock, const std::vector<unsigned int>& vMissingIndex,
142 const std::vector<CTransaction>& vTxn)
143{
144 if (vMissingIndex.size() != vTxn.size())
145 return false; // peer didn't answer what was asked
146
147 for (size_t i = 0; i < vMissingIndex.size(); i++)
148 {
149 unsigned int idx = vMissingIndex[i];
150 if (idx >= outBlock.vtx.size())
151 return false; // malformed
152 outBlock.vtx[idx] = vTxn[i];
153 }
154 return true;
155}
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.
43class CCmpctBlock
44{
45public:
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.
91uint64 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).
95bool 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).
104bool 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.
110bool FillMissingAndFinish(CBlock& outBlock, const std::vector<unsigned int>& vMissingIndex,
111 const std::vector<CTransaction>& vTxn);
112
113#endif
diff --git a/src/test_compactblock.cpp b/src/test_compactblock.cpp
new file mode 100644
index 0000000..f4b5583
--- /dev/null
+++ b/src/test_compactblock.cpp
@@ -0,0 +1,133 @@
1// Standalone test of compactblock.cpp's core logic: build a compact block,
2// reconstruct it from a simulated mempool, including the missing-tx path.
3// Not wired into the P2P layer -- proves BuildCompactBlock/
4// TryReconstructBlock/FillMissingAndFinish are correct in isolation.
5
6#include "headers.h"
7#include "compactblock.h"
8#include <cstdio>
9#include <cassert>
10
11// main.cpp defines this; we're not linking main.cpp, so define it here.
12map<uint256, CTransaction> mapTransactions;
13string GetAppDir() { return "."; } // util.h's debug-log path helper needs this
14
15static int g_fail = 0;
16#define CHECK(cond, msg) do { \
17 if (cond) printf(" ok %s\n", msg); \
18 else { printf(" FAIL %s\n", msg); g_fail++; } \
19} while (0)
20
21// Build a syntactically-valid-enough CTransaction that GetHash()s uniquely
22// per `nSeed` -- doesn't need to be consensus-valid, this test only
23// exercises short-ID computation and reconstruction, not script validation.
24static CTransaction MakeTx(int nSeed)
25{
26 CTransaction tx;
27 tx.nVersion = 1;
28 tx.nLockTime = nSeed; // cheapest way to make each tx's serialization (and hash) unique
29 return tx;
30}
31
32int main()
33{
34 // Build a block: coinbase + 20 regular transactions.
35 CBlock block;
36 block.nVersion = 1;
37 block.hashPrevBlock = uint256("0x00000000000000000000000000000000000000000000000000000000000abc");
38 block.nTime = 1234567890;
39 block.nBits = 0x1f0fffff;
40 block.nNonce = 42;
41 block.vtx.push_back(MakeTx(0)); // coinbase
42 for (int i = 1; i <= 20; i++)
43 block.vtx.push_back(MakeTx(i));
44 block.hashMerkleRoot = block.BuildMerkleTree();
45
46 printf("build_and_reconstruct_full_mempool\n");
47 {
48 CCmpctBlock cmpct;
49 CHECK(BuildCompactBlock(block, cmpct), "BuildCompactBlock succeeds");
50 CHECK(cmpct.vPrefilledIndex.size() == 1 && cmpct.vPrefilledIndex[0] == 0, "only the coinbase is prefilled");
51 CHECK(cmpct.vShortTxId.size() == 20, "one short ID per remaining tx");
52
53 // Simulate a peer with every transaction already in its mempool.
54 mapTransactions.clear();
55 for (size_t i = 1; i < block.vtx.size(); i++)
56 mapTransactions[block.vtx[i].GetHash()] = block.vtx[i];
57
58 CBlock rebuilt;
59 vector<unsigned int> vMissing;
60 CHECK(TryReconstructBlock(cmpct, rebuilt, vMissing), "fully reconstructs when mempool has everything");
61 CHECK(vMissing.empty(), "nothing reported missing");
62 CHECK(rebuilt.GetHash() == block.GetHash(), "reconstructed block hash matches original exactly");
63 CHECK(rebuilt.vtx.size() == block.vtx.size(), "same transaction count");
64 bool fAllMatch = true;
65 for (size_t i = 0; i < block.vtx.size(); i++)
66 if (rebuilt.vtx[i].GetHash() != block.vtx[i].GetHash()) fAllMatch = false;
67 CHECK(fAllMatch, "every transaction in the right position");
68 }
69
70 printf("partial_mempool_then_getblocktxn_completes_it\n");
71 {
72 CCmpctBlock cmpct;
73 BuildCompactBlock(block, cmpct);
74
75 // Simulate a peer missing 3 of the 20 non-coinbase transactions.
76 mapTransactions.clear();
77 vector<unsigned int> expectMissing;
78 for (size_t i = 1; i < block.vtx.size(); i++)
79 {
80 if (i == 5 || i == 12 || i == 19) { expectMissing.push_back((unsigned int)i); continue; }
81 mapTransactions[block.vtx[i].GetHash()] = block.vtx[i];
82 }
83
84 CBlock rebuilt;
85 vector<unsigned int> vMissing;
86 CHECK(!TryReconstructBlock(cmpct, rebuilt, vMissing), "reports incomplete when mempool is missing some tx");
87 CHECK(vMissing == expectMissing, "missing indices are exactly the ones not in mempool");
88
89 // Simulate the getblocktxn round trip: peer sends back the missing txs.
90 vector<CTransaction> vTxn;
91 foreach(unsigned int idx, vMissing)
92 vTxn.push_back(block.vtx[idx]);
93
94 CHECK(FillMissingAndFinish(rebuilt, vMissing, vTxn), "FillMissingAndFinish succeeds with matching response");
95 CHECK(rebuilt.GetHash() == block.GetHash(), "block hash matches after filling in the missing tx");
96 }
97
98 printf("mismatched_blocktxn_response_rejected\n");
99 {
100 CCmpctBlock cmpct;
101 BuildCompactBlock(block, cmpct);
102 mapTransactions.clear();
103 mapTransactions[block.vtx[1].GetHash()] = block.vtx[1]; // only one in mempool
104
105 CBlock rebuilt;
106 vector<unsigned int> vMissing;
107 TryReconstructBlock(cmpct, rebuilt, vMissing);
108 CHECK(vMissing.size() == 19, "19 missing when mempool has only 1 of 20");
109
110 vector<CTransaction> vWrongCount;
111 vWrongCount.push_back(block.vtx[2]); // way fewer than vMissing.size()
112 CHECK(!FillMissingAndFinish(rebuilt, vMissing, vWrongCount), "rejects a response with the wrong tx count");
113 }
114
115 printf("different_blocks_give_different_short_ids_for_same_tx\n");
116 {
117 // Same transaction, two different (simulated) blocks -- short IDs
118 // must differ, since they're keyed per-block (hashPrevBlock +
119 // fresh nonce each time), not just derived from the txid alone.
120 CTransaction tx = MakeTx(99);
121 uint256 prevA = uint256("0x1111111111111111111111111111111111111111111111111111111111111111");
122 uint256 prevB = uint256("0x2222222222222222222222222222222222222222222222222222222222222222");
123 uint64 id1 = ComputeShortTxId(prevA, 0xAAAABBBBCCCCDDDDULL, tx.GetHash());
124 uint64 id2 = ComputeShortTxId(prevB, 0xAAAABBBBCCCCDDDDULL, tx.GetHash());
125 uint64 id3 = ComputeShortTxId(prevA, 0x1111222233334444ULL, tx.GetHash());
126 CHECK(id1 != id2, "different hashPrevBlock -> different short ID");
127 CHECK(id1 != id3, "different nonce -> different short ID");
128 CHECK(id1 <= 0xFFFFFFFFFFFFULL, "short ID fits in 48 bits");
129 }
130
131 printf("\n%s (%d failures)\n", g_fail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", g_fail);
132 return g_fail == 0 ? 0 : 1;
133}