wallet: Disallow abandon of conflicted txes
This commit is contained in:
parent
598a9c4e4d
commit
fa795cf9c5
4 changed files with 19 additions and 9 deletions
|
@ -2190,14 +2190,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"
|
||||
|
@ -2205,6 +2205,7 @@ UniValue abandontransaction(const JSONRPCRequest& request)
|
|||
+ HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
|
||||
+ HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
|
||||
);
|
||||
}
|
||||
|
||||
ObserveSafeMode();
|
||||
|
||||
|
|
|
@ -1083,7 +1083,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)
|
||||
|
@ -1099,7 +1099,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
Loading…
Reference in a new issue