Skip to content

Commit fa795cf

Browse files
author
MarcoFalke
committed
wallet: Disallow abandon of conflicted txes
1 parent 598a9c4 commit fa795cf

File tree

4 files changed

+19
-9
lines changed

4 files changed

+19
-9
lines changed

src/wallet/rpcwallet.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2190,21 +2190,22 @@ UniValue abandontransaction(const JSONRPCRequest& request)
21902190
return NullUniValue;
21912191
}
21922192

2193-
if (request.fHelp || request.params.size() != 1)
2193+
if (request.fHelp || request.params.size() != 1) {
21942194
throw std::runtime_error(
21952195
"abandontransaction \"txid\"\n"
21962196
"\nMark in-wallet transaction <txid> as abandoned\n"
21972197
"This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
21982198
"for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
21992199
"It only works on transactions which are not included in a block and are not currently in the mempool.\n"
2200-
"It has no effect on transactions which are already conflicted or abandoned.\n"
2200+
"It has no effect on transactions which are already abandoned.\n"
22012201
"\nArguments:\n"
22022202
"1. \"txid\" (string, required) The transaction id\n"
22032203
"\nResult:\n"
22042204
"\nExamples:\n"
22052205
+ HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
22062206
+ HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
22072207
);
2208+
}
22082209

22092210
ObserveSafeMode();
22102211

src/wallet/wallet.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,7 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
10831083
{
10841084
LOCK2(cs_main, cs_wallet);
10851085
const CWalletTx* wtx = GetWalletTx(hashTx);
1086-
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() <= 0 && !wtx->InMempool();
1086+
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
10871087
}
10881088

10891089
bool CWallet::AbandonTransaction(const uint256& hashTx)
@@ -1099,7 +1099,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
10991099
auto it = mapWallet.find(hashTx);
11001100
assert(it != mapWallet.end());
11011101
CWalletTx& origtx = it->second;
1102-
if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) {
1102+
if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) {
11031103
return false;
11041104
}
11051105

test/functional/wallet_abandonconflict.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
descendants as abandoned which allows their inputs to be respent. It can be
99
used to replace "stuck" or evicted transactions. It only works on transactions
1010
which are not included in a block and are not currently in the mempool. It has
11-
no effect on transactions which are already conflicted or abandoned.
11+
no effect on transactions which are already abandoned.
1212
"""
1313
from test_framework.test_framework import BitcoinTestFramework
1414
from test_framework.util import *
1515

16+
1617
class AbandonConflictTest(BitcoinTestFramework):
1718
def set_test_params(self):
1819
self.num_nodes = 2
@@ -28,6 +29,11 @@ def run_test(self):
2829
sync_mempools(self.nodes)
2930
self.nodes[1].generate(1)
3031

32+
# Can not abandon non-wallet transaction
33+
assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', lambda: self.nodes[0].abandontransaction(txid='ff' * 32))
34+
# Can not abandon confirmed transaction
35+
assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: self.nodes[0].abandontransaction(txid=txA))
36+
3137
sync_blocks(self.nodes)
3238
newbalance = self.nodes[0].getbalance()
3339
assert(balance - newbalance < Decimal("0.001")) #no more than fees lost

test/functional/wallet_bumpfee.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,13 +231,16 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
231231
assert_equal([t for t in rbf_node.listunspent(minconf=0, include_unsafe=False) if t["txid"] == bumpid], [])
232232

233233
# submit a block with the rbf tx to clear the bump tx out of the mempool,
234-
# then call abandon to make sure the wallet doesn't attempt to resubmit the
235-
# bump tx, then invalidate the block so the rbf tx will be put back in the
236-
# mempool. this makes it possible to check whether the rbf tx outputs are
234+
# then invalidate the block so the rbf tx will be put back in the mempool.
235+
# This makes it possible to check whether the rbf tx outputs are
237236
# spendable before the rbf tx is confirmed.
238237
block = submit_block_with_tx(rbf_node, rbftx)
239-
rbf_node.abandontransaction(bumpid)
238+
# Can not abandon conflicted tx
239+
assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: rbf_node.abandontransaction(txid=bumpid))
240240
rbf_node.invalidateblock(block.hash)
241+
# Call abandon to make sure the wallet doesn't attempt to resubmit
242+
# the bump tx and hope the wallet does not rebroadcast before we call.
243+
rbf_node.abandontransaction(bumpid)
241244
assert bumpid not in rbf_node.getrawmempool()
242245
assert rbfid in rbf_node.getrawmempool()
243246

0 commit comments

Comments
 (0)