Merge #12282: wallet: Disallow abandon of conflicted txes

fa795cf wallet: Disallow abandon of conflicted txes (MarcoFalke)

Pull request description:

  Abandon transactions that are already conflicted is a noop, so don't try and return false/throw instead.

Tree-SHA512: fd2af4149bd2323f7f31fe18685c763790b8589319b4e467b464ab456d5e8971501ab16d124e57a22693666b06ae433ac3e59f0fd6dfbd2be2c6cae8be5bcbd8
This commit is contained in:
Wladimir J. van der Laan 2018-02-08 15:57:08 +01:00
commit 663911ed58
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
4 changed files with 19 additions and 9 deletions

View File

@ -2184,14 +2184,14 @@ UniValue abandontransaction(const JSONRPCRequest& request)
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1)
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"abandontransaction \"txid\"\n"
"\nMark in-wallet transaction <txid> as abandoned\n"
"This will mark this transaction and all its in-wallet descendants as abandoned which will allow\n"
"for their inputs to be respent. It can be used to replace \"stuck\" or evicted transactions.\n"
"It only works on transactions which are not included in a block and are not currently in the mempool.\n"
"It has no effect on transactions which are already conflicted or abandoned.\n"
"It has no effect on transactions which are already abandoned.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"\nResult:\n"
@ -2199,6 +2199,7 @@ UniValue abandontransaction(const JSONRPCRequest& request)
+ HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
+ HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
);
}
ObserveSafeMode();

View File

@ -1078,7 +1078,7 @@ bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
{
LOCK2(cs_main, cs_wallet);
const CWalletTx* wtx = GetWalletTx(hashTx);
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() <= 0 && !wtx->InMempool();
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() == 0 && !wtx->InMempool();
}
bool CWallet::AbandonTransaction(const uint256& hashTx)
@ -1094,7 +1094,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
auto it = mapWallet.find(hashTx);
assert(it != mapWallet.end());
CWalletTx& origtx = it->second;
if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) {
if (origtx.GetDepthInMainChain() != 0 || origtx.InMempool()) {
return false;
}

View File

@ -8,11 +8,12 @@
descendants as abandoned which allows their inputs to be respent. It can be
used to replace "stuck" or evicted transactions. It only works on transactions
which are not included in a block and are not currently in the mempool. It has
no effect on transactions which are already conflicted or abandoned.
no effect on transactions which are already abandoned.
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
class AbandonConflictTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
@ -28,6 +29,11 @@ class AbandonConflictTest(BitcoinTestFramework):
sync_mempools(self.nodes)
self.nodes[1].generate(1)
# Can not abandon non-wallet transaction
assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', lambda: self.nodes[0].abandontransaction(txid='ff' * 32))
# Can not abandon confirmed transaction
assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: self.nodes[0].abandontransaction(txid=txA))
sync_blocks(self.nodes)
newbalance = self.nodes[0].getbalance()
assert(balance - newbalance < Decimal("0.001")) #no more than fees lost

View File

@ -231,13 +231,16 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address):
assert_equal([t for t in rbf_node.listunspent(minconf=0, include_unsafe=False) if t["txid"] == bumpid], [])
# submit a block with the rbf tx to clear the bump tx out of the mempool,
# then call abandon to make sure the wallet doesn't attempt to resubmit the
# bump tx, then invalidate the block so the rbf tx will be put back in the
# mempool. this makes it possible to check whether the rbf tx outputs are
# then invalidate the block so the rbf tx will be put back in the mempool.
# This makes it possible to check whether the rbf tx outputs are
# spendable before the rbf tx is confirmed.
block = submit_block_with_tx(rbf_node, rbftx)
rbf_node.abandontransaction(bumpid)
# Can not abandon conflicted tx
assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: rbf_node.abandontransaction(txid=bumpid))
rbf_node.invalidateblock(block.hash)
# Call abandon to make sure the wallet doesn't attempt to resubmit
# the bump tx and hope the wallet does not rebroadcast before we call.
rbf_node.abandontransaction(bumpid)
assert bumpid not in rbf_node.getrawmempool()
assert rbfid in rbf_node.getrawmempool()