2018-01-02 18:12:05 +01:00
// Copyright (c) 2011-2017 The Bitcoin Core developers
2016-04-26 00:51:08 +02:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-11-10 01:57:53 +01:00
# include <blockencodings.h>
# include <consensus/merkle.h>
# include <chainparams.h>
# include <random.h>
2016-04-26 00:51:08 +02:00
2017-11-10 01:57:53 +01:00
# include <test/test_bitcoin.h>
2016-04-26 00:51:08 +02:00
# include <boost/test/unit_test.hpp>
2016-12-05 05:44:37 +01:00
std : : vector < std : : pair < uint256 , CTransactionRef > > extra_txn ;
2016-04-26 00:51:08 +02:00
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 ) ;
2016-11-11 02:34:17 +01:00
block . vtx [ 0 ] = MakeTransactionRef ( tx ) ;
2016-04-26 00:51:08 +02:00
block . nVersion = 42 ;
2017-06-07 21:03:17 +02:00
block . hashPrevBlock = InsecureRand256 ( ) ;
2016-04-26 00:51:08 +02:00
block . nBits = 0x207fffff ;
2017-06-07 21:03:17 +02:00
tx . vin [ 0 ] . prevout . hash = InsecureRand256 ( ) ;
2016-04-26 00:51:08 +02:00
tx . vin [ 0 ] . prevout . n = 0 ;
2016-11-11 02:34:17 +01:00
block . vtx [ 1 ] = MakeTransactionRef ( tx ) ;
2016-04-26 00:51:08 +02:00
tx . vin . resize ( 10 ) ;
for ( size_t i = 0 ; i < tx . vin . size ( ) ; i + + ) {
2017-06-07 21:03:17 +02:00
tx . vin [ i ] . prevout . hash = InsecureRand256 ( ) ;
2016-04-26 00:51:08 +02:00
tx . vin [ i ] . prevout . n = 0 ;
}
2016-11-11 02:34:17 +01:00
block . vtx [ 2 ] = MakeTransactionRef ( tx ) ;
2016-04-26 00:51:08 +02:00
bool mutated ;
block . hashMerkleRoot = BlockMerkleRoot ( block , & mutated ) ;
assert ( ! mutated ) ;
while ( ! CheckProofOfWork ( block . GetHash ( ) , block . nBits , Params ( ) . GetConsensus ( ) ) ) + + block . nNonce ;
return block ;
}
2017-08-16 00:24:39 +02:00
// Number of shared use_counts we expect for a tx we haven't touched
2018-04-11 19:51:28 +02:00
// (block + mempool + our copy from the GetSharedTx call)
constexpr long SHARED_TX_OFFSET { 3 } ;
2016-04-26 00:51:08 +02:00
BOOST_AUTO_TEST_CASE ( SimpleRoundTripTest )
{
2017-01-13 22:53:21 +01:00
CTxMemPool pool ;
2016-04-26 00:51:08 +02:00
TestMemPoolEntryHelper entry ;
CBlock block ( BuildBlockTestCase ( ) ) ;
2018-04-11 19:51:28 +02:00
pool . addUnchecked ( block . vtx [ 2 ] - > GetHash ( ) , entry . FromTx ( block . vtx [ 2 ] ) ) ;
2017-11-06 23:08:55 +01:00
LOCK ( pool . cs ) ;
2016-11-11 02:26:00 +01:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( block . vtx [ 2 ] - > GetHash ( ) ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 0 ) ;
2016-04-26 00:51:08 +02:00
// Do a simple ShortTxIDs RT
{
2016-06-25 19:17:45 +02:00
CBlockHeaderAndShortTxIDs shortIDs ( block , true ) ;
2016-04-26 00:51:08 +02:00
CDataStream stream ( SER_NETWORK , PROTOCOL_VERSION ) ;
stream < < shortIDs ;
CBlockHeaderAndShortTxIDs shortIDs2 ;
stream > > shortIDs2 ;
PartiallyDownloadedBlock partialBlock ( & pool ) ;
2016-12-05 05:44:37 +01:00
BOOST_CHECK ( partialBlock . InitData ( shortIDs2 , extra_txn ) = = READ_STATUS_OK ) ;
2016-04-26 00:51:08 +02:00
BOOST_CHECK ( partialBlock . IsTxAvailable ( 0 ) ) ;
BOOST_CHECK ( ! partialBlock . IsTxAvailable ( 1 ) ) ;
BOOST_CHECK ( partialBlock . IsTxAvailable ( 2 ) ) ;
2016-11-11 02:26:00 +01:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( block . vtx [ 2 ] - > GetHash ( ) ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 1 ) ;
2016-04-26 00:51:08 +02:00
2016-11-29 23:51:26 +01:00
size_t poolSize = pool . size ( ) ;
pool . removeRecursive ( * block . vtx [ 2 ] ) ;
BOOST_CHECK_EQUAL ( pool . size ( ) , poolSize - 1 ) ;
2016-04-26 00:51:08 +02:00
CBlock block2 ;
2016-11-11 22:01:27 +01:00
{
PartiallyDownloadedBlock tmp = partialBlock ;
BOOST_CHECK ( partialBlock . FillBlock ( block2 , { } ) = = READ_STATUS_INVALID ) ; // No transactions
partialBlock = tmp ;
}
2016-04-26 00:51:08 +02:00
2016-11-11 22:01:27 +01:00
// Wrong transaction
{
PartiallyDownloadedBlock tmp = partialBlock ;
partialBlock . FillBlock ( block2 , { block . vtx [ 2 ] } ) ; // Current implementation doesn't check txn here, but don't require that
partialBlock = tmp ;
}
2016-04-26 00:51:08 +02:00
bool mutated ;
BOOST_CHECK ( block . hashMerkleRoot ! = BlockMerkleRoot ( block2 , & mutated ) ) ;
CBlock block3 ;
2016-11-11 22:01:27 +01:00
BOOST_CHECK ( partialBlock . FillBlock ( block3 , { block . vtx [ 1 ] } ) = = READ_STATUS_OK ) ;
2016-04-26 00:51:08 +02:00
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 ;
2017-08-01 12:22:41 +02:00
explicit TestHeaderAndShortIDs ( const CBlockHeaderAndShortTxIDs & orig ) {
2016-04-26 00:51:08 +02:00
CDataStream stream ( SER_NETWORK , PROTOCOL_VERSION ) ;
stream < < orig ;
stream > > * this ;
}
2017-08-01 12:22:41 +02:00
explicit TestHeaderAndShortIDs ( const CBlock & block ) :
2016-06-25 19:17:45 +02:00
TestHeaderAndShortIDs ( CBlockHeaderAndShortTxIDs ( block , true ) ) { }
2016-04-26 00:51:08 +02:00
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 >
2016-10-29 01:29:17 +02:00
inline void SerializationOp ( Stream & s , Operation ser_action ) {
2016-04-26 00:51:08 +02:00
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 )
{
2017-01-13 22:53:21 +01:00
CTxMemPool pool ;
2016-04-26 00:51:08 +02:00
TestMemPoolEntryHelper entry ;
CBlock block ( BuildBlockTestCase ( ) ) ;
2018-04-11 19:51:28 +02:00
pool . addUnchecked ( block . vtx [ 2 ] - > GetHash ( ) , entry . FromTx ( block . vtx [ 2 ] ) ) ;
2017-11-06 23:08:55 +01:00
LOCK ( pool . cs ) ;
2016-11-11 02:26:00 +01:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( block . vtx [ 2 ] - > GetHash ( ) ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 0 ) ;
uint256 txhash ;
2016-04-26 00:51:08 +02:00
// 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 ) ;
2016-11-11 02:26:00 +01:00
shortIDs . shorttxids [ 0 ] = shortIDs . GetShortID ( block . vtx [ 0 ] - > GetHash ( ) ) ;
shortIDs . shorttxids [ 1 ] = shortIDs . GetShortID ( block . vtx [ 2 ] - > GetHash ( ) ) ;
2016-04-26 00:51:08 +02:00
CDataStream stream ( SER_NETWORK , PROTOCOL_VERSION ) ;
stream < < shortIDs ;
CBlockHeaderAndShortTxIDs shortIDs2 ;
stream > > shortIDs2 ;
PartiallyDownloadedBlock partialBlock ( & pool ) ;
2016-12-05 05:44:37 +01:00
BOOST_CHECK ( partialBlock . InitData ( shortIDs2 , extra_txn ) = = READ_STATUS_OK ) ;
2016-04-26 00:51:08 +02:00
BOOST_CHECK ( ! partialBlock . IsTxAvailable ( 0 ) ) ;
BOOST_CHECK ( partialBlock . IsTxAvailable ( 1 ) ) ;
BOOST_CHECK ( partialBlock . IsTxAvailable ( 2 ) ) ;
2018-04-11 19:51:28 +02:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( block . vtx [ 2 ] - > GetHash ( ) ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 1 ) ; // +1 because of partialBlock
2016-04-26 00:51:08 +02:00
CBlock block2 ;
2016-11-11 22:01:27 +01:00
{
PartiallyDownloadedBlock tmp = partialBlock ;
BOOST_CHECK ( partialBlock . FillBlock ( block2 , { } ) = = READ_STATUS_INVALID ) ; // No transactions
partialBlock = tmp ;
}
2016-04-26 00:51:08 +02:00
2016-11-11 22:01:27 +01:00
// Wrong transaction
{
PartiallyDownloadedBlock tmp = partialBlock ;
partialBlock . FillBlock ( block2 , { block . vtx [ 1 ] } ) ; // Current implementation doesn't check txn here, but don't require that
partialBlock = tmp ;
}
2018-04-11 19:51:28 +02:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( block . vtx [ 2 ] - > GetHash ( ) ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 2 ) ; // +2 because of partialBlock and block2
2016-04-26 00:51:08 +02:00
bool mutated ;
BOOST_CHECK ( block . hashMerkleRoot ! = BlockMerkleRoot ( block2 , & mutated ) ) ;
CBlock block3 ;
2016-11-11 22:01:27 +01:00
PartiallyDownloadedBlock partialBlockCopy = partialBlock ;
BOOST_CHECK ( partialBlock . FillBlock ( block3 , { block . vtx [ 0 ] } ) = = READ_STATUS_OK ) ;
2016-04-26 00:51:08 +02:00
BOOST_CHECK_EQUAL ( block . GetHash ( ) . ToString ( ) , block3 . GetHash ( ) . ToString ( ) ) ;
BOOST_CHECK_EQUAL ( block . hashMerkleRoot . ToString ( ) , BlockMerkleRoot ( block3 , & mutated ) . ToString ( ) ) ;
BOOST_CHECK ( ! mutated ) ;
2018-04-11 19:51:28 +02:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( block . vtx [ 2 ] - > GetHash ( ) ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 3 ) ; // +2 because of partialBlock and block2 and block3
2016-11-11 02:26:00 +01:00
txhash = block . vtx [ 2 ] - > GetHash ( ) ;
block . vtx . clear ( ) ;
block2 . vtx . clear ( ) ;
block3 . vtx . clear ( ) ;
2018-04-11 19:51:28 +02:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( txhash ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 1 - 1 ) ; // + 1 because of partialBlock; -1 because of block.
2016-04-26 00:51:08 +02:00
}
2018-04-11 19:51:28 +02:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( txhash ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET - 1 ) ; // -1 because of block
2016-04-26 00:51:08 +02:00
}
BOOST_AUTO_TEST_CASE ( SufficientPreforwardRTTest )
{
2017-01-13 22:53:21 +01:00
CTxMemPool pool ;
2016-04-26 00:51:08 +02:00
TestMemPoolEntryHelper entry ;
CBlock block ( BuildBlockTestCase ( ) ) ;
2018-04-11 19:51:28 +02:00
pool . addUnchecked ( block . vtx [ 1 ] - > GetHash ( ) , entry . FromTx ( block . vtx [ 1 ] ) ) ;
2017-11-06 23:08:55 +01:00
LOCK ( pool . cs ) ;
2016-11-11 02:26:00 +01:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( block . vtx [ 1 ] - > GetHash ( ) ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 0 ) ;
uint256 txhash ;
2016-04-26 00:51:08 +02:00
// 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 ) ;
2016-11-11 02:26:00 +01:00
shortIDs . shorttxids [ 0 ] = shortIDs . GetShortID ( block . vtx [ 1 ] - > GetHash ( ) ) ;
2016-04-26 00:51:08 +02:00
CDataStream stream ( SER_NETWORK , PROTOCOL_VERSION ) ;
stream < < shortIDs ;
CBlockHeaderAndShortTxIDs shortIDs2 ;
stream > > shortIDs2 ;
PartiallyDownloadedBlock partialBlock ( & pool ) ;
2016-12-05 05:44:37 +01:00
BOOST_CHECK ( partialBlock . InitData ( shortIDs2 , extra_txn ) = = READ_STATUS_OK ) ;
2016-04-26 00:51:08 +02:00
BOOST_CHECK ( partialBlock . IsTxAvailable ( 0 ) ) ;
BOOST_CHECK ( partialBlock . IsTxAvailable ( 1 ) ) ;
BOOST_CHECK ( partialBlock . IsTxAvailable ( 2 ) ) ;
2016-11-11 02:26:00 +01:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( block . vtx [ 1 ] - > GetHash ( ) ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 1 ) ;
2016-04-26 00:51:08 +02:00
CBlock block2 ;
2016-11-11 22:01:27 +01:00
PartiallyDownloadedBlock partialBlockCopy = partialBlock ;
BOOST_CHECK ( partialBlock . FillBlock ( block2 , { } ) = = READ_STATUS_OK ) ;
2016-04-26 00:51:08 +02:00
BOOST_CHECK_EQUAL ( block . GetHash ( ) . ToString ( ) , block2 . GetHash ( ) . ToString ( ) ) ;
bool mutated ;
BOOST_CHECK_EQUAL ( block . hashMerkleRoot . ToString ( ) , BlockMerkleRoot ( block2 , & mutated ) . ToString ( ) ) ;
BOOST_CHECK ( ! mutated ) ;
2016-11-11 02:26:00 +01:00
txhash = block . vtx [ 1 ] - > GetHash ( ) ;
block . vtx . clear ( ) ;
block2 . vtx . clear ( ) ;
2018-04-11 19:51:28 +02:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( txhash ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET + 1 - 1 ) ; // + 1 because of partialBlock; -1 because of block.
2016-04-26 00:51:08 +02:00
}
2018-04-11 19:51:28 +02:00
BOOST_CHECK_EQUAL ( pool . mapTx . find ( txhash ) - > GetSharedTx ( ) . use_count ( ) , SHARED_TX_OFFSET - 1 ) ; // -1 because of block
2016-04-26 00:51:08 +02:00
}
BOOST_AUTO_TEST_CASE ( EmptyBlockRoundTripTest )
{
2017-01-13 22:53:21 +01:00
CTxMemPool pool ;
2016-04-26 00:51:08 +02:00
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 ) ;
2016-11-11 02:34:17 +01:00
block . vtx [ 0 ] = MakeTransactionRef ( std : : move ( coinbase ) ) ;
2016-04-26 00:51:08 +02:00
block . nVersion = 42 ;
2017-06-07 21:03:17 +02:00
block . hashPrevBlock = InsecureRand256 ( ) ;
2016-04-26 00:51:08 +02:00
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
{
2016-06-25 19:17:45 +02:00
CBlockHeaderAndShortTxIDs shortIDs ( block , false ) ;
2016-04-26 00:51:08 +02:00
CDataStream stream ( SER_NETWORK , PROTOCOL_VERSION ) ;
stream < < shortIDs ;
CBlockHeaderAndShortTxIDs shortIDs2 ;
stream > > shortIDs2 ;
PartiallyDownloadedBlock partialBlock ( & pool ) ;
2016-12-05 05:44:37 +01:00
BOOST_CHECK ( partialBlock . InitData ( shortIDs2 , extra_txn ) = = READ_STATUS_OK ) ;
2016-04-26 00:51:08 +02:00
BOOST_CHECK ( partialBlock . IsTxAvailable ( 0 ) ) ;
CBlock block2 ;
2016-11-11 02:34:17 +01:00
std : : vector < CTransactionRef > vtx_missing ;
2016-04-26 00:51:08 +02:00
BOOST_CHECK ( partialBlock . FillBlock ( block2 , vtx_missing ) = = READ_STATUS_OK ) ;
BOOST_CHECK_EQUAL ( block . GetHash ( ) . ToString ( ) , block2 . GetHash ( ) . ToString ( ) ) ;
BOOST_CHECK_EQUAL ( block . hashMerkleRoot . ToString ( ) , BlockMerkleRoot ( block2 , & mutated ) . ToString ( ) ) ;
BOOST_CHECK ( ! mutated ) ;
}
}
BOOST_AUTO_TEST_CASE ( TransactionsRequestSerializationTest ) {
BlockTransactionsRequest req1 ;
2017-06-07 21:03:17 +02:00
req1 . blockhash = InsecureRand256 ( ) ;
2016-04-26 00:51:08 +02:00
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 ( )