aboutsummaryrefslogtreecommitdiff
path: root/test/functional/rpc_psbt.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/rpc_psbt.py')
-rwxr-xr-xtest/functional/rpc_psbt.py108
1 files changed, 96 insertions, 12 deletions
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 6f9f52f001..8a7ea7aa58 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -1,12 +1,19 @@
#!/usr/bin/env python3
-# Copyright (c) 2018 The Bitcoin Core developers
+# Copyright (c) 2018-2019 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the Partially Signed Transaction RPCs.
"""
+from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, find_output, disconnect_nodes, connect_nodes_bi, sync_blocks
+from test_framework.util import (
+ assert_equal,
+ assert_raises_rpc_error,
+ connect_nodes_bi,
+ disconnect_nodes,
+ find_output,
+)
import json
import os
@@ -40,22 +47,22 @@ class PSBTTest(BitcoinTestFramework):
online_node.importaddress(offline_addr, "", False)
mining_node.sendtoaddress(address=offline_addr, amount=1.0)
mining_node.generate(nblocks=1)
- sync_blocks([mining_node, online_node])
+ self.sync_blocks([mining_node, online_node])
# Construct an unsigned PSBT on the online node (who doesn't know the output is Segwit, so will include a non-witness UTXO)
utxos = online_node.listunspent(addresses=[offline_addr])
raw = online_node.createrawtransaction([{"txid":utxos[0]["txid"], "vout":utxos[0]["vout"]}],[{online_addr:0.9999}])
psbt = online_node.walletprocesspsbt(online_node.converttopsbt(raw))["psbt"]
- assert("non_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0])
+ assert "non_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0]
# Have the offline node sign the PSBT (which will update the UTXO to segwit)
signed_psbt = offline_node.walletprocesspsbt(psbt)["psbt"]
- assert("witness_utxo" in mining_node.decodepsbt(signed_psbt)["inputs"][0])
+ assert "witness_utxo" in mining_node.decodepsbt(signed_psbt)["inputs"][0]
# Make sure we can mine the resulting transaction
txid = mining_node.sendrawtransaction(mining_node.finalizepsbt(signed_psbt)["hex"])
mining_node.generate(1)
- sync_blocks([mining_node, online_node])
+ self.sync_blocks([mining_node, online_node])
assert_equal(online_node.gettxout(txid,0)["confirmations"], 1)
# Reconnect
@@ -159,11 +166,11 @@ class PSBTTest(BitcoinTestFramework):
node1_addr = self.nodes[1].getnewaddress()
node2_addr = self.nodes[2].getnewaddress()
txid1 = self.nodes[0].sendtoaddress(node1_addr, 13)
- txid2 =self.nodes[0].sendtoaddress(node2_addr, 13)
- self.nodes[0].generate(6)
+ txid2 = self.nodes[0].sendtoaddress(node2_addr, 13)
+ blockhash = self.nodes[0].generate(6)[0]
self.sync_all()
- vout1 = find_output(self.nodes[1], txid1, 13)
- vout2 = find_output(self.nodes[2], txid2, 13)
+ vout1 = find_output(self.nodes[1], txid1, 13, blockhash=blockhash)
+ vout2 = find_output(self.nodes[2], txid2, 13, blockhash=blockhash)
# Create a psbt spending outputs from nodes 1 and 2
psbt_orig = self.nodes[0].createpsbt([{"txid":txid1, "vout":vout1}, {"txid":txid2, "vout":vout2}], {self.nodes[0].getnewaddress():25.999})
@@ -192,8 +199,8 @@ class PSBTTest(BitcoinTestFramework):
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable":True}, False)
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
- assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
- assert "bip32_derivs" not in psbt_in
+ assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
+ assert "bip32_derivs" not in psbt_in
assert_equal(decoded_psbt["tx"]["locktime"], block_height+2)
# Same construction with only locktime set
@@ -211,6 +218,10 @@ class PSBTTest(BitcoinTestFramework):
assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
assert_equal(decoded_psbt["tx"]["locktime"], 0)
+ # Make sure change address wallet does not have P2SH innerscript access to results in success
+ # when attempting BnB coin selection
+ self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)
+
# Regression test for 14473 (mishandling of already-signed witness transaction):
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])
complete_psbt = self.nodes[0].walletprocesspsbt(psbtx_info["psbt"])
@@ -264,6 +275,9 @@ class PSBTTest(BitcoinTestFramework):
combined = self.nodes[2].combinepsbt(combiner['combine'])
assert_equal(combined, combiner['result'])
+ # Empty combiner test
+ assert_raises_rpc_error(-8, "Parameter 'txs' cannot be empty", self.nodes[0].combinepsbt, [])
+
# Finalizer test
for finalizer in finalizers:
finalized = self.nodes[2].finalizepsbt(finalizer['finalize'], False)['psbt']
@@ -285,5 +299,75 @@ class PSBTTest(BitcoinTestFramework):
psbt = self.nodes[1].walletcreatefundedpsbt([], [{p2pkh : 1}], 0, {"includeWatching" : True}, True)
self.nodes[0].decodepsbt(psbt['psbt'])
+ # Test decoding error: invalid base64
+ assert_raises_rpc_error(-22, "TX decode failed invalid base64", self.nodes[0].decodepsbt, ";definitely not base64;")
+
+ # Send to all types of addresses
+ addr1 = self.nodes[1].getnewaddress("", "bech32")
+ txid1 = self.nodes[0].sendtoaddress(addr1, 11)
+ vout1 = find_output(self.nodes[0], txid1, 11)
+ addr2 = self.nodes[1].getnewaddress("", "legacy")
+ txid2 = self.nodes[0].sendtoaddress(addr2, 11)
+ vout2 = find_output(self.nodes[0], txid2, 11)
+ addr3 = self.nodes[1].getnewaddress("", "p2sh-segwit")
+ txid3 = self.nodes[0].sendtoaddress(addr3, 11)
+ vout3 = find_output(self.nodes[0], txid3, 11)
+ self.sync_all()
+
+ # Update a PSBT with UTXOs from the node
+ # Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
+ psbt = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1},{"txid":txid2, "vout":vout2},{"txid":txid3, "vout":vout3}], {self.nodes[0].getnewaddress():32.999})
+ decoded = self.nodes[1].decodepsbt(psbt)
+ assert "witness_utxo" not in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
+ assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
+ assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
+ updated = self.nodes[1].utxoupdatepsbt(psbt)
+ decoded = self.nodes[1].decodepsbt(updated)
+ assert "witness_utxo" in decoded['inputs'][0] and "non_witness_utxo" not in decoded['inputs'][0]
+ assert "witness_utxo" not in decoded['inputs'][1] and "non_witness_utxo" not in decoded['inputs'][1]
+ assert "witness_utxo" not in decoded['inputs'][2] and "non_witness_utxo" not in decoded['inputs'][2]
+
+ # Two PSBTs with a common input should not be joinable
+ psbt1 = self.nodes[1].createpsbt([{"txid":txid1, "vout":vout1}], {self.nodes[0].getnewaddress():Decimal('10.999')})
+ assert_raises_rpc_error(-8, "exists in multiple PSBTs", self.nodes[1].joinpsbts, [psbt1, updated])
+
+ # Join two distinct PSBTs
+ addr4 = self.nodes[1].getnewaddress("", "p2sh-segwit")
+ txid4 = self.nodes[0].sendtoaddress(addr4, 5)
+ vout4 = find_output(self.nodes[0], txid4, 5)
+ self.nodes[0].generate(6)
+ self.sync_all()
+ psbt2 = self.nodes[1].createpsbt([{"txid":txid4, "vout":vout4}], {self.nodes[0].getnewaddress():Decimal('4.999')})
+ psbt2 = self.nodes[1].walletprocesspsbt(psbt2)['psbt']
+ psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
+ assert "final_scriptwitness" in psbt2_decoded['inputs'][0] and "final_scriptSig" in psbt2_decoded['inputs'][0]
+ joined = self.nodes[0].joinpsbts([psbt, psbt2])
+ joined_decoded = self.nodes[0].decodepsbt(joined)
+ assert len(joined_decoded['inputs']) == 4 and len(joined_decoded['outputs']) == 2 and "final_scriptwitness" not in joined_decoded['inputs'][3] and "final_scriptSig" not in joined_decoded['inputs'][3]
+
+ # Newly created PSBT needs UTXOs and updating
+ addr = self.nodes[1].getnewaddress("", "p2sh-segwit")
+ txid = self.nodes[0].sendtoaddress(addr, 7)
+ addrinfo = self.nodes[1].getaddressinfo(addr)
+ blockhash = self.nodes[0].generate(6)[0]
+ self.sync_all()
+ vout = find_output(self.nodes[0], txid, 7, blockhash=blockhash)
+ psbt = self.nodes[1].createpsbt([{"txid":txid, "vout":vout}], {self.nodes[0].getnewaddress("", "p2sh-segwit"):Decimal('6.999')})
+ analyzed = self.nodes[0].analyzepsbt(psbt)
+ assert not analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0]['is_final'] and analyzed['inputs'][0]['next'] == 'updater' and analyzed['next'] == 'updater'
+
+ # After update with wallet, only needs signing
+ updated = self.nodes[1].walletprocesspsbt(psbt, False, 'ALL', True)['psbt']
+ analyzed = self.nodes[0].analyzepsbt(updated)
+ assert analyzed['inputs'][0]['has_utxo'] and not analyzed['inputs'][0]['is_final'] and analyzed['inputs'][0]['next'] == 'signer' and analyzed['next'] == 'signer' and analyzed['inputs'][0]['missing']['signatures'][0] == addrinfo['embedded']['witness_program']
+
+ # Check fee and size things
+ assert analyzed['fee'] == Decimal('0.001') and analyzed['estimated_vsize'] == 134 and analyzed['estimated_feerate'] == Decimal('0.00746268')
+
+ # After signing and finalizing, needs extracting
+ signed = self.nodes[1].walletprocesspsbt(updated)['psbt']
+ analyzed = self.nodes[0].analyzepsbt(signed)
+ assert analyzed['inputs'][0]['has_utxo'] and analyzed['inputs'][0]['is_final'] and analyzed['next'] == 'extractor'
+
if __name__ == '__main__':
PSBTTest().main()