@ -48,6 +48,8 @@ bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
*/
CFeeRate CWallet : : minTxFee = CFeeRate ( DEFAULT_TRANSACTION_MINFEE ) ;
const uint256 CMerkleTx : : ABANDON_HASH ( uint256S ( " 0000000000000000000000000000000000000000000000000000000000000001 " ) ) ;
/** @defgroup mapWallet
*
* @ {
@ -455,9 +457,12 @@ bool CWallet::IsSpent(const uint256& hash, unsigned int n) const
{
const uint256 & wtxid = it - > second ;
std : : map < uint256 , CWalletTx > : : const_iterator mit = mapWallet . find ( wtxid ) ;
if ( mit ! = mapWallet . end ( ) & & mit - > second . GetDepthInMainChain ( ) > = 0 )
if ( mit ! = mapWallet . end ( ) ) {
int depth = mit - > second . GetDepthInMainChain ( ) ;
if ( depth > 0 | | ( depth = = 0 & & ! mit - > second . isAbandoned ( ) ) )
return true ; // Spent
}
}
return false ;
}
@ -610,7 +615,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
BOOST_FOREACH ( const CTxIn & txin , wtx . vin ) {
if ( mapWallet . count ( txin . prevout . hash ) ) {
CWalletTx & prevtx = mapWallet [ txin . prevout . hash ] ;
if ( prevtx . nIndex = = - 1 & & ! prevtx . hash Block. IsNull ( ) ) {
if ( prevtx . nIndex = = - 1 & & ! prevtx . hash Unset ( ) ) {
MarkConflicted ( prevtx . hashBlock , wtx . GetHash ( ) ) ;
}
}
@ -631,7 +636,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
wtxOrdered . insert ( make_pair ( wtx . nOrderPos , TxPair ( & wtx , ( CAccountingEntry * ) 0 ) ) ) ;
wtx . nTimeSmart = wtx . nTimeReceived ;
if ( ! wtxIn . hash Block. IsNull ( ) )
if ( ! wtxIn . hash Unset ( ) )
{
if ( mapBlockIndex . count ( wtxIn . hashBlock ) )
{
@ -681,7 +686,13 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD
if ( ! fInsertedNew )
{
// Merge
if ( ! wtxIn . hashBlock . IsNull ( ) & & wtxIn . hashBlock ! = wtx . hashBlock )
if ( ! wtxIn . hashUnset ( ) & & wtxIn . hashBlock ! = wtx . hashBlock )
{
wtx . hashBlock = wtxIn . hashBlock ;
fUpdated = true ;
}
// If no longer abandoned, update
if ( wtxIn . hashBlock . IsNull ( ) & & wtx . isAbandoned ( ) )
{
wtx . hashBlock = wtxIn . hashBlock ;
fUpdated = true ;
@ -768,6 +779,63 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl
return false ;
}
bool CWallet : : AbandonTransaction ( const uint256 & hashTx )
{
LOCK2 ( cs_main , cs_wallet ) ;
// Do not flush the wallet here for performance reasons
CWalletDB walletdb ( strWalletFile , " r+ " , false ) ;
std : : set < uint256 > todo ;
std : : set < uint256 > done ;
// Can't mark abandoned if confirmed or in mempool
assert ( mapWallet . count ( hashTx ) ) ;
CWalletTx & origtx = mapWallet [ hashTx ] ;
if ( origtx . GetDepthInMainChain ( ) > 0 | | origtx . InMempool ( ) ) {
return false ;
}
todo . insert ( hashTx ) ;
while ( ! todo . empty ( ) ) {
uint256 now = * todo . begin ( ) ;
todo . erase ( now ) ;
done . insert ( now ) ;
assert ( mapWallet . count ( now ) ) ;
CWalletTx & wtx = mapWallet [ now ] ;
int currentconfirm = wtx . GetDepthInMainChain ( ) ;
// If the orig tx was not in block, none of its spends can be
assert ( currentconfirm < = 0 ) ;
// if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
if ( currentconfirm = = 0 & & ! wtx . isAbandoned ( ) ) {
// If the orig tx was not in block/mempool, none of its spends can be in mempool
assert ( ! wtx . InMempool ( ) ) ;
wtx . nIndex = - 1 ;
wtx . setAbandoned ( ) ;
wtx . MarkDirty ( ) ;
wtx . WriteToDisk ( & walletdb ) ;
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
TxSpends : : const_iterator iter = mapTxSpends . lower_bound ( COutPoint ( hashTx , 0 ) ) ;
while ( iter ! = mapTxSpends . end ( ) & & iter - > first . hash = = now ) {
if ( ! done . count ( iter - > second ) ) {
todo . insert ( iter - > second ) ;
}
iter + + ;
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed
BOOST_FOREACH ( const CTxIn & txin , wtx . vin )
{
if ( mapWallet . count ( txin . prevout . hash ) )
mapWallet [ txin . prevout . hash ] . MarkDirty ( ) ;
}
}
}
return true ;
}
void CWallet : : MarkConflicted ( const uint256 & hashBlock , const uint256 & hashTx )
{
LOCK2 ( cs_main , cs_wallet ) ;
@ -976,7 +1044,7 @@ int CWalletTx::GetRequestCount() const
if ( IsCoinBase ( ) )
{
// Generated block
if ( ! hash Block. IsNull ( ) )
if ( ! hash Unset ( ) )
{
map < uint256 , int > : : const_iterator mi = pwallet - > mapRequestCount . find ( hashBlock ) ;
if ( mi ! = pwallet - > mapRequestCount . end ( ) )
@ -992,7 +1060,7 @@ int CWalletTx::GetRequestCount() const
nRequests = ( * mi ) . second ;
// How about the block it's in?
if ( nRequests = = 0 & & ! hash Block. IsNull ( ) )
if ( nRequests = = 0 & & ! hash Unset ( ) )
{
map < uint256 , int > : : const_iterator mi = pwallet - > mapRequestCount . find ( hashBlock ) ;
if ( mi ! = pwallet - > mapRequestCount . end ( ) )
@ -1166,7 +1234,7 @@ void CWallet::ReacceptWalletTransactions()
int nDepth = wtx . GetDepthInMainChain ( ) ;
if ( ! wtx . IsCoinBase ( ) & & nDepth = = 0 ) {
if ( ! wtx . IsCoinBase ( ) & & ( nDepth = = 0 & & ! wtx . isAbandoned ( ) ) ) {
mapSorted . insert ( std : : make_pair ( wtx . nOrderPos , & wtx ) ) ;
}
}
@ -1186,7 +1254,7 @@ bool CWalletTx::RelayWalletTransaction()
assert ( pwallet - > GetBroadcastTransactions ( ) ) ;
if ( ! IsCoinBase ( ) )
{
if ( GetDepthInMainChain ( ) = = 0 ) {
if ( GetDepthInMainChain ( ) = = 0 & & ! isAbandoned ( ) ) {
LogPrintf ( " Relaying wtx %s \n " , GetHash ( ) . ToString ( ) ) ;
RelayTransaction ( ( CTransaction ) * this ) ;
return true ;
@ -2927,8 +2995,9 @@ int CMerkleTx::SetMerkleBranch(const CBlock& block)
int CMerkleTx : : GetDepthInMainChain ( const CBlockIndex * & pindexRet ) const
{
if ( hash Block. IsNull ( ) )
if ( hash Unset ( ) )
return 0 ;
AssertLockHeld ( cs_main ) ;
// Find the block it claims to be in
@ -2956,4 +3025,3 @@ bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee)
CValidationState state ;
return : : AcceptToMemoryPool ( mempool , state , * this , fLimitFree , NULL , false , fRejectAbsurdFee ) ;
}