Skip to content

Commit 73a339a

Browse files
committed
test: refactor: support sending funds with outpoint result
This commit introduces a helper `create_outpoints` to execute the `send` RPC and immediately return the target address outpoints as UTXO dictionary in the common format, making the tests more readable and avoiding unnecessary duplication.
1 parent d724bb5 commit 73a339a

9 files changed

+90
-107
lines changed

test/functional/rpc_psbt.py

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
assert_greater_than,
3939
assert_greater_than_or_equal,
4040
assert_raises_rpc_error,
41-
find_output,
4241
find_vout_for_address,
4342
random_bytes,
4443
)
@@ -417,16 +416,17 @@ def run_test(self):
417416
self.nodes[0].converttopsbt(hexstring=signedtx['hex'], permitsigdata=True)
418417

419418
# Create outputs to nodes 1 and 2
419+
# (note that we intentionally create two different txs here, as we want
420+
# to check that each node is missing prevout data for one of the two
421+
# utxos, see "should only have data for one input" test below)
420422
node1_addr = self.nodes[1].getnewaddress()
421423
node2_addr = self.nodes[2].getnewaddress()
422-
txid1 = self.nodes[0].sendtoaddress(node1_addr, 13)
423-
txid2 = self.nodes[0].sendtoaddress(node2_addr, 13)
424-
blockhash = self.generate(self.nodes[0], 6)[0]
425-
vout1 = find_output(self.nodes[1], txid1, 13, blockhash=blockhash)
426-
vout2 = find_output(self.nodes[2], txid2, 13, blockhash=blockhash)
424+
utxo1 = self.create_outpoints(self.nodes[0], outputs=[{node1_addr: 13}])[0]
425+
utxo2 = self.create_outpoints(self.nodes[0], outputs=[{node2_addr: 13}])[0]
426+
self.generate(self.nodes[0], 6)[0]
427427

428428
# Create a psbt spending outputs from nodes 1 and 2
429-
psbt_orig = self.nodes[0].createpsbt([{"txid":txid1, "vout":vout1}, {"txid":txid2, "vout":vout2}], {self.nodes[0].getnewaddress():25.999})
429+
psbt_orig = self.nodes[0].createpsbt([utxo1, utxo2], {self.nodes[0].getnewaddress():25.999})
430430

431431
# Update psbts, should only have data for one input and not the other
432432
psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL")['psbt']
@@ -603,22 +603,17 @@ def run_test(self):
603603

604604
# Send to all types of addresses
605605
addr1 = self.nodes[1].getnewaddress("", "bech32")
606-
txid1 = self.nodes[0].sendtoaddress(addr1, 11)
607-
vout1 = find_output(self.nodes[0], txid1, 11)
608606
addr2 = self.nodes[1].getnewaddress("", "legacy")
609-
txid2 = self.nodes[0].sendtoaddress(addr2, 11)
610-
vout2 = find_output(self.nodes[0], txid2, 11)
611607
addr3 = self.nodes[1].getnewaddress("", "p2sh-segwit")
612-
txid3 = self.nodes[0].sendtoaddress(addr3, 11)
613-
vout3 = find_output(self.nodes[0], txid3, 11)
608+
utxo1, utxo2, utxo3 = self.create_outpoints(self.nodes[1], outputs=[{addr1: 11}, {addr2: 11}, {addr3: 11}])
614609
self.sync_all()
615610

616611
def test_psbt_input_keys(psbt_input, keys):
617612
"""Check that the psbt input has only the expected keys."""
618613
assert_equal(set(keys), set(psbt_input.keys()))
619614

620615
# Create a PSBT. None of the inputs are filled initially
621-
psbt = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1},{"txid":txid2, "vout":vout2},{"txid":txid3, "vout":vout3}], {self.nodes[0].getnewaddress():32.999})
616+
psbt = self.nodes[1].createpsbt([utxo1, utxo2, utxo3], {self.nodes[0].getnewaddress():32.999})
622617
decoded = self.nodes[1].decodepsbt(psbt)
623618
test_psbt_input_keys(decoded['inputs'][0], [])
624619
test_psbt_input_keys(decoded['inputs'][1], [])
@@ -641,15 +636,14 @@ def test_psbt_input_keys(psbt_input, keys):
641636
test_psbt_input_keys(decoded['inputs'][2], ['non_witness_utxo','witness_utxo', 'bip32_derivs', 'redeem_script'])
642637

643638
# Two PSBTs with a common input should not be joinable
644-
psbt1 = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1}], {self.nodes[0].getnewaddress():Decimal('10.999')})
639+
psbt1 = self.nodes[1].createpsbt([utxo1], {self.nodes[0].getnewaddress():Decimal('10.999')})
645640
assert_raises_rpc_error(-8, "exists in multiple PSBTs", self.nodes[1].joinpsbts, [psbt1, updated])
646641

647642
# Join two distinct PSBTs
648643
addr4 = self.nodes[1].getnewaddress("", "p2sh-segwit")
649-
txid4 = self.nodes[0].sendtoaddress(addr4, 5)
650-
vout4 = find_output(self.nodes[0], txid4, 5)
644+
utxo4 = self.create_outpoints(self.nodes[0], outputs=[{addr4: 5}])[0]
651645
self.generate(self.nodes[0], 6)
652-
psbt2 = self.nodes[1].createpsbt([{"txid":txid4, "vout":vout4}], {self.nodes[0].getnewaddress():Decimal('4.999')})
646+
psbt2 = self.nodes[1].createpsbt([utxo4], {self.nodes[0].getnewaddress():Decimal('4.999')})
653647
psbt2 = self.nodes[1].walletprocesspsbt(psbt2)['psbt']
654648
psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
655649
assert "final_scriptwitness" in psbt2_decoded['inputs'][0] and "final_scriptSig" in psbt2_decoded['inputs'][0]
@@ -669,11 +663,10 @@ def test_psbt_input_keys(psbt_input, keys):
669663

670664
# Newly created PSBT needs UTXOs and updating
671665
addr = self.nodes[1].getnewaddress("", "p2sh-segwit")
672-
txid = self.nodes[0].sendtoaddress(addr, 7)
666+
utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 7}])[0]
673667
addrinfo = self.nodes[1].getaddressinfo(addr)
674-
blockhash = self.generate(self.nodes[0], 6)[0]
675-
vout = find_output(self.nodes[0], txid, 7, blockhash=blockhash)
676-
psbt = self.nodes[1].createpsbt([{"txid":txid, "vout":vout}], {self.nodes[0].getnewaddress("", "p2sh-segwit"):Decimal('6.999')})
668+
self.generate(self.nodes[0], 6)[0]
669+
psbt = self.nodes[1].createpsbt([utxo], {self.nodes[0].getnewaddress("", "p2sh-segwit"):Decimal('6.999')})
677670
analyzed = self.nodes[0].analyzepsbt(psbt)
678671
assert not analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0]['is_final'] and analyzed['inputs'][0]['next'] == 'updater' and analyzed['next'] == 'updater'
679672

@@ -872,9 +865,8 @@ def test_psbt_input_keys(psbt_input, keys):
872865

873866
self.log.info("Test that walletprocesspsbt both updates and signs a non-updated psbt containing Taproot inputs")
874867
addr = self.nodes[0].getnewaddress("", "bech32m")
875-
txid = self.nodes[0].sendtoaddress(addr, 1)
876-
vout = find_vout_for_address(self.nodes[0], txid, addr)
877-
psbt = self.nodes[0].createpsbt([{"txid": txid, "vout": vout}], [{self.nodes[0].getnewaddress(): 0.9999}])
868+
utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 1}])[0]
869+
psbt = self.nodes[0].createpsbt([utxo], [{self.nodes[0].getnewaddress(): 0.9999}])
878870
signed = self.nodes[0].walletprocesspsbt(psbt)
879871
rawtx = signed["hex"]
880872
self.nodes[0].sendrawtransaction(rawtx)
@@ -962,11 +954,10 @@ def test_psbt_input_keys(psbt_input, keys):
962954

963955
descriptor = descsum_create(f"wpkh({key})")
964956

965-
txid = self.nodes[0].sendtoaddress(address, 1)
957+
utxo = self.create_outpoints(self.nodes[0], outputs=[{address: 1}])[0]
966958
self.sync_all()
967-
vout = find_output(self.nodes[0], txid, 1)
968959

969-
psbt = self.nodes[2].createpsbt([{"txid": txid, "vout": vout}], {self.nodes[0].getnewaddress(): 0.99999})
960+
psbt = self.nodes[2].createpsbt([utxo], {self.nodes[0].getnewaddress(): 0.99999})
970961
decoded = self.nodes[2].decodepsbt(psbt)
971962
test_psbt_input_keys(decoded['inputs'][0], [])
972963

test/functional/test_framework/test_framework.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
PortSeed,
3131
assert_equal,
3232
check_json_precision,
33+
find_vout_for_address,
3334
get_datadir_path,
3435
initialize_datadir,
3536
p2p_port,
@@ -697,6 +698,22 @@ def generatetodescriptor(self, generator, *args, sync_fun=None, **kwargs):
697698
sync_fun() if sync_fun else self.sync_all()
698699
return blocks
699700

701+
def create_outpoints(self, node, *, outputs):
702+
"""Send funds to a given list of `{address: amount}` targets using the bitcoind
703+
wallet and return the corresponding outpoints as a list of dictionaries
704+
`[{"txid": txid, "vout": vout1}, {"txid": txid, "vout": vout2}, ...]`.
705+
The result can be used to specify inputs for RPCs like `createrawtransaction`,
706+
`createpsbt`, `lockunspent` etc."""
707+
assert all(len(output.keys()) == 1 for output in outputs)
708+
send_res = node.send(outputs)
709+
assert send_res["complete"]
710+
utxos = []
711+
for output in outputs:
712+
address = list(output.keys())[0]
713+
vout = find_vout_for_address(node, send_res["txid"], address)
714+
utxos.append({"txid": send_res["txid"], "vout": vout})
715+
return utxos
716+
700717
def sync_blocks(self, nodes=None, wait=1, timeout=60):
701718
"""
702719
Wait until everybody has the same tip.

test/functional/wallet_basic.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
assert_equal,
1919
assert_fee_amount,
2020
assert_raises_rpc_error,
21-
find_vout_for_address,
2221
)
2322
from test_framework.wallet_util import test_address
2423
from test_framework.wallet import MiniWallet
@@ -471,10 +470,9 @@ def run_test(self):
471470
# Import address and private key to check correct behavior of spendable unspents
472471
# 1. Send some coins to generate new UTXO
473472
address_to_import = self.nodes[2].getnewaddress()
474-
txid = self.nodes[0].sendtoaddress(address_to_import, 1)
473+
utxo = self.create_outpoints(self.nodes[0], outputs=[{address_to_import: 1}])[0]
475474
self.sync_mempools(self.nodes[0:3])
476-
vout = find_vout_for_address(self.nodes[2], txid, address_to_import)
477-
self.nodes[2].lockunspent(False, [{"txid": txid, "vout": vout}])
475+
self.nodes[2].lockunspent(False, [utxo])
478476
self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))
479477

480478
self.log.info("Test sendtoaddress with fee_rate param (explicit fee rate in sat/vB)")

test/functional/wallet_fundrawtransaction.py

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
assert_greater_than_or_equal,
2323
assert_raises_rpc_error,
2424
count_bytes,
25-
find_vout_for_address,
2625
get_fee,
2726
)
2827
from test_framework.wallet_util import generate_keypair, WalletUnlock
@@ -85,14 +84,13 @@ def unlock_utxos(self, wallet):
8584
Unlock all UTXOs except the watchonly one
8685
"""
8786
to_keep = []
88-
if self.watchonly_txid is not None and self.watchonly_vout is not None:
89-
to_keep.append({"txid": self.watchonly_txid, "vout": self.watchonly_vout})
87+
if self.watchonly_utxo is not None:
88+
to_keep.append(self.watchonly_utxo)
9089
wallet.lockunspent(True)
9190
wallet.lockunspent(False, to_keep)
9291

9392
def run_test(self):
94-
self.watchonly_txid = None
95-
self.watchonly_vout = None
93+
self.watchonly_utxo = None
9694
self.log.info("Connect nodes, set fees, generate blocks, and sync")
9795
self.min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
9896
# This test is not meant to test fee estimation and we'd like
@@ -163,11 +161,10 @@ def test_change_position(self):
163161
watchonly_pubkey = self.nodes[0].getaddressinfo(watchonly_address)["pubkey"]
164162
self.watchonly_amount = Decimal(200)
165163
wwatch.importpubkey(watchonly_pubkey, "", True)
166-
self.watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, self.watchonly_amount)
164+
self.watchonly_utxo = self.create_outpoints(self.nodes[0], outputs=[{watchonly_address: self.watchonly_amount}])[0]
167165

168166
# Lock UTXO so nodes[0] doesn't accidentally spend it
169-
self.watchonly_vout = find_vout_for_address(self.nodes[0], self.watchonly_txid, watchonly_address)
170-
self.nodes[0].lockunspent(False, [{"txid": self.watchonly_txid, "vout": self.watchonly_vout}])
167+
self.nodes[0].lockunspent(False, [self.watchonly_utxo])
171168

172169
self.nodes[0].sendtoaddress(self.nodes[3].get_wallet_rpc(self.default_wallet_name).getnewaddress(), self.watchonly_amount / 10)
173170

@@ -738,7 +735,7 @@ def test_watchonly(self):
738735
result = wwatch.fundrawtransaction(rawtx, True)
739736
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
740737
assert_equal(len(res_dec["vin"]), 1)
741-
assert_equal(res_dec["vin"][0]["txid"], self.watchonly_txid)
738+
assert_equal(res_dec["vin"][0]["txid"], self.watchonly_utxo['txid'])
742739

743740
assert "fee" in result.keys()
744741
assert_greater_than(result["changepos"], -1)
@@ -758,7 +755,7 @@ def test_all_watched_funds(self):
758755
result = wwatch.fundrawtransaction(rawtx, includeWatching=True, changeAddress=w3.getrawchangeaddress(), subtractFeeFromOutputs=[0])
759756
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
760757
assert_equal(len(res_dec["vin"]), 1)
761-
assert res_dec["vin"][0]["txid"] == self.watchonly_txid
758+
assert res_dec["vin"][0]["txid"] == self.watchonly_utxo['txid']
762759

763760
assert_greater_than(result["fee"], 0)
764761
assert_equal(result["changepos"], -1)
@@ -970,10 +967,9 @@ def test_subtract_fee_with_presets(self):
970967
self.log.info("Test fundrawtxn subtract fee from outputs with preset inputs that are sufficient")
971968

972969
addr = self.nodes[0].getnewaddress()
973-
txid = self.nodes[0].sendtoaddress(addr, 10)
974-
vout = find_vout_for_address(self.nodes[0], txid, addr)
970+
utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 10}])[0]
975971

976-
rawtx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], [{self.nodes[0].getnewaddress(): 5}])
972+
rawtx = self.nodes[0].createrawtransaction([utxo], [{self.nodes[0].getnewaddress(): 5}])
977973
fundedtx = self.nodes[0].fundrawtransaction(rawtx, subtractFeeFromOutputs=[0])
978974
signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx['hex'])
979975
self.nodes[0].sendrawtransaction(signedtx['hex'])
@@ -1264,22 +1260,20 @@ def test_weight_calculation(self):
12641260

12651261
addr = wallet.getnewaddress(address_type="bech32")
12661262
ext_addr = self.nodes[0].getnewaddress(address_type="bech32")
1267-
txid = self.nodes[0].send([{addr: 5}, {ext_addr: 5}])["txid"]
1268-
vout = find_vout_for_address(self.nodes[0], txid, addr)
1269-
ext_vout = find_vout_for_address(self.nodes[0], txid, ext_addr)
1263+
utxo, ext_utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 5}, {ext_addr: 5}])
12701264

12711265
self.nodes[0].sendtoaddress(wallet.getnewaddress(address_type="bech32"), 5)
12721266
self.generate(self.nodes[0], 1)
12731267

1274-
rawtx = wallet.createrawtransaction([{'txid': txid, 'vout': vout}], [{self.nodes[0].getnewaddress(address_type="bech32"): 8}])
1268+
rawtx = wallet.createrawtransaction([utxo], [{self.nodes[0].getnewaddress(address_type="bech32"): 8}])
12751269
fundedtx = wallet.fundrawtransaction(rawtx, fee_rate=10, change_type="bech32")
12761270
# with 71-byte signatures we should expect following tx size
12771271
# tx overhead (10) + 2 inputs (41 each) + 2 p2wpkh (31 each) + (segwit marker and flag (2) + 2 p2wpkh 71 byte sig witnesses (107 each)) / witness scaling factor (4)
12781272
tx_size = ceil(10 + 41*2 + 31*2 + (2 + 107*2)/4)
12791273
assert_equal(fundedtx['fee'] * COIN, tx_size * 10)
12801274

12811275
# Using the other output should have 72 byte sigs
1282-
rawtx = wallet.createrawtransaction([{'txid': txid, 'vout': ext_vout}], [{self.nodes[0].getnewaddress(): 13}])
1276+
rawtx = wallet.createrawtransaction([ext_utxo], [{self.nodes[0].getnewaddress(): 13}])
12831277
ext_desc = self.nodes[0].getaddressinfo(ext_addr)["desc"]
12841278
fundedtx = wallet.fundrawtransaction(rawtx, fee_rate=10, change_type="bech32", solving_data={"descriptors": [ext_desc]})
12851279
# tx overhead (10) + 3 inputs (41 each) + 2 p2wpkh(31 each) + (segwit marker and flag (2) + 2 p2wpkh 71 bytes sig witnesses (107 each) + p2wpkh 72 byte sig witness (108)) / witness scaling factor (4)
@@ -1298,10 +1292,9 @@ def test_include_unsafe(self):
12981292
addr = wallet.getnewaddress()
12991293
inputs = []
13001294
for i in range(0, 2):
1301-
txid = self.nodes[2].sendtoaddress(addr, 5)
1302-
self.sync_mempools()
1303-
vout = find_vout_for_address(wallet, txid, addr)
1304-
inputs.append((txid, vout))
1295+
utxo = self.create_outpoints(self.nodes[2], outputs=[{addr: 5}])[0]
1296+
inputs.append((utxo['txid'], utxo['vout']))
1297+
self.sync_mempools()
13051298

13061299
# Unsafe inputs are ignored by default.
13071300
rawtx = wallet.createrawtransaction([], [{self.nodes[2].getnewaddress(): 7.5}])

test/functional/wallet_importdescriptors.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
from test_framework.util import (
2525
assert_equal,
2626
assert_raises_rpc_error,
27-
find_vout_for_address,
2827
)
2928
from test_framework.wallet_util import (
3029
get_generate_key,
@@ -493,12 +492,10 @@ def run_test(self):
493492
assert_equal(wmulti_pub.getwalletinfo()['keypoolsize'], 999)
494493

495494
# generate some utxos for next tests
496-
txid = w0.sendtoaddress(addr, 10)
497-
vout = find_vout_for_address(self.nodes[0], txid, addr)
495+
utxo = self.create_outpoints(w0, outputs=[{addr: 10}])[0]
498496

499497
addr2 = wmulti_pub.getnewaddress('', 'bech32')
500-
txid2 = w0.sendtoaddress(addr2, 10)
501-
vout2 = find_vout_for_address(self.nodes[0], txid2, addr2)
498+
utxo2 = self.create_outpoints(w0, outputs=[{addr2: 10}])[0]
502499

503500
self.generate(self.nodes[0], 6)
504501
assert_equal(wmulti_pub.getbalance(), wmulti_priv.getbalance())
@@ -554,7 +551,7 @@ def run_test(self):
554551
assert_equal(res[1]['success'], True)
555552
assert_equal(res[1]['warnings'][0], 'Not all private keys provided. Some wallet functionality may return unexpected errors')
556553

557-
rawtx = self.nodes[1].createrawtransaction([{'txid': txid, 'vout': vout}], {w0.getnewaddress(): 9.999})
554+
rawtx = self.nodes[1].createrawtransaction([utxo], {w0.getnewaddress(): 9.999})
558555
tx_signed_1 = wmulti_priv1.signrawtransactionwithwallet(rawtx)
559556
assert_equal(tx_signed_1['complete'], False)
560557
tx_signed_2 = wmulti_priv2.signrawtransactionwithwallet(tx_signed_1['hex'])
@@ -648,7 +645,7 @@ def run_test(self):
648645
}])
649646
assert_equal(res[0]['success'], True)
650647

651-
rawtx = self.nodes[1].createrawtransaction([{'txid': txid2, 'vout': vout2}], {w0.getnewaddress(): 9.999})
648+
rawtx = self.nodes[1].createrawtransaction([utxo2], {w0.getnewaddress(): 9.999})
652649
tx = wmulti_priv3.signrawtransactionwithwallet(rawtx)
653650
assert_equal(tx['complete'], True)
654651
self.nodes[1].sendrawtransaction(tx['hex'])

0 commit comments

Comments
 (0)