mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-06-30 02:31:05 +02:00
Tests for funding with external inputs
This commit is contained in:
@ -8,6 +8,7 @@ from decimal import Decimal
|
|||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
from test_framework.descriptors import descsum_create
|
from test_framework.descriptors import descsum_create
|
||||||
|
from test_framework.key import ECKey
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import (
|
from test_framework.util import (
|
||||||
assert_approx,
|
assert_approx,
|
||||||
@ -19,6 +20,7 @@ from test_framework.util import (
|
|||||||
count_bytes,
|
count_bytes,
|
||||||
find_vout_for_address,
|
find_vout_for_address,
|
||||||
)
|
)
|
||||||
|
from test_framework.wallet_util import bytes_to_wif
|
||||||
|
|
||||||
|
|
||||||
def get_unspent(listunspent, amount):
|
def get_unspent(listunspent, amount):
|
||||||
@ -132,6 +134,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
self.test_subtract_fee_with_presets()
|
self.test_subtract_fee_with_presets()
|
||||||
self.test_transaction_too_large()
|
self.test_transaction_too_large()
|
||||||
self.test_include_unsafe()
|
self.test_include_unsafe()
|
||||||
|
self.test_external_inputs()
|
||||||
self.test_22670()
|
self.test_22670()
|
||||||
|
|
||||||
def test_change_position(self):
|
def test_change_position(self):
|
||||||
@ -983,6 +986,56 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
wallet.sendmany("", outputs)
|
wallet.sendmany("", outputs)
|
||||||
self.generate(self.nodes[0], 10)
|
self.generate(self.nodes[0], 10)
|
||||||
assert_raises_rpc_error(-4, "Transaction too large", recipient.fundrawtransaction, rawtx)
|
assert_raises_rpc_error(-4, "Transaction too large", recipient.fundrawtransaction, rawtx)
|
||||||
|
self.nodes[0].unloadwallet("large")
|
||||||
|
|
||||||
|
def test_external_inputs(self):
|
||||||
|
self.log.info("Test funding with external inputs")
|
||||||
|
|
||||||
|
eckey = ECKey()
|
||||||
|
eckey.generate()
|
||||||
|
privkey = bytes_to_wif(eckey.get_bytes())
|
||||||
|
|
||||||
|
self.nodes[2].createwallet("extfund")
|
||||||
|
wallet = self.nodes[2].get_wallet_rpc("extfund")
|
||||||
|
|
||||||
|
# Make a weird but signable script. sh(pkh()) descriptor accomplishes this
|
||||||
|
desc = descsum_create("sh(pkh({}))".format(privkey))
|
||||||
|
if self.options.descriptors:
|
||||||
|
res = self.nodes[0].importdescriptors([{"desc": desc, "timestamp": "now"}])
|
||||||
|
else:
|
||||||
|
res = self.nodes[0].importmulti([{"desc": desc, "timestamp": "now"}])
|
||||||
|
assert res[0]["success"]
|
||||||
|
addr = self.nodes[0].deriveaddresses(desc)[0]
|
||||||
|
addr_info = self.nodes[0].getaddressinfo(addr)
|
||||||
|
|
||||||
|
self.nodes[0].sendtoaddress(addr, 10)
|
||||||
|
self.nodes[0].sendtoaddress(wallet.getnewaddress(), 10)
|
||||||
|
self.nodes[0].generate(6)
|
||||||
|
ext_utxo = self.nodes[0].listunspent(addresses=[addr])[0]
|
||||||
|
|
||||||
|
# An external input without solving data should result in an error
|
||||||
|
raw_tx = wallet.createrawtransaction([ext_utxo], {self.nodes[0].getnewaddress(): 15})
|
||||||
|
assert_raises_rpc_error(-4, "Insufficient funds", wallet.fundrawtransaction, raw_tx)
|
||||||
|
|
||||||
|
# Error conditions
|
||||||
|
assert_raises_rpc_error(-5, "'not a pubkey' is not hex", wallet.fundrawtransaction, raw_tx, {"solving_data": {"pubkeys":["not a pubkey"]}})
|
||||||
|
assert_raises_rpc_error(-5, "'01234567890a0b0c0d0e0f' is not a valid public key", wallet.fundrawtransaction, raw_tx, {"solving_data": {"pubkeys":["01234567890a0b0c0d0e0f"]}})
|
||||||
|
assert_raises_rpc_error(-5, "'not a script' is not hex", wallet.fundrawtransaction, raw_tx, {"solving_data": {"scripts":["not a script"]}})
|
||||||
|
assert_raises_rpc_error(-8, "Unable to parse descriptor 'not a descriptor'", wallet.fundrawtransaction, raw_tx, {"solving_data": {"descriptors":["not a descriptor"]}})
|
||||||
|
|
||||||
|
# But funding should work when the solving data is provided
|
||||||
|
funded_tx = wallet.fundrawtransaction(raw_tx, {"solving_data": {"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]}})
|
||||||
|
signed_tx = wallet.signrawtransactionwithwallet(funded_tx['hex'])
|
||||||
|
assert not signed_tx['complete']
|
||||||
|
signed_tx = self.nodes[0].signrawtransactionwithwallet(signed_tx['hex'])
|
||||||
|
assert signed_tx['complete']
|
||||||
|
|
||||||
|
funded_tx = wallet.fundrawtransaction(raw_tx, {"solving_data": {"descriptors": [desc]}})
|
||||||
|
signed_tx = wallet.signrawtransactionwithwallet(funded_tx['hex'])
|
||||||
|
assert not signed_tx['complete']
|
||||||
|
signed_tx = self.nodes[0].signrawtransactionwithwallet(signed_tx['hex'])
|
||||||
|
assert signed_tx['complete']
|
||||||
|
self.nodes[2].unloadwallet("extfund")
|
||||||
|
|
||||||
def test_include_unsafe(self):
|
def test_include_unsafe(self):
|
||||||
self.log.info("Test fundrawtxn with unsafe inputs")
|
self.log.info("Test fundrawtxn with unsafe inputs")
|
||||||
@ -1017,6 +1070,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
assert all((txin["txid"], txin["vout"]) in inputs for txin in tx_dec["vin"])
|
assert all((txin["txid"], txin["vout"]) in inputs for txin in tx_dec["vin"])
|
||||||
signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
|
signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
|
||||||
assert wallet.testmempoolaccept([signedtx['hex']])[0]["allowed"]
|
assert wallet.testmempoolaccept([signedtx['hex']])[0]["allowed"]
|
||||||
|
self.nodes[0].unloadwallet("unsafe")
|
||||||
|
|
||||||
def test_22670(self):
|
def test_22670(self):
|
||||||
# In issue #22670, it was observed that ApproximateBestSubset may
|
# In issue #22670, it was observed that ApproximateBestSubset may
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
|
from test_framework.descriptors import descsum_create
|
||||||
|
from test_framework.key import ECKey
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import (
|
from test_framework.util import (
|
||||||
assert_approx,
|
assert_approx,
|
||||||
@ -16,6 +18,7 @@ from test_framework.util import (
|
|||||||
assert_raises_rpc_error,
|
assert_raises_rpc_error,
|
||||||
find_output,
|
find_output,
|
||||||
)
|
)
|
||||||
|
from test_framework.wallet_util import bytes_to_wif
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
@ -608,5 +611,42 @@ class PSBTTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
assert_raises_rpc_error(-25, 'Inputs missing or spent', self.nodes[0].walletprocesspsbt, 'cHNidP8BAJoCAAAAAkvEW8NnDtdNtDpsmze+Ht2LH35IJcKv00jKAlUs21RrAwAAAAD/////S8Rbw2cO1020OmybN74e3Ysffkglwq/TSMoCVSzbVGsBAAAAAP7///8CwLYClQAAAAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XIAAAAAAAEAnQIAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP7///9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/v///wIA+QKVAAAAABl2qRT9zXUVA8Ls5iVqynLHe5/vSe1XyYisQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAAAAAQEfQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==')
|
assert_raises_rpc_error(-25, 'Inputs missing or spent', self.nodes[0].walletprocesspsbt, 'cHNidP8BAJoCAAAAAkvEW8NnDtdNtDpsmze+Ht2LH35IJcKv00jKAlUs21RrAwAAAAD/////S8Rbw2cO1020OmybN74e3Ysffkglwq/TSMoCVSzbVGsBAAAAAP7///8CwLYClQAAAAAWABSNJKzjaUb3uOxixsvh1GGE3fW7zQD5ApUAAAAAFgAUKNw0x8HRctAgmvoevm4u1SbN7XIAAAAAAAEAnQIAAAACczMa321tVHuN4GKWKRncycI22aX3uXgwSFUKM2orjRsBAAAAAP7///9zMxrfbW1Ue43gYpYpGdzJwjbZpfe5eDBIVQozaiuNGwAAAAAA/v///wIA+QKVAAAAABl2qRT9zXUVA8Ls5iVqynLHe5/vSe1XyYisQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAAAAAQEfQM0ClQAAAAAWABRmWQUcjSjghQ8/uH4Bn/zkakwLtAAAAA==')
|
||||||
|
|
||||||
|
# Test that we can fund psbts with external inputs specified
|
||||||
|
eckey = ECKey()
|
||||||
|
eckey.generate()
|
||||||
|
privkey = bytes_to_wif(eckey.get_bytes())
|
||||||
|
|
||||||
|
# Make a weird but signable script. sh(pkh()) descriptor accomplishes this
|
||||||
|
desc = descsum_create("sh(pkh({}))".format(privkey))
|
||||||
|
if self.options.descriptors:
|
||||||
|
res = self.nodes[0].importdescriptors([{"desc": desc, "timestamp": "now"}])
|
||||||
|
else:
|
||||||
|
res = self.nodes[0].importmulti([{"desc": desc, "timestamp": "now"}])
|
||||||
|
assert res[0]["success"]
|
||||||
|
addr = self.nodes[0].deriveaddresses(desc)[0]
|
||||||
|
addr_info = self.nodes[0].getaddressinfo(addr)
|
||||||
|
|
||||||
|
self.nodes[0].sendtoaddress(addr, 10)
|
||||||
|
self.nodes[0].generate(6)
|
||||||
|
ext_utxo = self.nodes[0].listunspent(addresses=[addr])[0]
|
||||||
|
|
||||||
|
# An external input without solving data should result in an error
|
||||||
|
assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[1].walletcreatefundedpsbt, [ext_utxo], {self.nodes[0].getnewaddress(): 10 + ext_utxo['amount']}, 0, {'add_inputs': True})
|
||||||
|
|
||||||
|
# But funding should work when the solving data is provided
|
||||||
|
psbt = self.nodes[1].walletcreatefundedpsbt([ext_utxo], {self.nodes[0].getnewaddress(): 15}, 0, {'add_inputs': True, "solving_data": {"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]}})
|
||||||
|
signed = self.nodes[1].walletprocesspsbt(psbt['psbt'])
|
||||||
|
assert not signed['complete']
|
||||||
|
signed = self.nodes[0].walletprocesspsbt(signed['psbt'])
|
||||||
|
assert signed['complete']
|
||||||
|
self.nodes[0].finalizepsbt(signed['psbt'])
|
||||||
|
|
||||||
|
psbt = self.nodes[1].walletcreatefundedpsbt([ext_utxo], {self.nodes[0].getnewaddress(): 15}, 0, {'add_inputs': True, "solving_data":{"descriptors": [desc]}})
|
||||||
|
signed = self.nodes[1].walletprocesspsbt(psbt['psbt'])
|
||||||
|
assert not signed['complete']
|
||||||
|
signed = self.nodes[0].walletprocesspsbt(signed['psbt'])
|
||||||
|
assert signed['complete']
|
||||||
|
self.nodes[0].finalizepsbt(signed['psbt'])
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
PSBTTest().main()
|
PSBTTest().main()
|
||||||
|
Reference in New Issue
Block a user