mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-05 14:06:27 -05:00
rpc: add minconf and maxconf options to sendall
This commit is contained in:
parent
a07a413466
commit
cfe5aebc79
3 changed files with 91 additions and 1 deletions
|
@ -8,3 +8,4 @@ added to the following RPCs:
|
||||||
- `fundrawtransaction`
|
- `fundrawtransaction`
|
||||||
- `send`
|
- `send`
|
||||||
- `walletcreatefundedpsbt`
|
- `walletcreatefundedpsbt`
|
||||||
|
- `sendall`
|
||||||
|
|
|
@ -1292,7 +1292,7 @@ RPCHelpMan sendall()
|
||||||
{"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch-only.\n"
|
{"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch-only.\n"
|
||||||
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
|
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
|
||||||
"e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
|
"e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
|
||||||
{"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Use exactly the specified inputs to build the transaction. Specifying inputs is incompatible with send_max.",
|
{"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Use exactly the specified inputs to build the transaction. Specifying inputs is incompatible with the send_max, minconf, and maxconf options.",
|
||||||
{
|
{
|
||||||
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
|
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
|
||||||
{
|
{
|
||||||
|
@ -1307,6 +1307,8 @@ RPCHelpMan sendall()
|
||||||
{"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
{"lock_unspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
||||||
{"psbt", RPCArg::Type::BOOL, RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
|
{"psbt", RPCArg::Type::BOOL, RPCArg::DefaultHint{"automatic"}, "Always return a PSBT, implies add_to_wallet=false."},
|
||||||
{"send_max", RPCArg::Type::BOOL, RPCArg::Default{false}, "When true, only use UTXOs that can pay for their own fees to maximize the output amount. When 'false' (default), no UTXO is left behind. send_max is incompatible with providing specific inputs."},
|
{"send_max", RPCArg::Type::BOOL, RPCArg::Default{false}, "When true, only use UTXOs that can pay for their own fees to maximize the output amount. When 'false' (default), no UTXO is left behind. send_max is incompatible with providing specific inputs."},
|
||||||
|
{"minconf", RPCArg::Type::NUM, RPCArg::Default{0}, "Require inputs with at least this many confirmations."},
|
||||||
|
{"maxconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Require inputs with at most this many confirmations."},
|
||||||
},
|
},
|
||||||
FundTxDoc()
|
FundTxDoc()
|
||||||
),
|
),
|
||||||
|
@ -1381,6 +1383,23 @@ RPCHelpMan sendall()
|
||||||
|
|
||||||
coin_control.fAllowWatchOnly = ParseIncludeWatchonly(options["include_watching"], *pwallet);
|
coin_control.fAllowWatchOnly = ParseIncludeWatchonly(options["include_watching"], *pwallet);
|
||||||
|
|
||||||
|
if (options.exists("minconf")) {
|
||||||
|
if (options["minconf"].getInt<int>() < 0)
|
||||||
|
{
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid minconf (minconf cannot be negative): %s", options["minconf"].getInt<int>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
coin_control.m_min_depth = options["minconf"].getInt<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.exists("maxconf")) {
|
||||||
|
coin_control.m_max_depth = options["maxconf"].getInt<int>();
|
||||||
|
|
||||||
|
if (coin_control.m_max_depth < coin_control.m_min_depth) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("maxconf can't be lower than minconf: %d < %d", coin_control.m_max_depth, coin_control.m_min_depth));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const bool rbf{options.exists("replaceable") ? options["replaceable"].get_bool() : pwallet->m_signal_rbf};
|
const bool rbf{options.exists("replaceable") ? options["replaceable"].get_bool() : pwallet->m_signal_rbf};
|
||||||
|
|
||||||
FeeCalculation fee_calc_out;
|
FeeCalculation fee_calc_out;
|
||||||
|
@ -1402,6 +1421,8 @@ RPCHelpMan sendall()
|
||||||
bool send_max{options.exists("send_max") ? options["send_max"].get_bool() : false};
|
bool send_max{options.exists("send_max") ? options["send_max"].get_bool() : false};
|
||||||
if (options.exists("inputs") && options.exists("send_max")) {
|
if (options.exists("inputs") && options.exists("send_max")) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot combine send_max with specific inputs.");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot combine send_max with specific inputs.");
|
||||||
|
} else if (options.exists("inputs") && (options.exists("minconf") || options.exists("maxconf"))) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot combine minconf or maxconf with specific inputs.");
|
||||||
} else if (options.exists("inputs")) {
|
} else if (options.exists("inputs")) {
|
||||||
for (const CTxIn& input : rawTx.vin) {
|
for (const CTxIn& input : rawTx.vin) {
|
||||||
if (pwallet->IsSpent(input.prevout)) {
|
if (pwallet->IsSpent(input.prevout)) {
|
||||||
|
|
|
@ -317,6 +317,68 @@ class SendallTest(BitcoinTestFramework):
|
||||||
assert_equal(decoded["tx"]["vin"][0]["vout"], utxo["vout"])
|
assert_equal(decoded["tx"]["vin"][0]["vout"], utxo["vout"])
|
||||||
assert_equal(decoded["tx"]["vout"][0]["scriptPubKey"]["address"], self.remainder_target)
|
assert_equal(decoded["tx"]["vout"][0]["scriptPubKey"]["address"], self.remainder_target)
|
||||||
|
|
||||||
|
@cleanup
|
||||||
|
def sendall_with_minconf(self):
|
||||||
|
# utxo of 17 bicoin has 6 confirmations, utxo of 4 has 3
|
||||||
|
self.add_utxos([17])
|
||||||
|
self.generate(self.nodes[0], 2)
|
||||||
|
self.add_utxos([4])
|
||||||
|
self.generate(self.nodes[0], 2)
|
||||||
|
|
||||||
|
self.log.info("Test sendall fails because minconf is negative")
|
||||||
|
|
||||||
|
assert_raises_rpc_error(-8,
|
||||||
|
"Invalid minconf (minconf cannot be negative): -2",
|
||||||
|
self.wallet.sendall,
|
||||||
|
recipients=[self.remainder_target],
|
||||||
|
options={"minconf": -2})
|
||||||
|
self.log.info("Test sendall fails because minconf is used while specific inputs are provided")
|
||||||
|
|
||||||
|
utxo = self.wallet.listunspent()[0]
|
||||||
|
assert_raises_rpc_error(-8,
|
||||||
|
"Cannot combine minconf or maxconf with specific inputs.",
|
||||||
|
self.wallet.sendall,
|
||||||
|
recipients=[self.remainder_target],
|
||||||
|
options={"inputs": [utxo], "minconf": 2})
|
||||||
|
|
||||||
|
self.log.info("Test sendall fails because there are no utxos with enough confirmations specified by minconf")
|
||||||
|
|
||||||
|
assert_raises_rpc_error(-6,
|
||||||
|
"Total value of UTXO pool too low to pay for transaction. Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.",
|
||||||
|
self.wallet.sendall,
|
||||||
|
recipients=[self.remainder_target],
|
||||||
|
options={"minconf": 7})
|
||||||
|
|
||||||
|
self.log.info("Test sendall only spends utxos with a specified number of confirmations when minconf is used")
|
||||||
|
self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"minconf": 6})
|
||||||
|
|
||||||
|
assert_equal(len(self.wallet.listunspent()), 1)
|
||||||
|
assert_equal(self.wallet.listunspent()[0]['confirmations'], 3)
|
||||||
|
|
||||||
|
# decrease minconf and show the remaining utxo is picked up
|
||||||
|
self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"minconf": 3})
|
||||||
|
assert_equal(self.wallet.getbalance(), 0)
|
||||||
|
|
||||||
|
@cleanup
|
||||||
|
def sendall_with_maxconf(self):
|
||||||
|
# utxo of 17 bicoin has 6 confirmations, utxo of 4 has 3
|
||||||
|
self.add_utxos([17])
|
||||||
|
self.generate(self.nodes[0], 2)
|
||||||
|
self.add_utxos([4])
|
||||||
|
self.generate(self.nodes[0], 2)
|
||||||
|
|
||||||
|
self.log.info("Test sendall fails because there are no utxos with enough confirmations specified by maxconf")
|
||||||
|
assert_raises_rpc_error(-6,
|
||||||
|
"Total value of UTXO pool too low to pay for transaction. Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.",
|
||||||
|
self.wallet.sendall,
|
||||||
|
recipients=[self.remainder_target],
|
||||||
|
options={"maxconf": 1})
|
||||||
|
|
||||||
|
self.log.info("Test sendall only spends utxos with a specified number of confirmations when maxconf is used")
|
||||||
|
self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"maxconf":4})
|
||||||
|
assert_equal(len(self.wallet.listunspent()), 1)
|
||||||
|
assert_equal(self.wallet.listunspent()[0]['confirmations'], 6)
|
||||||
|
|
||||||
# This tests needs to be the last one otherwise @cleanup will fail with "Transaction too large" error
|
# 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):
|
def sendall_fails_with_transaction_too_large(self):
|
||||||
self.log.info("Test that sendall fails if resulting transaction is too large")
|
self.log.info("Test that sendall fails if resulting transaction is too large")
|
||||||
|
@ -392,6 +454,12 @@ class SendallTest(BitcoinTestFramework):
|
||||||
# Sendall succeeds with watchonly wallets spending specific UTXOs
|
# Sendall succeeds with watchonly wallets spending specific UTXOs
|
||||||
self.sendall_watchonly_specific_inputs()
|
self.sendall_watchonly_specific_inputs()
|
||||||
|
|
||||||
|
# Sendall only uses outputs with at least a give number of confirmations when using minconf
|
||||||
|
self.sendall_with_minconf()
|
||||||
|
|
||||||
|
# Sendall only uses outputs with less than a given number of confirmation when using minconf
|
||||||
|
self.sendall_with_maxconf()
|
||||||
|
|
||||||
# Sendall fails when many inputs result to too large transaction
|
# Sendall fails when many inputs result to too large transaction
|
||||||
self.sendall_fails_with_transaction_too_large()
|
self.sendall_fails_with_transaction_too_large()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue