Skip to content

Commit 9addbd7

Browse files
committed
wallet: Automatically abandon orphaned coinbases and their children
1 parent 48174c0 commit 9addbd7

File tree

3 files changed

+34
-12
lines changed

3 files changed

+34
-12
lines changed

src/wallet/transaction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ class CWalletTx
293293

294294
bool isAbandoned() const { return state<TxStateInactive>() && state<TxStateInactive>()->abandoned; }
295295
bool isConflicted() const { return state<TxStateConflicted>(); }
296+
bool isInactive() const { return state<TxStateInactive>(); }
296297
bool isUnconfirmed() const { return !isAbandoned() && !isConflicted() && !isConfirmed(); }
297298
bool isConfirmed() const { return state<TxStateConfirmed>(); }
298299
const uint256& GetHash() const { return tx->GetHash(); }

src/wallet/wallet.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,33 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
10671067
}
10681068
}
10691069

1070+
// Mark inactive coinbase transactions and their descendants as abandoned
1071+
if (wtx.IsCoinBase() && wtx.isInactive()) {
1072+
std::vector<CWalletTx*> txs{&wtx};
1073+
1074+
TxStateInactive inactive_state = TxStateInactive{/*abandoned=*/true};
1075+
1076+
while (!txs.empty()) {
1077+
CWalletTx* desc_tx = txs.back();
1078+
txs.pop_back();
1079+
desc_tx->m_state = inactive_state;
1080+
// Break caches since we have changed the state
1081+
desc_tx->MarkDirty();
1082+
batch.WriteTx(*desc_tx);
1083+
MarkInputsDirty(desc_tx->tx);
1084+
for (unsigned int i = 0; i < desc_tx->tx->vout.size(); ++i) {
1085+
COutPoint outpoint(desc_tx->GetHash(), i);
1086+
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(outpoint);
1087+
for (TxSpends::const_iterator it = range.first; it != range.second; ++it) {
1088+
const auto wit = mapWallet.find(it->second);
1089+
if (wit != mapWallet.end()) {
1090+
txs.push_back(&wit->second);
1091+
}
1092+
}
1093+
}
1094+
}
1095+
}
1096+
10701097
//// debug print
10711098
WalletLogPrintf("AddToWallet %s %s%s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
10721099

@@ -1276,7 +1303,11 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
12761303
wtx.MarkDirty();
12771304
batch.WriteTx(wtx);
12781305
NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
1279-
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
1306+
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too.
1307+
// States are not permanent, so these transactions can become unabandoned if they are re-added to the
1308+
// mempool, or confirmed in a block, or conflicted.
1309+
// Note: If the reorged coinbase is re-added to the main chain, the descendants that have not had their
1310+
// states change will remain abandoned and will require manual broadcast if the user wants them.
12801311
for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
12811312
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
12821313
for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {

test/functional/wallet_orphanedreward.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,7 @@ def run_test(self):
3737
# from the wallet can still be spent.
3838
self.nodes[0].invalidateblock(blk)
3939
self.generate(self.nodes[0], 152)
40-
# Without the following abandontransaction call, the coins are
41-
# not considered available yet.
42-
assert_equal(self.nodes[1].getbalances()["mine"], {
43-
"trusted": 0,
44-
"untrusted_pending": 0,
45-
"immature": 0,
46-
})
47-
# The following abandontransaction is necessary to make the later
48-
# lines succeed, and probably should not be needed; see
49-
# https://github.com/bitcoin/bitcoin/issues/14148.
50-
self.nodes[1].abandontransaction(txid)
40+
# We expect the descendants of orphaned rewards to no longer be considered
5141
assert_equal(self.nodes[1].getbalances()["mine"], {
5242
"trusted": 10,
5343
"untrusted_pending": 0,

0 commit comments

Comments
 (0)