mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 19:42:46 +01:00
merge bitcoin#19969: Send RPC bug fix and touch-ups
This commit is contained in:
parent
17e2168a4f
commit
76a49addd0
@ -9,5 +9,6 @@ RPC changes
|
||||
prevents adding more inputs if necessary and consequently the RPC fails.
|
||||
|
||||
- A new `send` RPC with similar syntax to `walletcreatefundedpsbt`, including
|
||||
support for coin selection and a custom fee rate. Using the new `send` method
|
||||
is encouraged: `sendmany` and `sendtoaddress` may be deprecated in a future release.
|
||||
support for coin selection and a custom fee rate. The `send` RPC is experimental
|
||||
and may change in subsequent releases. Using it is encouraged once it's no
|
||||
longer experimental: `sendmany` and `sendtoaddress` may be deprecated in a future release.
|
||||
|
@ -21,14 +21,16 @@
|
||||
|
||||
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime)
|
||||
{
|
||||
if (outputs_in.isNull())
|
||||
if (outputs_in.isNull()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
|
||||
}
|
||||
|
||||
UniValue inputs;
|
||||
if (inputs_in.isNull())
|
||||
if (inputs_in.isNull()) {
|
||||
inputs = UniValue::VARR;
|
||||
else
|
||||
} else {
|
||||
inputs = inputs_in.get_array();
|
||||
}
|
||||
|
||||
const bool outputs_is_obj = outputs_in.isObject();
|
||||
UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
|
||||
|
@ -3267,7 +3267,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
|
||||
{"lockUnspents", UniValueType(UniValue::VBOOL)},
|
||||
{"lock_unspents", UniValueType(UniValue::VBOOL)},
|
||||
{"locktime", UniValueType(UniValue::VNUM)},
|
||||
{"feeRate", UniValueType()}, // will be checked below,
|
||||
{"feeRate", UniValueType()}, // will be checked below
|
||||
{"psbt", UniValueType(UniValue::VBOOL)},
|
||||
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
|
||||
{"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
|
||||
@ -4006,9 +4006,10 @@ static UniValue listlabels(const JSONRPCRequest& request)
|
||||
static UniValue send(const JSONRPCRequest& request)
|
||||
{
|
||||
RPCHelpMan{"send",
|
||||
"\nEXPERIMENTAL warning: this call may be changed in future releases.\n"
|
||||
"\nSend a transaction.\n",
|
||||
{
|
||||
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
|
||||
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "A JSON array with outputs (key-value pairs), where none of the keys are duplicated.\n"
|
||||
"That is, each address can only appear once and there can only be one 'data' object.\n"
|
||||
"For convenience, a dictionary, which holds the key-value pairs directly, is also accepted.",
|
||||
{
|
||||
@ -4039,7 +4040,7 @@ static UniValue send(const JSONRPCRequest& request)
|
||||
{"include_watching", RPCArg::Type::BOOL, /* default */ "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"
|
||||
"e.g. with 'importpubkey' or 'importmulti' with the 'pubkeys' or 'desc' field."},
|
||||
{"inputs", RPCArg::Type::ARR, /* default */ "empty array", "Specify inputs instead of adding them automatically. A json array of json objects",
|
||||
{"inputs", RPCArg::Type::ARR, /* default */ "empty array", "Specify inputs instead of adding them automatically. A JSON array of JSON objects",
|
||||
{
|
||||
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
|
||||
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
|
||||
@ -4049,7 +4050,7 @@ static UniValue send(const JSONRPCRequest& request)
|
||||
{"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
|
||||
{"lock_unspents", RPCArg::Type::BOOL, /* default */ "false", "Lock selected unspent outputs"},
|
||||
{"psbt", RPCArg::Type::BOOL, /* default */ "automatic", "Always return a PSBT, implies add_to_wallet=false."},
|
||||
{"subtract_fee_from_outputs", RPCArg::Type::ARR, /* default */ "empty array", "A json array of integers.\n"
|
||||
{"subtract_fee_from_outputs", RPCArg::Type::ARR, /* default */ "empty array", "A JSON array of integers.\n"
|
||||
"The fee will be equally deducted from the amount of each specified output.\n"
|
||||
"Those recipients will receive less funds than you enter in their corresponding amount field.\n"
|
||||
"If no outputs are specified here, the sender pays the fee.",
|
||||
@ -4071,8 +4072,8 @@ static UniValue send(const JSONRPCRequest& request)
|
||||
},
|
||||
RPCExamples{""
|
||||
"\nSend with a fee rate of 1 " + CURRENCY_ATOM + "/B\n"
|
||||
+ HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 " + CURRENCY_ATOM + "/B\n" +
|
||||
"\nCreate a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n")
|
||||
+ HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 " + CURRENCY_ATOM + "/B\n") +
|
||||
"\nCreate a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n"
|
||||
+ HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.1}' 1 economical '{\"add_to_wallet\": false, \"inputs\": [{\"txid\":\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\", \"vout\":1}]}'")
|
||||
}
|
||||
}.Check(request);
|
||||
@ -4136,7 +4137,7 @@ static UniValue send(const JSONRPCRequest& request)
|
||||
// Make a blank psbt
|
||||
PartiallySignedTransaction psbtx(rawTx);
|
||||
|
||||
// Fill transaction with out data and sign
|
||||
// Fill transaction with our data and sign
|
||||
bool complete = true;
|
||||
const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_ALL, true, false);
|
||||
if (err != TransactionError::OK) {
|
||||
@ -4148,13 +4149,11 @@ static UniValue send(const JSONRPCRequest& request)
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
|
||||
// Serialize the PSBT
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << psbtx;
|
||||
const std::string result_str = EncodeBase64(ssTx.str());
|
||||
|
||||
if (psbt_opt_in || !complete || !add_to_wallet) {
|
||||
result.pushKV("psbt", result_str);
|
||||
// Serialize the PSBT
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << psbtx;
|
||||
result.pushKV("psbt", EncodeBase64(ssTx.str()));
|
||||
}
|
||||
|
||||
if (complete) {
|
||||
|
@ -29,9 +29,9 @@ class WalletSendTest(BitcoinTestFramework):
|
||||
|
||||
def test_send(self, from_wallet, to_wallet=None, amount=None, data=None,
|
||||
arg_conf_target=None, arg_estimate_mode=None,
|
||||
conf_target=None, estimate_mode=None, add_to_wallet=None,psbt=None,
|
||||
inputs=None,add_inputs=None,change_address=None,change_position=None,
|
||||
include_watching=None,locktime=None,lock_unspents=None,subtract_fee_from_outputs=None,
|
||||
conf_target=None, estimate_mode=None, add_to_wallet=None, psbt=None,
|
||||
inputs=None, add_inputs=None, change_address=None, change_position=None,
|
||||
include_watching=None, locktime=None, lock_unspents=None, subtract_fee_from_outputs=None,
|
||||
expect_error=None):
|
||||
assert (amount is None) != (data is None)
|
||||
|
||||
@ -86,13 +86,13 @@ class WalletSendTest(BitcoinTestFramework):
|
||||
res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
|
||||
else:
|
||||
try:
|
||||
assert_raises_rpc_error(expect_error[0],expect_error[1],from_wallet.send,
|
||||
outputs=outputs,conf_target=arg_conf_target,estimate_mode=arg_estimate_mode,options=options)
|
||||
assert_raises_rpc_error(expect_error[0], expect_error[1], from_wallet.send,
|
||||
outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
|
||||
except AssertionError:
|
||||
# Provide debug info if the test fails
|
||||
self.log.error("Unexpected successful result:")
|
||||
self.log.error(options)
|
||||
res = from_wallet.send(outputs=outputs,conf_target=arg_conf_target,estimate_mode=arg_estimate_mode,options=options)
|
||||
res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, options=options)
|
||||
self.log.error(res)
|
||||
if "txid" in res and add_to_wallet:
|
||||
self.log.error("Transaction details:")
|
||||
@ -124,7 +124,7 @@ class WalletSendTest(BitcoinTestFramework):
|
||||
tx = from_wallet.gettransaction(res["txid"])
|
||||
assert tx
|
||||
# Ensure transaction exists in the mempool:
|
||||
tx = from_wallet.getrawtransaction(res["txid"],True)
|
||||
tx = from_wallet.getrawtransaction(res["txid"], True)
|
||||
assert tx
|
||||
if amount:
|
||||
if subtract_fee_from_outputs:
|
||||
@ -157,7 +157,7 @@ class WalletSendTest(BitcoinTestFramework):
|
||||
self.nodes[1].createwallet(wallet_name="w2")
|
||||
w2 = self.nodes[1].get_wallet_rpc("w2")
|
||||
# w3 is a watch-only wallet, based on w2
|
||||
self.nodes[1].createwallet(wallet_name="w3",disable_private_keys=True)
|
||||
self.nodes[1].createwallet(wallet_name="w3", disable_private_keys=True)
|
||||
w3 = self.nodes[1].get_wallet_rpc("w3")
|
||||
for _ in range(3):
|
||||
a2_receive = w2.getnewaddress()
|
||||
@ -181,7 +181,7 @@ class WalletSendTest(BitcoinTestFramework):
|
||||
self.sync_blocks()
|
||||
|
||||
# w4 has private keys enabled, but only contains watch-only keys (from w2)
|
||||
self.nodes[1].createwallet(wallet_name="w4",disable_private_keys=False)
|
||||
self.nodes[1].createwallet(wallet_name="w4", disable_private_keys=False)
|
||||
w4 = self.nodes[1].get_wallet_rpc("w4")
|
||||
for _ in range(3):
|
||||
a2_receive = w2.getnewaddress()
|
||||
@ -312,7 +312,8 @@ class WalletSendTest(BitcoinTestFramework):
|
||||
locked_coins = w0.listlockunspent()
|
||||
assert_equal(len(locked_coins), 1)
|
||||
# Locked coins are automatically unlocked when manually selected
|
||||
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1],add_to_wallet=False)
|
||||
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, inputs=[utxo1], add_to_wallet=False)
|
||||
assert res["complete"]
|
||||
|
||||
self.log.info("Subtract fee from output")
|
||||
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, subtract_fee_from_outputs=[0])
|
||||
|
Loading…
Reference in New Issue
Block a user