upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/test_compactblock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/test_compactblock.cpp')
-rw-r--r--src/test_compactblock.cpp133
1 files changed, 133 insertions, 0 deletions
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}