Skip to content

Commit 928af61

Browse files
committed
allow send rpc take external inputs and solving data
1 parent e39b5a5 commit 928af61

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

src/wallet/rpcwallet.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4286,6 +4286,26 @@ static RPCHelpMan send()
42864286
},
42874287
{"replaceable", RPCArg::Type::BOOL, RPCArg::DefaultHint{"wallet default"}, "Marks this transaction as BIP125 replaceable.\n"
42884288
"Allows this transaction to be replaced by a transaction with higher fees"},
4289+
{"solving_data", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "Keys and scripts needed for producing a final transaction with a dummy signature.\n"
4290+
"Used for fee estimation during coin selection.",
4291+
{
4292+
{"pubkeys", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Public keys involved in this transaction.",
4293+
{
4294+
{"pubkey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A public key"},
4295+
},
4296+
},
4297+
{"scripts", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Scripts involved in this transaction.",
4298+
{
4299+
{"script", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "A script"},
4300+
},
4301+
},
4302+
{"descriptors", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Descriptors that provide solving data for this transaction.",
4303+
{
4304+
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A descriptor"},
4305+
},
4306+
}
4307+
}
4308+
},
42894309
},
42904310
"options"},
42914311
},

test/functional/wallet_send.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99

1010
from test_framework.authproxy import JSONRPCException
1111
from test_framework.descriptors import descsum_create
12+
from test_framework.key import ECKey
1213
from test_framework.test_framework import BitcoinTestFramework
1314
from test_framework.util import (
1415
assert_equal,
1516
assert_fee_amount,
1617
assert_greater_than,
1718
assert_raises_rpc_error,
1819
)
20+
from test_framework.wallet_util import bytes_to_wif
1921

2022
class WalletSendTest(BitcoinTestFramework):
2123
def set_test_params(self):
@@ -35,7 +37,7 @@ def test_send(self, from_wallet, to_wallet=None, amount=None, data=None,
3537
conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=None,
3638
inputs=None, add_inputs=None, include_unsafe=None, change_address=None, change_position=None, change_type=None,
3739
include_watching=None, locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None,
38-
expect_error=None):
40+
expect_error=None, solving_data=None):
3941
assert (amount is None) != (data is None)
4042

4143
from_balance_before = from_wallet.getbalances()["mine"]["trusted"]
@@ -94,6 +96,8 @@ def test_send(self, from_wallet, to_wallet=None, amount=None, data=None,
9496
options["replaceable"] = replaceable
9597
if subtract_fee_from_outputs is not None:
9698
options["subtract_fee_from_outputs"] = subtract_fee_from_outputs
99+
if solving_data is not None:
100+
options["solving_data"] = solving_data
97101

98102
if len(options.keys()) == 0:
99103
options = None
@@ -476,6 +480,46 @@ def run_test(self):
476480
res = self.test_send(from_wallet=w5, to_wallet=w0, amount=1, include_unsafe=True)
477481
assert res["complete"]
478482

483+
self.log.info("External outputs")
484+
eckey = ECKey()
485+
eckey.generate()
486+
privkey = bytes_to_wif(eckey.get_bytes())
487+
488+
self.nodes[1].createwallet("extsend")
489+
ext_wallet = self.nodes[1].get_wallet_rpc("extsend")
490+
self.nodes[1].createwallet("extfund")
491+
ext_fund = self.nodes[1].get_wallet_rpc("extfund")
492+
493+
# Make a weird but signable script. sh(pkh()) descriptor accomplishes this
494+
desc = descsum_create("sh(pkh({}))".format(privkey))
495+
if self.options.descriptors:
496+
res = ext_fund.importdescriptors([{"desc": desc, "timestamp": "now"}])
497+
else:
498+
res = ext_fund.importmulti([{"desc": desc, "timestamp": "now"}])
499+
assert res[0]["success"]
500+
addr = self.nodes[0].deriveaddresses(desc)[0]
501+
addr_info = ext_fund.getaddressinfo(addr)
502+
503+
self.nodes[0].sendtoaddress(addr, 10)
504+
self.nodes[0].sendtoaddress(ext_wallet.getnewaddress(), 10)
505+
self.nodes[0].generate(6)
506+
ext_utxo = ext_fund.listunspent(addresses=[addr])[0]
507+
508+
# An external input without solving data should result in an error
509+
self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, expect_error=(-4, "Insufficient funds"))
510+
511+
# But funding should work when the solving data is provided
512+
res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]})
513+
signed = ext_wallet.walletprocesspsbt(res["psbt"])
514+
signed = ext_fund.walletprocesspsbt(res["psbt"])
515+
assert signed["complete"]
516+
self.nodes[0].finalizepsbt(signed["psbt"])
517+
518+
res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"descriptors": [desc]})
519+
signed = ext_wallet.walletprocesspsbt(res["psbt"])
520+
signed = ext_fund.walletprocesspsbt(res["psbt"])
521+
assert signed["complete"]
522+
self.nodes[0].finalizepsbt(signed["psbt"])
479523

480524
if __name__ == '__main__':
481525
WalletSendTest().main()

0 commit comments

Comments
 (0)