// Copyright (c) 2012-2016 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 "coins.h"
# include "consensus/consensus.h"
# include "memusage.h"
# include "random.h"
# include <assert.h>
bool CCoinsView : : GetCoins ( const COutPoint & outpoint , Coin & coin ) const { return false ; }
bool CCoinsView : : HaveCoins ( const COutPoint & outpoint ) const { return false ; }
uint256 CCoinsView : : GetBestBlock ( ) const { return uint256 ( ) ; }
bool CCoinsView : : BatchWrite ( CCoinsMap & mapCoins , const uint256 & hashBlock ) { return false ; }
CCoinsViewCursor * CCoinsView : : Cursor ( ) const { return 0 ; }
CCoinsViewBacked : : CCoinsViewBacked ( CCoinsView * viewIn ) : base ( viewIn ) { }
bool CCoinsViewBacked : : GetCoins ( const COutPoint & outpoint , Coin & coin ) const { return base - > GetCoins ( outpoint , coin ) ; }
bool CCoinsViewBacked : : HaveCoins ( const COutPoint & outpoint ) const { return base - > HaveCoins ( outpoint ) ; }
uint256 CCoinsViewBacked : : GetBestBlock ( ) const { return base - > GetBestBlock ( ) ; }
void CCoinsViewBacked : : SetBackend ( CCoinsView & viewIn ) { base = & viewIn ; }
bool CCoinsViewBacked : : BatchWrite ( CCoinsMap & mapCoins , const uint256 & hashBlock ) { return base - > BatchWrite ( mapCoins , hashBlock ) ; }
CCoinsViewCursor * CCoinsViewBacked : : Cursor ( ) const { return base - > Cursor ( ) ; }
size_t CCoinsViewBacked : : EstimateSize ( ) const { return base - > EstimateSize ( ) ; }
SaltedOutpointHasher : : SaltedOutpointHasher ( ) : k0 ( GetRand ( std : : numeric_limits < uint64_t > : : max ( ) ) ) , k1 ( GetRand ( std : : numeric_limits < uint64_t > : : max ( ) ) ) { }
CCoinsViewCache : : CCoinsViewCache ( CCoinsView * baseIn ) : CCoinsViewBacked ( baseIn ) , cachedCoinsUsage ( 0 ) { }
size_t CCoinsViewCache : : DynamicMemoryUsage ( ) const {
return memusage : : DynamicUsage ( cacheCoins ) + cachedCoinsUsage ;
}
CCoinsMap : : iterator CCoinsViewCache : : FetchCoins ( const COutPoint & outpoint ) const {
CCoinsMap : : iterator it = cacheCoins . find ( outpoint ) ;
if ( it ! = cacheCoins . end ( ) )
return it ;
Coin tmp ;
if ( ! base - > GetCoins ( outpoint , tmp ) )
return cacheCoins . end ( ) ;
CCoinsMap : : iterator ret = cacheCoins . emplace ( std : : piecewise_construct , std : : forward_as_tuple ( outpoint ) , std : : forward_as_tuple ( std : : move ( tmp ) ) ) . first ;
if ( ret - > second . coins . IsPruned ( ) ) {
// The parent only has an empty entry for this outpoint; we can consider our
// version as fresh.
ret - > second . flags = CCoinsCacheEntry : : FRESH ;
}
cachedCoinsUsage + = ret - > second . coins . DynamicMemoryUsage ( ) ;
return ret ;
}
bool CCoinsViewCache : : GetCoins ( const COutPoint & outpoint , Coin & coin ) const {
CCoinsMap : : const_iterator it = FetchCoins ( outpoint ) ;
if ( it ! = cacheCoins . end ( ) ) {
coin = it - > second . coins ;
return true ;
}
return false ;
}
void CCoinsViewCache : : AddCoin ( const COutPoint & outpoint , Coin & & coin , bool possible_overwrite ) {
assert ( ! coin . IsPruned ( ) ) ;
if ( coin . out . scriptPubKey . IsUnspendable ( ) ) return ;
CCoinsMap : : iterator it ;
bool inserted ;
std : : tie ( it , inserted ) = cacheCoins . emplace ( std : : piecewise_construct , std : : forward_as_tuple ( outpoint ) , std : : tuple < > ( ) ) ;
bool fresh = false ;
if ( ! inserted ) {
cachedCoinsUsage - = it - > second . coins . DynamicMemoryUsage ( ) ;
}
if ( ! possible_overwrite ) {
if ( ! it - > second . coins . IsPruned ( ) ) {
throw std : : logic_error ( " Adding new coin that replaces non-pruned entry " ) ;
}
fresh = ! ( it - > second . flags & CCoinsCacheEntry : : DIRTY ) ;
}
it - > second . coins = std : : move ( coin ) ;
it - > second . flags | = CCoinsCacheEntry : : DIRTY | ( fresh ? CCoinsCacheEntry : : FRESH : 0 ) ;
cachedCoinsUsage + = it - > second . coins . DynamicMemoryUsage ( ) ;
}
void AddCoins ( CCoinsViewCache & cache , const CTransaction & tx , int nHeight ) {
bool fCoinbase = tx . IsCoinBase ( ) ;
const uint256 & txid = tx . GetHash ( ) ;
for ( size_t i = 0 ; i < tx . vout . size ( ) ; + + i ) {
// Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly
// deal with the pre-BIP30 occurrances of duplicate coinbase transactions.
cache . AddCoin ( COutPoint ( txid , i ) , Coin ( tx . vout [ i ] , nHeight , fCoinbase ) , fCoinbase ) ;
}
}
void CCoinsViewCache : : SpendCoin ( const COutPoint & outpoint , Coin * moveout ) {
CCoinsMap : : iterator it = FetchCoins ( outpoint ) ;
if ( it = = cacheCoins . end ( ) ) return ;
cachedCoinsUsage - = it - > second . coins . DynamicMemoryUsage ( ) ;
if ( moveout ) {
* moveout = std : : move ( it - > second . coins ) ;
}
if ( it - > second . flags & CCoinsCacheEntry : : FRESH ) {
cacheCoins . erase ( it ) ;
} else {
it - > second . flags | = CCoinsCacheEntry : : DIRTY ;
it - > second . coins . Clear ( ) ;
}
}
static const Coin coinEmpty ;
const Coin & CCoinsViewCache : : AccessCoin ( const COutPoint & outpoint ) const {
CCoinsMap : : const_iterator it = FetchCoins ( outpoint ) ;
if ( it = = cacheCoins . end ( ) ) {
return coinEmpty ;
} else {
return it - > second . coins ;
}
}
bool CCoinsViewCache : : HaveCoins ( const COutPoint & outpoint ) const {
CCoinsMap : : const_iterator it = FetchCoins ( outpoint ) ;
return ( it ! = cacheCoins . end ( ) & & ! it - > second . coins . IsPruned ( ) ) ;
}
bool CCoinsViewCache : : HaveCoinsInCache ( const COutPoint & outpoint ) const {
CCoinsMap : : const_iterator it = cacheCoins . find ( outpoint ) ;
return it ! = cacheCoins . end ( ) ;
}
uint256 CCoinsViewCache : : GetBestBlock ( ) const {
if ( hashBlock . IsNull ( ) )
hashBlock = base - > GetBestBlock ( ) ;
return hashBlock ;
}
void CCoinsViewCache : : SetBestBlock ( const uint256 & hashBlockIn ) {
hashBlock = hashBlockIn ;
}
bool CCoinsViewCache : : BatchWrite ( CCoinsMap & mapCoins , const uint256 & hashBlockIn ) {
for ( CCoinsMap : : iterator it = mapCoins . begin ( ) ; it ! = mapCoins . end ( ) ; ) {
if ( it - > second . flags & CCoinsCacheEntry : : DIRTY ) { // Ignore non-dirty entries (optimization).
CCoinsMap : : iterator itUs = cacheCoins . find ( it - > first ) ;
if ( itUs = = cacheCoins . end ( ) ) {
// The parent cache does not have an entry, while the child does
// We can ignore it if it's both FRESH and pruned in the child
if ( ! ( it - > second . flags & CCoinsCacheEntry : : FRESH & & it - > second . coins . IsPruned ( ) ) ) {
// Otherwise we will need to create it in the parent
// and move the data up and mark it as dirty
CCoinsCacheEntry & entry = cacheCoins [ it - > first ] ;
entry . coins = std : : move ( it - > second . coins ) ;
cachedCoinsUsage + = entry . coins . DynamicMemoryUsage ( ) ;
entry . flags = CCoinsCacheEntry : : DIRTY ;
// We can mark it FRESH in the parent if it was FRESH in the child
// Otherwise it might have just been flushed from the parent's cache
// and already exist in the grandparent
if ( it - > second . flags & CCoinsCacheEntry : : FRESH )
entry . flags | = CCoinsCacheEntry : : FRESH ;
}
} else {
// Assert that the child cache entry was not marked FRESH if the
// parent cache entry has unspent outputs. If this ever happens,
// it means the FRESH flag was misapplied and there is a logic
// error in the calling code.
if ( ( it - > second . flags & CCoinsCacheEntry : : FRESH ) & & ! itUs - > second . coins . IsPruned ( ) )
throw std : : logic_error ( " FRESH flag misapplied to cache entry for base transaction with spendable outputs " ) ;
// Found the entry in the parent cache
if ( ( itUs - > second . flags & CCoinsCacheEntry : : FRESH ) & & it - > second . coins . IsPruned ( ) ) {
// The grandparent does not have an entry, and the child is
// modified and being pruned. This means we can just delete
// it from the parent.
cachedCoinsUsage - = itUs - > second . coins . DynamicMemoryUsage ( ) ;
cacheCoins . erase ( itUs ) ;
} else {
// A normal modification.
cachedCoinsUsage - = itUs - > second . coins . DynamicMemoryUsage ( ) ;
itUs - > second . coins = std : : move ( it - > second . coins ) ;
cachedCoinsUsage + = itUs - > second . coins . DynamicMemoryUsage ( ) ;
itUs - > second . flags | = CCoinsCacheEntry : : DIRTY ;
// NOTE: It is possible the child has a FRESH flag here in
// the event the entry we found in the parent is pruned. But
// we must not copy that FRESH flag to the parent as that
// pruned state likely still needs to be communicated to the
// grandparent.
}
}
}
CCoinsMap : : iterator itOld = it + + ;
mapCoins . erase ( itOld ) ;
}
hashBlock = hashBlockIn ;
return true ;
}
bool CCoinsViewCache : : Flush ( ) {
bool fOk = base - > BatchWrite ( cacheCoins , hashBlock ) ;
cacheCoins . clear ( ) ;
cachedCoinsUsage = 0 ;
return fOk ;
}
void CCoinsViewCache : : Uncache ( const COutPoint & hash )
{
CCoinsMap : : iterator it = cacheCoins . find ( hash ) ;
if ( it ! = cacheCoins . end ( ) & & it - > second . flags = = 0 ) {
cachedCoinsUsage - = it - > second . coins . DynamicMemoryUsage ( ) ;
cacheCoins . erase ( it ) ;
}
}
unsigned int CCoinsViewCache : : GetCacheSize ( ) const {
return cacheCoins . size ( ) ;
}
const CTxOut & CCoinsViewCache : : GetOutputFor ( const CTxIn & input ) const
{
const Coin & coin = AccessCoin ( input . prevout ) ;
assert ( ! coin . IsPruned ( ) ) ;
return coin . out ;
}
CAmount CCoinsViewCache : : GetValueIn ( const CTransaction & tx ) const
{
if ( tx . IsCoinBase ( ) )
return 0 ;
CAmount nResult = 0 ;
for ( unsigned int i = 0 ; i < tx . vin . size ( ) ; i + + )
nResult + = GetOutputFor ( tx . vin [ i ] ) . nValue ;
return nResult ;
}
bool CCoinsViewCache : : HaveInputs ( const CTransaction & tx ) const
{
if ( ! tx . IsCoinBase ( ) ) {
for ( unsigned int i = 0 ; i < tx . vin . size ( ) ; i + + ) {
if ( ! HaveCoins ( tx . vin [ i ] . prevout ) ) {
return false ;
}
}
}
return true ;
}
static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_BASE_SIZE / : : GetSerializeSize ( CTxOut ( ) , SER_NETWORK , PROTOCOL_VERSION ) ; // TODO: merge with similar definition in undo.h.
const Coin & AccessByTxid ( const CCoinsViewCache & view , const uint256 & txid )
{
COutPoint iter ( txid , 0 ) ;
while ( iter . n < MAX_OUTPUTS_PER_BLOCK ) {
const Coin & alternate = view . AccessCoin ( iter ) ;
if ( ! alternate . IsPruned ( ) ) return alternate ;
+ + iter . n ;
}
return coinEmpty ;
}