@ -34,27 +34,27 @@ bool operator==(const Coin &a, const Coin &b) {
class CCoinsViewTest : public CCoinsView
class CCoinsViewTest : public CCoinsView
{
{
uint256 hashBestBlock_ ;
uint256 hashBestBlock_ ;
std : : map < uint256 , CC oins > map_ ;
std : : map < CO utPo int, Coin > map_ ;
public :
public :
bool GetCoins ( const uint256 & txid , CCoins & coins ) const
bool GetCoins ( const CO utPo int& outpoint , Coin & coin ) const
{
{
std : : map < uint256 , CC oins > : : const_iterator it = map_ . find ( txid ) ;
std : : map < CO utPo int, Coin > : : const_iterator it = map_ . find ( outpoint ) ;
if ( it = = map_ . end ( ) ) {
if ( it = = map_ . end ( ) ) {
return false ;
return false ;
}
}
coins = it - > second ;
coin = it - > second ;
if ( coins . IsPruned ( ) & & insecure_rand ( ) % 2 = = 0 ) {
if ( coin . IsPruned ( ) & & insecure_rand ( ) % 2 = = 0 ) {
// Randomly return false in case of an empty entry.
// Randomly return false in case of an empty entry.
return false ;
return false ;
}
}
return true ;
return true ;
}
}
bool HaveCoins ( const uint256 & txid ) const
bool HaveCoins ( const CO utPo int& outpoint ) const
{
{
CC oins coins ;
Coin coin ;
return GetCoins ( txid , coins ) ;
return GetCoins ( outpoint , coin ) ;
}
}
uint256 GetBestBlock ( ) const { return hashBestBlock_ ; }
uint256 GetBestBlock ( ) const { return hashBestBlock_ ; }
@ -106,7 +106,7 @@ static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
// This is a large randomized insert/remove simulation test on a variable-size
// This is a large randomized insert/remove simulation test on a variable-size
// stack of caches on top of CCoinsViewTest.
// stack of caches on top of CCoinsViewTest.
//
//
// It will randomly create/update/delete CC oins entries to a tip of caches, with
// It will randomly create/update/delete Coin entries to a tip of caches, with
// txids picked from a limited list of random 256-bit hashes. Occasionally, a
// txids picked from a limited list of random 256-bit hashes. Occasionally, a
// new tip is added to the stack of caches, or the tip is flushed and removed.
// new tip is added to the stack of caches, or the tip is flushed and removed.
//
//
@ -124,7 +124,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
bool missed_an_entry = false ;
bool missed_an_entry = false ;
// A simple map to track what we expect the cache stack to represent.
// A simple map to track what we expect the cache stack to represent.
std : : map < uint256 , CC oins > result ;
std : : map < CO utPo int, Coin > result ;
// The cache stack.
// The cache stack.
CCoinsViewTest base ; // A CCoinsViewTest at the bottom.
CCoinsViewTest base ; // A CCoinsViewTest at the bottom.
@ -142,39 +142,38 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
// Do a random modification.
// Do a random modification.
{
{
uint256 txid = txids [ insecure_rand ( ) % txids . size ( ) ] ; // txid we're going to modify in this iteration.
uint256 txid = txids [ insecure_rand ( ) % txids . size ( ) ] ; // txid we're going to modify in this iteration.
CC oins & coins = result [ txid ] ;
Coin & coin = result [ COutPoint ( txid , 0 ) ] ;
const Coin & entry = stack . back ( ) - > AccessCoin ( COutPoint ( txid , 0 ) ) ;
const Coin & entry = stack . back ( ) - > AccessCoin ( COutPoint ( txid , 0 ) ) ;
BOOST_CHECK ( ( entry . IsPruned ( ) & & coins . IsPruned ( ) ) | | entry = = Coin ( coins . vout [ 0 ] , coins . nHeight , coins . fCoinBase ) ) ;
BOOST_CHECK ( coin = = entry ) ;
if ( insecure_rand ( ) % 5 = = 0 | | coins . IsPruned ( ) ) {
if ( insecure_rand ( ) % 5 = = 0 | | coin . IsPruned ( ) ) {
if ( coins . IsPruned ( ) ) {
if ( coin . IsPruned ( ) ) {
added_an_entry = true ;
added_an_entry = true ;
} else {
} else {
updated_an_entry = true ;
updated_an_entry = true ;
}
}
coins . v out. resize ( 1 ) ;
coin . out . nValue = insecure_rand ( ) ;
coins . vout [ 0 ] . nValue = insecure_rand ( ) ;
coin . nHeight = 1 ;
} else {
} else {
coins . Clear ( ) ;
coin . Clear ( ) ;
removed_an_entry = true ;
removed_an_entry = true ;
}
}
if ( coins . IsPruned ( ) ) {
if ( coin . IsPruned ( ) ) {
stack . back ( ) - > SpendCoin ( COutPoint ( txid , 0 ) ) ;
stack . back ( ) - > SpendCoin ( COutPoint ( txid , 0 ) ) ;
} else {
} else {
stack . back ( ) - > AddCoin ( COutPoint ( txid , 0 ) , Coin ( coins . vout [ 0 ] , coins . nHeight , coins . fCoinBase ) , true ) ;
stack . back ( ) - > AddCoin ( COutPoint ( txid , 0 ) , Coin ( coin ) , true ) ;
}
}
}
}
// Once every 1000 iterations and at the end, verify the full cache.
// Once every 1000 iterations and at the end, verify the full cache.
if ( insecure_rand ( ) % 1000 = = 1 | | i = = NUM_SIMULATION_ITERATIONS - 1 ) {
if ( insecure_rand ( ) % 1000 = = 1 | | i = = NUM_SIMULATION_ITERATIONS - 1 ) {
for ( std : : map < uint256 , CCoins > : : iterator it = result . begin ( ) ; it ! = result . end ( ) ; it + + ) {
for ( auto it = result . begin ( ) ; it ! = result . end ( ) ; it + + ) {
const CCoins * coins = stack . back ( ) - > AccessCoins ( it - > first ) ;
const Coin & coin = stack . back ( ) - > AccessCoin ( it - > first ) ;
if ( coins ) {
BOOST_CHECK ( coin = = it - > second ) ;
BOOST_CHECK ( * coins = = it - > second ) ;
if ( coin . IsPruned ( ) ) {
found_an_entry = true ;
} else {
BOOST_CHECK ( it - > second . IsPruned ( ) ) ;
missed_an_entry = true ;
missed_an_entry = true ;
} else {
found_an_entry = true ;
}
}
}
}
BOOST_FOREACH ( const CCoinsViewCacheTest * test , stack ) {
BOOST_FOREACH ( const CCoinsViewCacheTest * test , stack ) {
@ -229,19 +228,19 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
BOOST_CHECK ( missed_an_entry ) ;
BOOST_CHECK ( missed_an_entry ) ;
}
}
typedef std : : tuple < CTransaction , CTxUndo , CCoins > TxData ;
// Store of all necessary tx and undo data for next test
// Store of all necessary tx and undo data for next test
std : : map < uint256 , TxData > alltxs ;
typedef std : : map < COutPoint , std : : tuple < CTransaction , CTxUndo , Coin > > UtxoData ;
UtxoData utxoData ;
TxData & FindRandomFrom ( const std : : set < uint256 > & txids et) {
UtxoData : : iterator FindRandomFrom ( const std : : set < CO utPo int> & utxoS et) {
assert ( txids et. size ( ) ) ;
assert ( utxoS et. size ( ) ) ;
std : : set < uint256 > : : iterator txIt = txidset . lower_bound ( GetRandHash ( ) ) ;
auto utxoSetIt = utxoSet . lower_bound ( COutPoint ( GetRandHash ( ) , 0 ) ) ;
if ( txIt = = txids et. end ( ) ) {
if ( u txoSet It = = utxoS et. end ( ) ) {
txIt = txids et. begin ( ) ;
u txoSet It = utxoS et. begin ( ) ;
}
}
std : : map < uint256 , TxData > : : iterator txdit = alltxs . find ( * txIt ) ;
auto utxoDataIt = utxoData . find ( * u txoSet It) ;
assert ( txdit ! = alltxs . end ( ) ) ;
assert ( utxoDataIt ! = utxoData . end ( ) ) ;
return txdit - > second ;
return utxoDataIt ;
}
}
@ -254,7 +253,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
{
{
bool spent_a_duplicate_coinbase = false ;
bool spent_a_duplicate_coinbase = false ;
// A simple map to track what we expect the cache stack to represent.
// A simple map to track what we expect the cache stack to represent.
std : : map < uint256 , CC oins > result ;
std : : map < CO utPo int, Coin > result ;
// The cache stack.
// The cache stack.
CCoinsViewTest base ; // A CCoinsViewTest at the bottom.
CCoinsViewTest base ; // A CCoinsViewTest at the bottom.
@ -262,10 +261,10 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
stack . push_back ( new CCoinsViewCacheTest ( & base ) ) ; // Start with one cache.
stack . push_back ( new CCoinsViewCacheTest ( & base ) ) ; // Start with one cache.
// Track the txids we've used in various sets
// Track the txids we've used in various sets
std : : set < uint256 > coinbaseids ;
std : : set < CO utPo int> coinbaseids ;
std : : set < uint256 > disconnectedids ;
std : : set < CO utPo int> disconnectedids ;
std : : set < uint256 > duplicateids ;
std : : set < CO utPo int> duplicateids ;
std : : set < uint256 > utxoset ;
std : : set < CO utPo int> utxoset ;
for ( unsigned int i = 0 ; i < NUM_SIMULATION_ITERATIONS ; i + + ) {
for ( unsigned int i = 0 ; i < NUM_SIMULATION_ITERATIONS ; i + + ) {
uint32_t randiter = insecure_rand ( ) ;
uint32_t randiter = insecure_rand ( ) ;
@ -277,22 +276,22 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
tx . vout . resize ( 1 ) ;
tx . vout . resize ( 1 ) ;
tx . vout [ 0 ] . nValue = i ; //Keep txs unique unless intended to duplicate
tx . vout [ 0 ] . nValue = i ; //Keep txs unique unless intended to duplicate
unsigned int height = insecure_rand ( ) ;
unsigned int height = insecure_rand ( ) ;
CC oins oldcoins ;
Coin oldcoins ;
// 2/20 times create a new coinbase
// 2/20 times create a new coinbase
if ( randiter % 20 < 2 | | coinbaseids . size ( ) < 10 ) {
if ( randiter % 20 < 2 | | coinbaseids . size ( ) < 10 ) {
// 1/10 of those times create a duplicate coinbase
// 1/10 of those times create a duplicate coinbase
if ( insecure_rand ( ) % 10 = = 0 & & coinbaseids . size ( ) ) {
if ( insecure_rand ( ) % 10 = = 0 & & coinbaseids . size ( ) ) {
TxData & tx d = FindRandomFrom ( coinbaseids ) ;
auto utxo d = FindRandomFrom ( coinbaseids ) ;
// Reuse the exact same coinbase
// Reuse the exact same coinbase
tx = std : : get < 0 > ( txd ) ;
tx = std : : get < 0 > ( u txod - > secon d) ;
// shouldn't be available for reconnection if its been duplicated
// shouldn't be available for reconnection if its been duplicated
disconnectedids . erase ( tx . GetHash ( ) ) ;
disconnectedids . erase ( utxod - > first ) ;
duplicateids . insert ( tx . GetHash ( ) ) ;
duplicateids . insert ( utxod - > first ) ;
}
}
else {
else {
coinbaseids . insert ( tx . GetHash ( ) ) ;
coinbaseids . insert ( COutPoint ( tx . GetHash ( ) , 0 ) ) ;
}
}
assert ( CTransaction ( tx ) . IsCoinBase ( ) ) ;
assert ( CTransaction ( tx ) . IsCoinBase ( ) ) ;
}
}
@ -300,85 +299,82 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
// 17/20 times reconnect previous or add a regular tx
// 17/20 times reconnect previous or add a regular tx
else {
else {
uint256 prevouthash ;
CO utPo int prevout ;
// 1/20 times reconnect a previously disconnected tx
// 1/20 times reconnect a previously disconnected tx
if ( randiter % 20 = = 2 & & disconnectedids . size ( ) ) {
if ( randiter % 20 = = 2 & & disconnectedids . size ( ) ) {
TxData & tx d = FindRandomFrom ( disconnectedids ) ;
auto utxo d = FindRandomFrom ( disconnectedids ) ;
tx = std : : get < 0 > ( txd ) ;
tx = std : : get < 0 > ( u txod - > secon d) ;
prevouthash = tx . vin [ 0 ] . prevout . hash ;
prevout = tx . vin [ 0 ] . prevout ;
if ( ! CTransaction ( tx ) . IsCoinBase ( ) & & ! utxoset . count ( prevouthash ) ) {
if ( ! CTransaction ( tx ) . IsCoinBase ( ) & & ! utxoset . count ( prevout ) ) {
disconnectedids . erase ( tx . GetHash ( ) ) ;
disconnectedids . erase ( utxod - > first ) ;
continue ;
continue ;
}
}
// If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate
// If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate
if ( utxoset . count ( tx . GetHash ( ) ) ) {
if ( utxoset . count ( utxod - > first ) ) {
assert ( CTransaction ( tx ) . IsCoinBase ( ) ) ;
assert ( CTransaction ( tx ) . IsCoinBase ( ) ) ;
assert ( duplicateids . count ( tx . GetHash ( ) ) ) ;
assert ( duplicateids . count ( utxod - > first ) ) ;
}
}
disconnectedids . erase ( tx . GetHash ( ) ) ;
disconnectedids . erase ( utxod - > first ) ;
}
}
// 16/20 times create a regular tx
// 16/20 times create a regular tx
else {
else {
TxData & tx d = FindRandomFrom ( utxoset ) ;
auto utxo d = FindRandomFrom ( utxoset ) ;
prevouthash = std : : get < 0 > ( txd ) . GetHash ( ) ;
prevout = utxod - > first ;
// Construct the tx to spend the coins of prevouthash
// Construct the tx to spend the coins of prevouthash
tx . vin [ 0 ] . prevout . hash = prevouthash ;
tx . vin [ 0 ] . prevout = prevout ;
tx . vin [ 0 ] . prevout . n = 0 ;
assert ( ! CTransaction ( tx ) . IsCoinBase ( ) ) ;
assert ( ! CTransaction ( tx ) . IsCoinBase ( ) ) ;
}
}
// In this simple test coins only have two states, spent or unspent, save the unspent state to restore
// In this simple test coins only have two states, spent or unspent, save the unspent state to restore
oldcoins = result [ prevouthash ] ;
oldcoins = result [ prevout ] ;
// Update the expected result of prevouthash to know these coins are spent
// Update the expected result of prevouthash to know these coins are spent
result [ prevouthash ] . Clear ( ) ;
result [ prevout ] . Clear ( ) ;
utxoset . erase ( prevouthash ) ;
utxoset . erase ( prevout ) ;
// The test is designed to ensure spending a duplicate coinbase will work properly
// The test is designed to ensure spending a duplicate coinbase will work properly
// if that ever happens and not resurrect the previously overwritten coinbase
// if that ever happens and not resurrect the previously overwritten coinbase
if ( duplicateids . count ( prevouthash ) ) {
if ( duplicateids . count ( prevout ) ) {
spent_a_duplicate_coinbase = true ;
spent_a_duplicate_coinbase = true ;
}
}
}
}
// Update the expected result to know about the new output coins
// Update the expected result to know about the new output coins
result [ tx . GetHash ( ) ] . FromTx ( tx , height ) ;
assert ( tx . vout . size ( ) = = 1 ) ;
const COutPoint outpoint ( tx . GetHash ( ) , 0 ) ;
result [ outpoint ] = Coin ( tx . vout [ 0 ] , height , CTransaction ( tx ) . IsCoinBase ( ) ) ;
// Call UpdateCoins on the top cache
// Call UpdateCoins on the top cache
CTxUndo undo ;
CTxUndo undo ;
UpdateCoins ( tx , * ( stack . back ( ) ) , undo , height ) ;
UpdateCoins ( tx , * ( stack . back ( ) ) , undo , height ) ;
// Update the utxo set for future spends
// Update the utxo set for future spends
utxoset . insert ( tx . GetHash ( ) ) ;
utxoset . insert ( outpoint ) ;
// Track this tx and undo info to use later
// Track this tx and undo info to use later
alltxs . insert ( std : : make_pair ( tx . GetHash ( ) , std : : make_tuple ( tx , undo , oldcoins ) ) ) ;
utxoData . emplace ( outpoint , std : : make_tuple ( tx , undo , oldcoins ) ) ;
} else if ( utxoset . size ( ) ) {
} else if ( utxoset . size ( ) ) {
//1/20 times undo a previous transaction
//1/20 times undo a previous transaction
TxData & txd = FindRandomFrom ( utxoset ) ;
auto utxod = FindRandomFrom ( utxoset ) ;
CTransaction & tx = std : : get < 0 > ( txd ) ;
CTxUndo & undo = std : : get < 1 > ( txd ) ;
CCoins & origcoins = std : : get < 2 > ( txd ) ;
uint256 undohash = tx . GetHash ( ) ;
CTransaction & tx = std : : get < 0 > ( utxod - > second ) ;
CTxUndo & undo = std : : get < 1 > ( utxod - > second ) ;
Coin & origcoins = std : : get < 2 > ( utxod - > second ) ;
// Update the expected result
// Update the expected result
// Remove new outputs
// Remove new outputs
result [ undohash ] . Clear ( ) ;
result [ utxod - > first ] . Clear ( ) ;
// If not coinbase restore prevout
// If not coinbase restore prevout
if ( ! tx . IsCoinBase ( ) ) {
if ( ! tx . IsCoinBase ( ) ) {
result [ tx . vin [ 0 ] . prevout . hash ] = origcoins ;
result [ tx . vin [ 0 ] . prevout ] = origcoins ;
}
}
// Disconnect the tx from the current UTXO
// Disconnect the tx from the current UTXO
// See code in DisconnectBlock
// See code in DisconnectBlock
// remove outputs
// remove outputs
{
stack . back ( ) - > SpendCoin ( utxod - > first ) ;
stack . back ( ) - > SpendCoin ( COutPoint ( undohash , 0 ) ) ;
}
// restore inputs
// restore inputs
if ( ! tx . IsCoinBase ( ) ) {
if ( ! tx . IsCoinBase ( ) ) {
const COutPoint & out = tx . vin [ 0 ] . prevout ;
const COutPoint & out = tx . vin [ 0 ] . prevout ;
@ -386,23 +382,19 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
ApplyTxInUndo ( std : : move ( coin ) , * ( stack . back ( ) ) , out ) ;
ApplyTxInUndo ( std : : move ( coin ) , * ( stack . back ( ) ) , out ) ;
}
}
// Store as a candidate for reconnection
// Store as a candidate for reconnection
disconnectedids . insert ( undohash ) ;
disconnectedids . insert ( utxod - > first ) ;
// Update the utxoset
// Update the utxoset
utxoset . erase ( undohash ) ;
utxoset . erase ( utxod - > first ) ;
if ( ! tx . IsCoinBase ( ) )
if ( ! tx . IsCoinBase ( ) )
utxoset . insert ( tx . vin [ 0 ] . prevout . hash ) ;
utxoset . insert ( tx . vin [ 0 ] . prevout ) ;
}
}
// Once every 1000 iterations and at the end, verify the full cache.
// Once every 1000 iterations and at the end, verify the full cache.
if ( insecure_rand ( ) % 1000 = = 1 | | i = = NUM_SIMULATION_ITERATIONS - 1 ) {
if ( insecure_rand ( ) % 1000 = = 1 | | i = = NUM_SIMULATION_ITERATIONS - 1 ) {
for ( std : : map < uint256 , CCoins > : : iterator it = result . begin ( ) ; it ! = result . end ( ) ; it + + ) {
for ( auto it = result . begin ( ) ; it ! = result . end ( ) ; it + + ) {
const CCoins * coins = stack . back ( ) - > AccessCoins ( it - > first ) ;
const Coin & coin = stack . back ( ) - > AccessCoin ( it - > first ) ;
if ( coins ) {
BOOST_CHECK ( coin = = it - > second ) ;
BOOST_CHECK ( * coins = = it - > second ) ;
} else {
BOOST_CHECK ( it - > second . IsPruned ( ) ) ;
}
}
}
}
}
@ -443,50 +435,36 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
BOOST_AUTO_TEST_CASE ( ccoins_serialization )
BOOST_AUTO_TEST_CASE ( ccoins_serialization )
{
{
// Good example
// Good example
CDataStream ss1 ( ParseHex ( " 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e " ) , SER_DISK , CLIENT_VERSION ) ;
CDataStream ss1 ( ParseHex ( " 97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35 " ) , SER_DISK , CLIENT_VERSION ) ;
CC oins cc1 ;
Coin cc1 ;
ss1 > > cc1 ;
ss1 > > cc1 ;
BOOST_CHECK_EQUAL ( cc1 . fCoinBase , false ) ;
BOOST_CHECK_EQUAL ( cc1 . fCoinBase , false ) ;
BOOST_CHECK_EQUAL ( cc1 . nHeight , 203998 ) ;
BOOST_CHECK_EQUAL ( cc1 . nHeight , 203998 ) ;
BOOST_CHECK_EQUAL ( cc1 . vout . size ( ) , 2 ) ;
BOOST_CHECK_EQUAL ( cc1 . out . nValue , 60000000000ULL ) ;
BOOST_CHECK_EQUAL ( cc1 . IsAvailable ( 0 ) , false ) ;
BOOST_CHECK_EQUAL ( HexStr ( cc1 . out . scriptPubKey ) , HexStr ( GetScriptForDestination ( CKeyID ( uint160 ( ParseHex ( " 816115944e077fe7c803cfa57f29b36bf87c1d35 " ) ) ) ) ) ) ;
BOOST_CHECK_EQUAL ( cc1 . IsAvailable ( 1 ) , true ) ;
BOOST_CHECK_EQUAL ( cc1 . vout [ 1 ] . nValue , 60000000000ULL ) ;
BOOST_CHECK_EQUAL ( HexStr ( cc1 . vout [ 1 ] . scriptPubKey ) , HexStr ( GetScriptForDestination ( CKeyID ( uint160 ( ParseHex ( " 816115944e077fe7c803cfa57f29b36bf87c1d35 " ) ) ) ) ) ) ;
// Good example
// Good example
CDataStream ss2 ( ParseHex ( " 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b " ) , SER_DISK , CLIENT_VERSION ) ;
CDataStream ss2 ( ParseHex ( " 8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4 " ) , SER_DISK , CLIENT_VERSION ) ;
CC oins cc2 ;
Coin cc2 ;
ss2 > > cc2 ;
ss2 > > cc2 ;
BOOST_CHECK_EQUAL ( cc2 . fCoinBase , true ) ;
BOOST_CHECK_EQUAL ( cc2 . fCoinBase , true ) ;
BOOST_CHECK_EQUAL ( cc2 . nHeight , 120891 ) ;
BOOST_CHECK_EQUAL ( cc2 . nHeight , 120891 ) ;
BOOST_CHECK_EQUAL ( cc2 . vout . size ( ) , 17 ) ;
BOOST_CHECK_EQUAL ( cc2 . out . nValue , 110397 ) ;
for ( int i = 0 ; i < 17 ; i + + ) {
BOOST_CHECK_EQUAL ( HexStr ( cc2 . out . scriptPubKey ) , HexStr ( GetScriptForDestination ( CKeyID ( uint160 ( ParseHex ( " 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4 " ) ) ) ) ) ) ;
BOOST_CHECK_EQUAL ( cc2 . IsAvailable ( i ) , i = = 4 | | i = = 16 ) ;
}
BOOST_CHECK_EQUAL ( cc2 . vout [ 4 ] . nValue , 234925952 ) ;
BOOST_CHECK_EQUAL ( HexStr ( cc2 . vout [ 4 ] . scriptPubKey ) , HexStr ( GetScriptForDestination ( CKeyID ( uint160 ( ParseHex ( " 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee " ) ) ) ) ) ) ;
BOOST_CHECK_EQUAL ( cc2 . vout [ 16 ] . nValue , 110397 ) ;
BOOST_CHECK_EQUAL ( HexStr ( cc2 . vout [ 16 ] . scriptPubKey ) , HexStr ( GetScriptForDestination ( CKeyID ( uint160 ( ParseHex ( " 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4 " ) ) ) ) ) ) ;
// Smallest possible example
// Smallest possible example
CDataStream ssx ( SER_DISK , CLIENT_VERSION ) ;
CDataStream ss3 ( ParseHex ( " 000006 " ) , SER_DISK , CLIENT_VERSION ) ;
BOOST_CHECK_EQUAL ( HexStr ( ssx . begin ( ) , ssx . end ( ) ) , " " ) ;
Coin cc3 ;
CDataStream ss3 ( ParseHex ( " 0002000600 " ) , SER_DISK , CLIENT_VERSION ) ;
CCoins cc3 ;
ss3 > > cc3 ;
ss3 > > cc3 ;
BOOST_CHECK_EQUAL ( cc3 . fCoinBase , false ) ;
BOOST_CHECK_EQUAL ( cc3 . fCoinBase , false ) ;
BOOST_CHECK_EQUAL ( cc3 . nHeight , 0 ) ;
BOOST_CHECK_EQUAL ( cc3 . nHeight , 0 ) ;
BOOST_CHECK_EQUAL ( cc3 . vout . size ( ) , 1 ) ;
BOOST_CHECK_EQUAL ( cc3 . out . nValue , 0 ) ;
BOOST_CHECK_EQUAL ( cc3 . IsAvailable ( 0 ) , true ) ;
BOOST_CHECK_EQUAL ( cc3 . out . scriptPubKey . size ( ) , 0 ) ;
BOOST_CHECK_EQUAL ( cc3 . vout [ 0 ] . nValue , 0 ) ;
BOOST_CHECK_EQUAL ( cc3 . vout [ 0 ] . scriptPubKey . size ( ) , 0 ) ;
// scriptPubKey that ends beyond the end of the stream
// scriptPubKey that ends beyond the end of the stream
CDataStream ss4 ( ParseHex ( " 0002000800 " ) , SER_DISK , CLIENT_VERSION ) ;
CDataStream ss4 ( ParseHex ( " 000007 " ) , SER_DISK , CLIENT_VERSION ) ;
try {
try {
CC oins cc4 ;
Coin cc4 ;
ss4 > > cc4 ;
ss4 > > cc4 ;
BOOST_CHECK_MESSAGE ( false , " We should have thrown " ) ;
BOOST_CHECK_MESSAGE ( false , " We should have thrown " ) ;
} catch ( const std : : ios_base : : failure & e ) {
} catch ( const std : : ios_base : : failure & e ) {
@ -497,17 +475,16 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
uint64_t x = 3000000000ULL ;
uint64_t x = 3000000000ULL ;
tmp < < VARINT ( x ) ;
tmp < < VARINT ( x ) ;
BOOST_CHECK_EQUAL ( HexStr ( tmp . begin ( ) , tmp . end ( ) ) , " 8a95c0bb00 " ) ;
BOOST_CHECK_EQUAL ( HexStr ( tmp . begin ( ) , tmp . end ( ) ) , " 8a95c0bb00 " ) ;
CDataStream ss5 ( ParseHex ( " 00020 08a95c0bb00 00 " ) , SER_DISK , CLIENT_VERSION ) ;
CDataStream ss5 ( ParseHex ( " 00008a95c0bb00 " ) , SER_DISK , CLIENT_VERSION ) ;
try {
try {
CC oins cc5 ;
Coin cc5 ;
ss5 > > cc5 ;
ss5 > > cc5 ;
BOOST_CHECK_MESSAGE ( false , " We should have thrown " ) ;
BOOST_CHECK_MESSAGE ( false , " We should have thrown " ) ;
} catch ( const std : : ios_base : : failure & e ) {
} catch ( const std : : ios_base : : failure & e ) {
}
}
}
}
const static uint256 TXID ;
const static COutPoint OUTPOINT ;
const static COutPoint OUTPOINT = { uint256 ( ) , 0 } ;
const static CAmount PRUNED = - 1 ;
const static CAmount PRUNED = - 1 ;
const static CAmount ABSENT = - 2 ;
const static CAmount ABSENT = - 2 ;
const static CAmount FAIL = - 3 ;
const static CAmount FAIL = - 3 ;
@ -522,15 +499,15 @@ const static auto FLAGS = {char(0), FRESH, DIRTY, char(DIRTY | FRESH)};
const static auto CLEAN_FLAGS = { char ( 0 ) , FRESH } ;
const static auto CLEAN_FLAGS = { char ( 0 ) , FRESH } ;
const static auto ABSENT_FLAGS = { NO_ENTRY } ;
const static auto ABSENT_FLAGS = { NO_ENTRY } ;
void SetCoinsValue ( CAmount value , CC oins & coins )
void SetCoinsValue ( CAmount value , Coin & coin )
{
{
assert ( value ! = ABSENT ) ;
assert ( value ! = ABSENT ) ;
coins . Clear ( ) ;
coin . Clear ( ) ;
assert ( coins . IsPruned ( ) ) ;
assert ( coin . IsPruned ( ) ) ;
if ( value ! = PRUNED ) {
if ( value ! = PRUNED ) {
coins . v out. emplace_back ( ) ;
coin . out . nValue = value ;
coins . vout . back ( ) . nValue = value ;
coin . nHeight = 1 ;
assert ( ! coins . IsPruned ( ) ) ;
assert ( ! coin . IsPruned ( ) ) ;
}
}
}
}
@ -544,24 +521,22 @@ size_t InsertCoinsMapEntry(CCoinsMap& map, CAmount value, char flags)
CCoinsCacheEntry entry ;
CCoinsCacheEntry entry ;
entry . flags = flags ;
entry . flags = flags ;
SetCoinsValue ( value , entry . coins ) ;
SetCoinsValue ( value , entry . coins ) ;
auto inserted = map . emplace ( TXID , std : : move ( entry ) ) ;
auto inserted = map . emplace ( OUTPOINT , std : : move ( entry ) ) ;
assert ( inserted . second ) ;
assert ( inserted . second ) ;
return inserted . first - > second . coins . DynamicMemoryUsage ( ) ;
return inserted . first - > second . coins . DynamicMemoryUsage ( ) ;
}
}
void GetCoinsMapEntry ( const CCoinsMap & map , CAmount & value , char & flags )
void GetCoinsMapEntry ( const CCoinsMap & map , CAmount & value , char & flags )
{
{
auto it = map . find ( TXID ) ;
auto it = map . find ( OUTPOINT ) ;
if ( it = = map . end ( ) ) {
if ( it = = map . end ( ) ) {
value = ABSENT ;
value = ABSENT ;
flags = NO_ENTRY ;
flags = NO_ENTRY ;
} else {
} else {
if ( it - > second . coins . IsPruned ( ) ) {
if ( it - > second . coins . IsPruned ( ) ) {
assert ( it - > second . coins . vout . size ( ) = = 0 ) ;
value = PRUNED ;
value = PRUNED ;
} else {
} else {
assert ( it - > second . coins . vout . size ( ) = = 1 ) ;
value = it - > second . coins . out . nValue ;
value = it - > second . coins . vout [ 0 ] . nValue ;
}
}
flags = it - > second . flags ;
flags = it - > second . flags ;
assert ( flags ! = NO_ENTRY ) ;
assert ( flags ! = NO_ENTRY ) ;