diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-06 01:53:03 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-06 01:53:03 +0000 |
| commit | ef9f06d43fc6577c15df2256abbb25b20d4227b8 (patch) | |
| tree | 7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/test_compactblock.cpp | |
| parent | be63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff) | |
| parent | ceae3d076b5ad11ee84729ae43f14e9312896646 (diff) | |
Bring main onto the line this project actually ships (incl. the 1.2.23 compactblock port)
Closes #6, #7, #8, #11, #35, #34, #29, #28, #27, and #16
See merge request !16
Diffstat (limited to 'src/test_compactblock.cpp')
| -rw-r--r-- | src/test_compactblock.cpp | 133 |
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. | ||
| 12 | map<uint256, CTransaction> mapTransactions; | ||
| 13 | string GetAppDir() { return "."; } // util.h's debug-log path helper needs this | ||
| 14 | |||
| 15 | static 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. | ||
| 24 | static 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 | |||
| 32 | int 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 | } | ||