Merge #11293: Deduplicate CMerkleBlock construction code, add test coverage

46ce223d1 Add tests for CMerkleBlock usage with txids specified (James O'Beirne)
5ab586f90 Consolidate CMerkleBlock constructor into a single method (James O'Beirne)

Pull request description:

  What started as a simple task to add test coverage ended up giving way to a light refactoring. This consolidates the mostly-identical `CMerkleBlock` constructors into one (using C++11 constructor delegation) and adds coverage for the by-txids construction case.

  ### Before

  ![selection_006](https://user-images.githubusercontent.com/73197/30242104-0f381fe4-9545-11e7-9617-83b87fce0456.png)

  ### After

  ![selection_008](https://user-images.githubusercontent.com/73197/30242107-1425dfaa-9545-11e7-9e6b-2c3432517dd1.png)

Tree-SHA512: eed84ed3e8bfc43473077b575c8252759a857e37275e4b36ca7cc2c17a65895e5f494bfd9d4aeab09fc6e98fc6a9c641ac7ecc0ddbeefe01a9e4308e7909e529
This commit is contained in:
MarcoFalke 2017-10-03 14:26:05 +02:00
commit dbc4ae0396
No known key found for this signature in database
GPG key ID: D2EA4850E7528B25
7 changed files with 120 additions and 42 deletions

View file

@ -50,6 +50,7 @@ BITCOIN_TESTS =\
test/main_tests.cpp \ test/main_tests.cpp \
test/mempool_tests.cpp \ test/mempool_tests.cpp \
test/merkle_tests.cpp \ test/merkle_tests.cpp \
test/merkleblock_tests.cpp \
test/miner_tests.cpp \ test/miner_tests.cpp \
test/multisig_tests.cpp \ test/multisig_tests.cpp \
test/net_tests.cpp \ test/net_tests.cpp \

View file

@ -9,7 +9,8 @@
#include "consensus/consensus.h" #include "consensus/consensus.h"
#include "utilstrencodings.h" #include "utilstrencodings.h"
CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter)
CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids)
{ {
header = block.GetBlockHeader(); header = block.GetBlockHeader();
@ -22,36 +23,14 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter)
for (unsigned int i = 0; i < block.vtx.size(); i++) for (unsigned int i = 0; i < block.vtx.size(); i++)
{ {
const uint256& hash = block.vtx[i]->GetHash(); const uint256& hash = block.vtx[i]->GetHash();
if (filter.IsRelevantAndUpdate(*block.vtx[i])) if (txids && txids->count(hash)) {
{
vMatch.push_back(true); vMatch.push_back(true);
vMatchedTxn.push_back(std::make_pair(i, hash)); } else if (filter && filter->IsRelevantAndUpdate(*block.vtx[i])) {
}
else
vMatch.push_back(false);
vHashes.push_back(hash);
}
txn = CPartialMerkleTree(vHashes, vMatch);
}
CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids)
{
header = block.GetBlockHeader();
std::vector<bool> vMatch;
std::vector<uint256> vHashes;
vMatch.reserve(block.vtx.size());
vHashes.reserve(block.vtx.size());
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
const uint256& hash = block.vtx[i]->GetHash();
if (txids.count(hash))
vMatch.push_back(true); vMatch.push_back(true);
else vMatchedTxn.emplace_back(i, hash);
} else {
vMatch.push_back(false); vMatch.push_back(false);
}
vHashes.push_back(hash); vHashes.push_back(hash);
} }

View file

@ -131,8 +131,12 @@ public:
CBlockHeader header; CBlockHeader header;
CPartialMerkleTree txn; CPartialMerkleTree txn;
public: /**
/** Public only for unit testing and relay testing (not relayed) */ * Public only for unit testing and relay testing (not relayed).
*
* Used only when a bloom filter is specified to allow
* testing the transactions which matched the bloom filter.
*/
std::vector<std::pair<unsigned int, uint256> > vMatchedTxn; std::vector<std::pair<unsigned int, uint256> > vMatchedTxn;
/** /**
@ -140,10 +144,10 @@ public:
* Note that this will call IsRelevantAndUpdate on the filter for each transaction, * Note that this will call IsRelevantAndUpdate on the filter for each transaction,
* thus the filter will likely be modified. * thus the filter will likely be modified.
*/ */
CMerkleBlock(const CBlock& block, CBloomFilter& filter); CMerkleBlock(const CBlock& block, CBloomFilter& filter) : CMerkleBlock(block, &filter, nullptr) { }
// Create from a CBlock, matching the txids in the set // Create from a CBlock, matching the txids in the set
CMerkleBlock(const CBlock& block, const std::set<uint256>& txids); CMerkleBlock(const CBlock& block, const std::set<uint256>& txids) : CMerkleBlock(block, nullptr, &txids) { }
CMerkleBlock() {} CMerkleBlock() {}
@ -154,6 +158,10 @@ public:
READWRITE(header); READWRITE(header);
READWRITE(txn); READWRITE(txn);
} }
private:
// Combined constructor to consolidate code
CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids);
}; };
#endif // BITCOIN_MERKLEBLOCK_H #endif // BITCOIN_MERKLEBLOCK_H

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,78 @@
// Copyright (c) 2012-2017 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 "merkleblock.h"
#include "uint256.h"
#include "test/test_bitcoin.h"
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(merkleblock_tests, BasicTestingSetup)
/**
* Create a CMerkleBlock using a list of txids which will be found in the
* given block.
*/
BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_found)
{
CBlock block = getBlock13b8a();
std::set<uint256> txids;
// Last txn in block.
uint256 txhash1 = uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20");
// Second txn in block.
uint256 txhash2 = uint256S("0xf9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07");
txids.insert(txhash1);
txids.insert(txhash2);
CMerkleBlock merkleBlock(block, txids);
BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex());
// vMatchedTxn is only used when bloom filter is specified.
BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0);
std::vector<uint256> vMatched;
std::vector<unsigned int> vIndex;
BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex());
BOOST_CHECK_EQUAL(vMatched.size(), 2);
// Ordered by occurrence in depth-first tree traversal.
BOOST_CHECK_EQUAL(vMatched[0].ToString(), txhash2.ToString());
BOOST_CHECK_EQUAL(vIndex[0], 1);
BOOST_CHECK_EQUAL(vMatched[1].ToString(), txhash1.ToString());
BOOST_CHECK_EQUAL(vIndex[1], 8);
}
/**
* Create a CMerkleBlock using a list of txids which will not be found in the
* given block.
*/
BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_not_found)
{
CBlock block = getBlock13b8a();
std::set<uint256> txids2;
txids2.insert(uint256S("0xc0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
CMerkleBlock merkleBlock(block, txids2);
BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex());
BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0);
std::vector<uint256> vMatched;
std::vector<unsigned int> vIndex;
BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex());
BOOST_CHECK_EQUAL(vMatched.size(), 0);
BOOST_CHECK_EQUAL(vIndex.size(), 0);
}
BOOST_AUTO_TEST_SUITE_END()

File diff suppressed because one or more lines are too long

View file

@ -110,4 +110,7 @@ struct TestMemPoolEntryHelper
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; } TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; }
}; };
CBlock getBlock13b8a();
#endif #endif