aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
authorW. J. van der Laan <laanwj@protonmail.com>2021-05-10 15:55:22 +0200
committerW. J. van der Laan <laanwj@protonmail.com>2021-05-10 16:05:23 +0200
commit32692d26813ba099c1451ae5941d38923bec761c (patch)
tree893b98d913055c7be76c0422b05007c18b649346 /test/functional
parent8bed1706eac45a90a016471275e4857a22984289 (diff)
parent11d6459b6e101f05f36e13799c400bef82d2fc21 (diff)
downloadbitcoin-32692d26813ba099c1451ae5941d38923bec761c.tar.xz
Merge bitcoin/bitcoin#21359: rpc: include_unsafe option for fundrawtransaction
11d6459b6e101f05f36e13799c400bef82d2fc21 rpc: include_unsafe option for fundrawtransaction (t-bast) Pull request description: Allow RPC users to opt-in to unsafe inputs when funding a raw transaction. Applications that need to manage a complex RBF flow (such as lightning nodes using anchor outputs) are very limited if they can only use safe inputs. I also added this option to `send` and `walletcreatefundedpsbt` who internally delegate to `fundrawtransaction`. Fixes #21299 ACKs for top commit: laanwj: Code review ACK 11d6459b6e101f05f36e13799c400bef82d2fc21 Tree-SHA512: 5e542a4febcfd6f41cf784678ff02ec9282eae2082c274983f72c5ea87b7ebbe1bd5fdc6a020d7a9d5996157754eb4966b8aeb6c1ceebf0b1519f735579b8bac
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py35
-rwxr-xr-xtest/functional/rpc_psbt.py8
-rwxr-xr-xtest/functional/wallet_send.py27
3 files changed, 65 insertions, 5 deletions
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index 0cce5493fc..4b07a32c54 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -98,6 +98,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.test_option_subtract_fee_from_outputs()
self.test_subtract_fee_with_presets()
self.test_transaction_too_large()
+ self.test_include_unsafe()
def test_change_position(self):
"""Ensure setting changePosition in fundraw with an exact match is handled properly."""
@@ -934,6 +935,40 @@ class RawTransactionsTest(BitcoinTestFramework):
self.nodes[0].generate(10)
assert_raises_rpc_error(-4, "Transaction too large", recipient.fundrawtransaction, rawtx)
+ def test_include_unsafe(self):
+ self.log.info("Test fundrawtxn with unsafe inputs")
+
+ self.nodes[0].createwallet("unsafe")
+ wallet = self.nodes[0].get_wallet_rpc("unsafe")
+
+ # We receive unconfirmed funds from external keys (unsafe outputs).
+ addr = wallet.getnewaddress()
+ txid1 = self.nodes[2].sendtoaddress(addr, 6)
+ txid2 = self.nodes[2].sendtoaddress(addr, 4)
+ self.sync_all()
+ vout1 = find_vout_for_address(wallet, txid1, addr)
+ vout2 = find_vout_for_address(wallet, txid2, addr)
+
+ # Unsafe inputs are ignored by default.
+ rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 5}])
+ assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, rawtx)
+
+ # But we can opt-in to use them for funding.
+ fundedtx = wallet.fundrawtransaction(rawtx, {"include_unsafe": True})
+ tx_dec = wallet.decoderawtransaction(fundedtx['hex'])
+ assert any([txin['txid'] == txid1 and txin['vout'] == vout1 for txin in tx_dec['vin']])
+ signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
+ wallet.sendrawtransaction(signedtx['hex'])
+
+ # And we can also use them once they're confirmed.
+ self.nodes[0].generate(1)
+ rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 3}])
+ fundedtx = wallet.fundrawtransaction(rawtx, {"include_unsafe": True})
+ tx_dec = wallet.decoderawtransaction(fundedtx['hex'])
+ assert any([txin['txid'] == txid2 and txin['vout'] == vout2 for txin in tx_dec['vin']])
+ signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
+ wallet.sendrawtransaction(signedtx['hex'])
+
if __name__ == '__main__':
RawTransactionsTest().main()
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index c6e96e4a92..cf4d8a134c 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -402,6 +402,14 @@ class PSBTTest(BitcoinTestFramework):
# We don't care about the decode result, but decoding must succeed.
self.nodes[0].decodepsbt(double_processed_psbt["psbt"])
+ # Make sure unsafe inputs are included if specified
+ self.nodes[2].createwallet(wallet_name="unsafe")
+ wunsafe = self.nodes[2].get_wallet_rpc("unsafe")
+ self.nodes[0].sendtoaddress(wunsafe.getnewaddress(), 2)
+ self.sync_mempools()
+ assert_raises_rpc_error(-4, "Insufficient funds", wunsafe.walletcreatefundedpsbt, [], [{self.nodes[0].getnewaddress(): 1}])
+ wunsafe.walletcreatefundedpsbt([], [{self.nodes[0].getnewaddress(): 1}], 0, {"include_unsafe": True})
+
# BIP 174 Test Vectors
# Check that unknown values are just passed through
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index e061fa952c..d24d1693af 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -33,12 +33,15 @@ class WalletSendTest(BitcoinTestFramework):
def test_send(self, from_wallet, to_wallet=None, amount=None, data=None,
arg_conf_target=None, arg_estimate_mode=None, arg_fee_rate=None,
conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=None,
- inputs=None, add_inputs=None, change_address=None, change_position=None, change_type=None,
+ inputs=None, add_inputs=None, include_unsafe=None, change_address=None, change_position=None, change_type=None,
include_watching=None, locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None,
expect_error=None):
assert (amount is None) != (data is None)
- from_balance_before = from_wallet.getbalance()
+ from_balance_before = from_wallet.getbalances()["mine"]["trusted"]
+ if include_unsafe:
+ from_balance_before += from_wallet.getbalances()["mine"]["untrusted_pending"]
+
if to_wallet is None:
assert amount is None
else:
@@ -71,6 +74,8 @@ class WalletSendTest(BitcoinTestFramework):
options["inputs"] = inputs
if add_inputs is not None:
options["add_inputs"] = add_inputs
+ if include_unsafe is not None:
+ options["include_unsafe"] = include_unsafe
if change_address is not None:
options["change_address"] = change_address
if change_position is not None:
@@ -133,6 +138,10 @@ class WalletSendTest(BitcoinTestFramework):
assert not "txid" in res
assert "psbt" in res
+ from_balance = from_wallet.getbalances()["mine"]["trusted"]
+ if include_unsafe:
+ from_balance += from_wallet.getbalances()["mine"]["untrusted_pending"]
+
if add_to_wallet and not include_watching:
# Ensure transaction exists in the wallet:
tx = from_wallet.gettransaction(res["txid"])
@@ -143,13 +152,13 @@ class WalletSendTest(BitcoinTestFramework):
assert tx
if amount:
if subtract_fee_from_outputs:
- assert_equal(from_balance_before - from_wallet.getbalance(), amount)
+ assert_equal(from_balance_before - from_balance, amount)
else:
- assert_greater_than(from_balance_before - from_wallet.getbalance(), amount)
+ assert_greater_than(from_balance_before - from_balance, amount)
else:
assert next((out for out in tx["vout"] if out["scriptPubKey"]["asm"] == "OP_RETURN 35"), None)
else:
- assert_equal(from_balance_before, from_wallet.getbalance())
+ assert_equal(from_balance_before, from_balance)
if to_wallet:
self.sync_mempools()
@@ -459,6 +468,14 @@ class WalletSendTest(BitcoinTestFramework):
self.log.info("Subtract fee from output")
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, subtract_fee_from_outputs=[0])
+ self.log.info("Include unsafe inputs")
+ self.nodes[1].createwallet(wallet_name="w5")
+ w5 = self.nodes[1].get_wallet_rpc("w5")
+ self.test_send(from_wallet=w0, to_wallet=w5, amount=2)
+ self.test_send(from_wallet=w5, to_wallet=w0, amount=1, expect_error=(-4, "Insufficient funds"))
+ res = self.test_send(from_wallet=w5, to_wallet=w0, amount=1, include_unsafe=True)
+ assert res["complete"]
+
if __name__ == '__main__':
WalletSendTest().main()