mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -05:00
Merge bitcoin/bitcoin#28979: wallet, rpc: document and update sendall
behavior around unconfirmed inputs
71aae72e1f
test: test sendall does ancestor aware funding (ishaanam)36757941a0
wallet, rpc: implement ancestor aware funding for sendall (ishaanam)544131f3fb
rpc, test: test sendall spends unconfirmed change and unconfirmed inputs when specified (ishaanam) Pull request description: This PR: - Adds a functional test that `sendall` spends unconfirmed change - Adds a functional test that `sendall` spends regular unconfirmed inputs when specified by user - Adds ancestor aware funding to `sendall` by using `calculateCombinedBumpFee` and adjusting the effective value accordingly - Adds a functional test for ancestor aware funding in `sendall` ACKs for top commit: S3RK: ACK71aae72e1f
achow101: ACK71aae72e1f
furszy: ACK71aae72e1f
Tree-SHA512: acaeb7c65166ce53123a1d6cb5012197202246acc02ef9f37a28154cc93afdbd77c25e840ab79bdc7e0b88904014a43ab1ddea79d5337dc310ea210634ab61f0
This commit is contained in:
commit
e54c392356
2 changed files with 77 additions and 2 deletions
|
@ -1295,7 +1295,7 @@ RPCHelpMan sendall()
|
|||
{
|
||||
return RPCHelpMan{"sendall",
|
||||
"EXPERIMENTAL warning: this call may be changed in future releases.\n"
|
||||
"\nSpend the value of all (or specific) confirmed UTXOs in the wallet to one or more recipients.\n"
|
||||
"\nSpend the value of all (or specific) confirmed UTXOs and unconfirmed change in the wallet to one or more recipients.\n"
|
||||
"Unconfirmed inbound UTXOs and locked UTXOs will not be spent. Sendall will respect the avoid_reuse wallet flag.\n"
|
||||
"If your wallet contains many small inputs, either because it received tiny payments or as a result of accumulating change, consider using `send_max` to exclude inputs that are worth less than the fees needed to spend them.\n",
|
||||
{
|
||||
|
@ -1470,10 +1470,18 @@ RPCHelpMan sendall()
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<COutPoint> outpoints_spent;
|
||||
outpoints_spent.reserve(rawTx.vin.size());
|
||||
|
||||
for (const CTxIn& tx_in : rawTx.vin) {
|
||||
outpoints_spent.push_back(tx_in.prevout);
|
||||
}
|
||||
|
||||
// estimate final size of tx
|
||||
const TxSize tx_size{CalculateMaximumSignedTxSize(CTransaction(rawTx), pwallet.get())};
|
||||
const CAmount fee_from_size{fee_rate.GetFee(tx_size.vsize)};
|
||||
const CAmount effective_value{total_input_value - fee_from_size};
|
||||
const std::optional<CAmount> total_bump_fees{pwallet->chain().calculateCombinedBumpFee(outpoints_spent, fee_rate)};
|
||||
CAmount effective_value = total_input_value - fee_from_size - total_bump_fees.value_or(0);
|
||||
|
||||
if (fee_from_size > pwallet->m_default_max_tx_fee) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, TransactionErrorString(TransactionError::MAX_FEE_EXCEEDED).original);
|
||||
|
|
|
@ -379,6 +379,64 @@ class SendallTest(BitcoinTestFramework):
|
|||
assert_equal(len(self.wallet.listunspent()), 1)
|
||||
assert_equal(self.wallet.listunspent()[0]['confirmations'], 6)
|
||||
|
||||
@cleanup
|
||||
def sendall_spends_unconfirmed_change(self):
|
||||
self.log.info("Test that sendall spends unconfirmed change")
|
||||
self.add_utxos([17])
|
||||
self.wallet.sendtoaddress(self.remainder_target, 10)
|
||||
assert_greater_than(self.wallet.getbalances()["mine"]["trusted"], 6)
|
||||
self.test_sendall_success(sendall_args = [self.remainder_target])
|
||||
|
||||
assert_equal(self.wallet.getbalance(), 0)
|
||||
|
||||
@cleanup
|
||||
def sendall_spends_unconfirmed_inputs_if_specified(self):
|
||||
self.log.info("Test that sendall spends specified unconfirmed inputs")
|
||||
self.def_wallet.sendtoaddress(self.wallet.getnewaddress(), 17)
|
||||
self.wallet.syncwithvalidationinterfacequeue()
|
||||
assert_equal(self.wallet.getbalances()["mine"]["untrusted_pending"], 17)
|
||||
unspent = self.wallet.listunspent(minconf=0)[0]
|
||||
|
||||
self.wallet.sendall(recipients=[self.remainder_target], inputs=[unspent])
|
||||
assert_equal(self.wallet.getbalance(), 0)
|
||||
|
||||
@cleanup
|
||||
def sendall_does_ancestor_aware_funding(self):
|
||||
self.log.info("Test that sendall does ancestor aware funding for unconfirmed inputs")
|
||||
|
||||
# higher parent feerate
|
||||
self.def_wallet.sendtoaddress(address=self.wallet.getnewaddress(), amount=17, fee_rate=20)
|
||||
self.wallet.syncwithvalidationinterfacequeue()
|
||||
|
||||
assert_equal(self.wallet.getbalances()["mine"]["untrusted_pending"], 17)
|
||||
unspent = self.wallet.listunspent(minconf=0)[0]
|
||||
|
||||
parent_txid = unspent["txid"]
|
||||
assert_equal(self.wallet.gettransaction(parent_txid)["confirmations"], 0)
|
||||
|
||||
res_1 = self.wallet.sendall(recipients=[self.def_wallet.getnewaddress()], inputs=[unspent], fee_rate=20, add_to_wallet=False, lock_unspents=True)
|
||||
child_hex = res_1["hex"]
|
||||
|
||||
child_tx = self.wallet.decoderawtransaction(child_hex)
|
||||
higher_parent_feerate_amount = child_tx["vout"][0]["value"]
|
||||
|
||||
# lower parent feerate
|
||||
self.def_wallet.sendtoaddress(address=self.wallet.getnewaddress(), amount=17, fee_rate=10)
|
||||
self.wallet.syncwithvalidationinterfacequeue()
|
||||
assert_equal(self.wallet.getbalances()["mine"]["untrusted_pending"], 34)
|
||||
unspent = self.wallet.listunspent(minconf=0)[0]
|
||||
|
||||
parent_txid = unspent["txid"]
|
||||
assert_equal(self.wallet.gettransaction(parent_txid)["confirmations"], 0)
|
||||
|
||||
res_2 = self.wallet.sendall(recipients=[self.def_wallet.getnewaddress()], inputs=[unspent], fee_rate=20, add_to_wallet=False, lock_unspents=True)
|
||||
child_hex = res_2["hex"]
|
||||
|
||||
child_tx = self.wallet.decoderawtransaction(child_hex)
|
||||
lower_parent_feerate_amount = child_tx["vout"][0]["value"]
|
||||
|
||||
assert_greater_than(higher_parent_feerate_amount, lower_parent_feerate_amount)
|
||||
|
||||
# This tests needs to be the last one otherwise @cleanup will fail with "Transaction too large" error
|
||||
def sendall_fails_with_transaction_too_large(self):
|
||||
self.log.info("Test that sendall fails if resulting transaction is too large")
|
||||
|
@ -460,6 +518,15 @@ class SendallTest(BitcoinTestFramework):
|
|||
# Sendall only uses outputs with less than a given number of confirmation when using minconf
|
||||
self.sendall_with_maxconf()
|
||||
|
||||
# Sendall spends unconfirmed change
|
||||
self.sendall_spends_unconfirmed_change()
|
||||
|
||||
# Sendall spends unconfirmed inputs if they are specified
|
||||
self.sendall_spends_unconfirmed_inputs_if_specified()
|
||||
|
||||
# Sendall does ancestor aware funding when spending an unconfirmed UTXO
|
||||
self.sendall_does_ancestor_aware_funding()
|
||||
|
||||
# Sendall fails when many inputs result to too large transaction
|
||||
self.sendall_fails_with_transaction_too_large()
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue