aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/test/blockencodings_tests.cpp315
2 files changed, 316 insertions, 0 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 41d811fb54..c8918eb53f 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -45,6 +45,7 @@ BITCOIN_TESTS =\
test/base58_tests.cpp \
test/base64_tests.cpp \
test/bip32_tests.cpp \
+ test/blockencodings_tests.cpp \
test/bloom_tests.cpp \
test/Checkpoints_tests.cpp \
test/coins_tests.cpp \
diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp
new file mode 100644
index 0000000000..3884bf3fe3
--- /dev/null
+++ b/src/test/blockencodings_tests.cpp
@@ -0,0 +1,315 @@
+// Copyright (c) 2011-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "blockencodings.h"
+#include "consensus/merkle.h"
+#include "chainparams.h"
+#include "random.h"
+
+#include "test/test_bitcoin.h"
+
+#include <boost/test/unit_test.hpp>
+
+struct RegtestingSetup : public TestingSetup {
+ RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
+};
+
+BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegtestingSetup)
+
+static CBlock BuildBlockTestCase() {
+ CBlock block;
+ CMutableTransaction tx;
+ tx.vin.resize(1);
+ tx.vin[0].scriptSig.resize(10);
+ tx.vout.resize(1);
+ tx.vout[0].nValue = 42;
+
+ block.vtx.resize(3);
+ block.vtx[0] = tx;
+ block.nVersion = 42;
+ block.hashPrevBlock = GetRandHash();
+ block.nBits = 0x207fffff;
+
+ tx.vin[0].prevout.hash = GetRandHash();
+ tx.vin[0].prevout.n = 0;
+ block.vtx[1] = tx;
+
+ tx.vin.resize(10);
+ for (size_t i = 0; i < tx.vin.size(); i++) {
+ tx.vin[i].prevout.hash = GetRandHash();
+ tx.vin[i].prevout.n = 0;
+ }
+ block.vtx[2] = tx;
+
+ bool mutated;
+ block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
+ assert(!mutated);
+ while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
+ return block;
+}
+
+// Number of shared use_counts we expect for a tx we havent touched
+// == 2 (mempool + our copy from the GetSharedTx call)
+#define SHARED_TX_OFFSET 2
+
+BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
+{
+ CTxMemPool pool(CFeeRate(0));
+ TestMemPoolEntryHelper entry;
+ CBlock block(BuildBlockTestCase());
+
+ pool.addUnchecked(block.vtx[2].GetHash(), entry.FromTx(block.vtx[2]));
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+
+ // Do a simple ShortTxIDs RT
+ {
+ CBlockHeaderAndShortTxIDs shortIDs(block);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << shortIDs;
+
+ CBlockHeaderAndShortTxIDs shortIDs2;
+ stream >> shortIDs2;
+
+ PartiallyDownloadedBlock partialBlock(&pool);
+ BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK);
+ BOOST_CHECK( partialBlock.IsTxAvailable(0));
+ BOOST_CHECK(!partialBlock.IsTxAvailable(1));
+ BOOST_CHECK( partialBlock.IsTxAvailable(2));
+
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+
+ std::list<CTransaction> removed;
+ pool.removeRecursive(block.vtx[2], removed);
+ BOOST_CHECK_EQUAL(removed.size(), 1);
+
+ CBlock block2;
+ std::vector<CTransaction> vtx_missing;
+ BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions
+
+ vtx_missing.push_back(block.vtx[2]); // Wrong transaction
+ partialBlock.FillBlock(block2, vtx_missing); // Current implementation doesn't check txn here, but don't require that
+ bool mutated;
+ BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
+
+ vtx_missing[0] = block.vtx[1];
+ CBlock block3;
+ BOOST_CHECK(partialBlock.FillBlock(block3, vtx_missing) == READ_STATUS_OK);
+ BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
+ BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
+ BOOST_CHECK(!mutated);
+ }
+}
+
+class TestHeaderAndShortIDs {
+ // Utility to encode custom CBlockHeaderAndShortTxIDs
+public:
+ CBlockHeader header;
+ uint64_t nonce;
+ std::vector<uint64_t> shorttxids;
+ std::vector<PrefilledTransaction> prefilledtxn;
+
+ TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs& orig) {
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << orig;
+ stream >> *this;
+ }
+ TestHeaderAndShortIDs(const CBlock& block) :
+ TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block)) {}
+
+ uint64_t GetShortID(const uint256& txhash) const {
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << *this;
+ CBlockHeaderAndShortTxIDs base;
+ stream >> base;
+ return base.GetShortID(txhash);
+ }
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
+ READWRITE(header);
+ READWRITE(nonce);
+ size_t shorttxids_size = shorttxids.size();
+ READWRITE(VARINT(shorttxids_size));
+ shorttxids.resize(shorttxids_size);
+ for (size_t i = 0; i < shorttxids.size(); i++) {
+ uint32_t lsb = shorttxids[i] & 0xffffffff;
+ uint16_t msb = (shorttxids[i] >> 32) & 0xffff;
+ READWRITE(lsb);
+ READWRITE(msb);
+ shorttxids[i] = (uint64_t(msb) << 32) | uint64_t(lsb);
+ }
+ READWRITE(prefilledtxn);
+ }
+};
+
+BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
+{
+ CTxMemPool pool(CFeeRate(0));
+ TestMemPoolEntryHelper entry;
+ CBlock block(BuildBlockTestCase());
+
+ pool.addUnchecked(block.vtx[2].GetHash(), entry.FromTx(block.vtx[2]));
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+
+ // Test with pre-forwarding tx 1, but not coinbase
+ {
+ TestHeaderAndShortIDs shortIDs(block);
+ shortIDs.prefilledtxn.resize(1);
+ shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
+ shortIDs.shorttxids.resize(2);
+ shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0].GetHash());
+ shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2].GetHash());
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << shortIDs;
+
+ CBlockHeaderAndShortTxIDs shortIDs2;
+ stream >> shortIDs2;
+
+ PartiallyDownloadedBlock partialBlock(&pool);
+ BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK);
+ BOOST_CHECK(!partialBlock.IsTxAvailable(0));
+ BOOST_CHECK( partialBlock.IsTxAvailable(1));
+ BOOST_CHECK( partialBlock.IsTxAvailable(2));
+
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+
+ CBlock block2;
+ std::vector<CTransaction> vtx_missing;
+ BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions
+
+ vtx_missing.push_back(block.vtx[1]); // Wrong transaction
+ partialBlock.FillBlock(block2, vtx_missing); // Current implementation doesn't check txn here, but don't require that
+ bool mutated;
+ BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
+
+ vtx_missing[0] = block.vtx[0];
+ CBlock block3;
+ BOOST_CHECK(partialBlock.FillBlock(block3, vtx_missing) == READ_STATUS_OK);
+ BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
+ BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
+ BOOST_CHECK(!mutated);
+
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+ }
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+}
+
+BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
+{
+ CTxMemPool pool(CFeeRate(0));
+ TestMemPoolEntryHelper entry;
+ CBlock block(BuildBlockTestCase());
+
+ pool.addUnchecked(block.vtx[1].GetHash(), entry.FromTx(block.vtx[1]));
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+
+ // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
+ {
+ TestHeaderAndShortIDs shortIDs(block);
+ shortIDs.prefilledtxn.resize(2);
+ shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
+ shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
+ shortIDs.shorttxids.resize(1);
+ shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1].GetHash());
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << shortIDs;
+
+ CBlockHeaderAndShortTxIDs shortIDs2;
+ stream >> shortIDs2;
+
+ PartiallyDownloadedBlock partialBlock(&pool);
+ BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK);
+ BOOST_CHECK( partialBlock.IsTxAvailable(0));
+ BOOST_CHECK( partialBlock.IsTxAvailable(1));
+ BOOST_CHECK( partialBlock.IsTxAvailable(2));
+
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+
+ CBlock block2;
+ std::vector<CTransaction> vtx_missing;
+ BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
+ BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
+ bool mutated;
+ BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
+ BOOST_CHECK(!mutated);
+
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
+ }
+ BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
+}
+
+BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
+{
+ CTxMemPool pool(CFeeRate(0));
+ CMutableTransaction coinbase;
+ coinbase.vin.resize(1);
+ coinbase.vin[0].scriptSig.resize(10);
+ coinbase.vout.resize(1);
+ coinbase.vout[0].nValue = 42;
+
+ CBlock block;
+ block.vtx.resize(1);
+ block.vtx[0] = coinbase;
+ block.nVersion = 42;
+ block.hashPrevBlock = GetRandHash();
+ block.nBits = 0x207fffff;
+
+ bool mutated;
+ block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
+ assert(!mutated);
+ while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
+
+ // Test simple header round-trip with only coinbase
+ {
+ CBlockHeaderAndShortTxIDs shortIDs(block);
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << shortIDs;
+
+ CBlockHeaderAndShortTxIDs shortIDs2;
+ stream >> shortIDs2;
+
+ PartiallyDownloadedBlock partialBlock(&pool);
+ BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK);
+ BOOST_CHECK(partialBlock.IsTxAvailable(0));
+
+ CBlock block2;
+ std::vector<CTransaction> vtx_missing;
+ BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
+ BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
+ bool mutated;
+ BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
+ BOOST_CHECK(!mutated);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
+ BlockTransactionsRequest req1;
+ req1.blockhash = GetRandHash();
+ req1.indexes.resize(4);
+ req1.indexes[0] = 0;
+ req1.indexes[1] = 1;
+ req1.indexes[2] = 3;
+ req1.indexes[3] = 4;
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << req1;
+
+ BlockTransactionsRequest req2;
+ stream >> req2;
+
+ BOOST_CHECK_EQUAL(req1.blockhash.ToString(), req2.blockhash.ToString());
+ BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
+ BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
+ BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
+ BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
+ BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
+}
+
+BOOST_AUTO_TEST_SUITE_END()