forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f4f8f14
commit e3b2222
Showing
2 changed files
with
316 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |