mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-30 18:35:54 +02:00
Merge #13932: Additional utility RPCs for PSBT
540729ef4bImplement analyzepsbt RPC and tests (Andrew Chow)77542cf2a5Move PSBT UTXO fetching to a separate method (Andrew Chow)cb40b3abd4Figure out what is missing during signing (Andrew Chow)08f749c914Implement joinpsbts RPC and tests (Andrew Chow)7344a7b998Implement utxoupdatepsbt RPC and tests (Andrew Chow) Pull request description: This PR adds 3 new utility RPCs for interacting with PSBTs. `utxoupdatepsbt` updates a PSBT with UTXO information from the node. It only works with witness UTXOs because full transactions (as would be needed for non-witness UTXOs) are not available unless txindex is enabled. `joinpsbts` joins the inputs from multiple distinct PSBTs into one PSBT. e.g. if PSBT 1 has inputs 1 and 2, and PSBT 2 has inputs 3 and 4, `joinpsbts` would create a new PSBT with inputs 1, 2, 3, and 4. `analyzepsbt` analyzes a PSBT and determines the current state of it and all of its inputs, and the next step that needs to be done. Tree-SHA512: 3c1fa302201abca76a8901d0c2be7b4ccbce334d989533c215f8b3e50e22f2f018ce6209544b26789f58f5980a253c0655111e1e20d47d5656e0414c64891a5c
This commit is contained in:
@@ -5,8 +5,9 @@
|
||||
"""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, sync_blocks
|
||||
|
||||
import json
|
||||
import os
|
||||
@@ -20,7 +21,7 @@ class PSBTTest(BitcoinTestFramework):
|
||||
self.setup_clean_chain = False
|
||||
self.num_nodes = 3
|
||||
# TODO: remove -txindex. Currently required for getrawtransaction call.
|
||||
self.extra_args = [[], ["-txindex"], ["-txindex"]]
|
||||
self.extra_args = [["-txindex"], ["-txindex"], ["-txindex"]]
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
@@ -296,5 +297,72 @@ class PSBTTest(BitcoinTestFramework):
|
||||
# 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)
|
||||
self.nodes[0].generate(6)
|
||||
self.sync_all()
|
||||
vout = find_output(self.nodes[0], txid, 7)
|
||||
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'] == '0.00746268 BTC/kB'
|
||||
|
||||
# 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()
|
||||
|
||||
Reference in New Issue
Block a user