aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rwxr-xr-xtest/functional/feature_notifications.py10
-rwxr-xr-xtest/functional/feature_rbf.py25
-rwxr-xr-xtest/functional/p2p_compactblocks_blocksonly.py130
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py54
-rwxr-xr-xtest/functional/rpc_psbt.py40
-rw-r--r--test/functional/test_framework/util.py9
-rwxr-xr-xtest/functional/test_runner.py1
-rwxr-xr-xtest/functional/wallet_basic.py26
-rwxr-xr-xtest/functional/wallet_hd.py2
-rwxr-xr-xtest/functional/wallet_send.py46
10 files changed, 302 insertions, 41 deletions
diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py
index 5ef3860867..4382022a7a 100755
--- a/test/functional/feature_notifications.py
+++ b/test/functional/feature_notifications.py
@@ -42,7 +42,6 @@ class NotificationsTest(BitcoinTestFramework):
f"-alertnotify=echo > {os.path.join(self.alertnotify_dir, '%s')}",
f"-blocknotify=echo > {os.path.join(self.blocknotify_dir, '%s')}",
], [
- "-rescan",
f"-walletnotify=echo %h_%b > {os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))}",
]]
self.wallet_names = [self.default_wallet_name, self.wallet]
@@ -91,16 +90,15 @@ class NotificationsTest(BitcoinTestFramework):
# directory content should equal the generated transaction hashes
tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count)))
- self.stop_node(1)
self.expect_wallet_notify(tx_details)
self.log.info("test -walletnotify after rescan")
- # restart node to rescan to force wallet notifications
- self.start_node(1)
- self.connect_nodes(0, 1)
-
+ # rescan to force wallet notifications
+ self.nodes[1].rescanblockchain()
self.wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10)
+ self.connect_nodes(0, 1)
+
# directory content should equal the generated transaction hashes
tx_details = list(map(lambda t: (t['txid'], t['blockheight'], t['blockhash']), self.nodes[1].listtransactions("*", block_count)))
self.expect_wallet_notify(tx_details)
diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py
index d759a5aab5..4eaaf46454 100755
--- a/test/functional/feature_rbf.py
+++ b/test/functional/feature_rbf.py
@@ -26,7 +26,7 @@ from test_framework.script_util import (
DUMMY_2_P2WPKH_SCRIPT,
)
from test_framework.wallet import MiniWallet
-
+from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
MAX_REPLACEMENT_LIMIT = 100
class ReplaceByFeeTest(BitcoinTestFramework):
@@ -44,9 +44,6 @@ class ReplaceByFeeTest(BitcoinTestFramework):
]
self.supports_cli = False
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
# the pre-mined test framework chain contains coinbase outputs to the
@@ -531,9 +528,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert tx2b_txid in self.nodes[0].getrawmempool()
def test_rpc(self):
- us0 = self.nodes[0].listunspent()[0]
+ us0 = self.wallet.get_utxo()
ins = [us0]
- outs = {self.nodes[0].getnewaddress(): Decimal(1.0000000)}
+ outs = {ADDRESS_BCRT1_UNSPENDABLE: Decimal(1.0000000)}
rawtx0 = self.nodes[0].createrawtransaction(ins, outs, 0, True)
rawtx1 = self.nodes[0].createrawtransaction(ins, outs, 0, False)
json0 = self.nodes[0].decoderawtransaction(rawtx0)
@@ -541,14 +538,16 @@ class ReplaceByFeeTest(BitcoinTestFramework):
assert_equal(json0["vin"][0]["sequence"], 4294967293)
assert_equal(json1["vin"][0]["sequence"], 4294967295)
- rawtx2 = self.nodes[0].createrawtransaction([], outs)
- frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
- frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})
+ if self.is_wallet_compiled():
+ self.init_wallet(0)
+ rawtx2 = self.nodes[0].createrawtransaction([], outs)
+ frawtx2a = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": True})
+ frawtx2b = self.nodes[0].fundrawtransaction(rawtx2, {"replaceable": False})
- json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
- json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
- assert_equal(json0["vin"][0]["sequence"], 4294967293)
- assert_equal(json1["vin"][0]["sequence"], 4294967294)
+ json0 = self.nodes[0].decoderawtransaction(frawtx2a['hex'])
+ json1 = self.nodes[0].decoderawtransaction(frawtx2b['hex'])
+ assert_equal(json0["vin"][0]["sequence"], 4294967293)
+ assert_equal(json1["vin"][0]["sequence"], 4294967294)
def test_no_inherited_signaling(self):
confirmed_utxo = self.wallet.get_utxo()
diff --git a/test/functional/p2p_compactblocks_blocksonly.py b/test/functional/p2p_compactblocks_blocksonly.py
new file mode 100755
index 0000000000..4073ec03a6
--- /dev/null
+++ b/test/functional/p2p_compactblocks_blocksonly.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+# Copyright (c) 2021-2021 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 that a node in blocksonly mode does not request compact blocks."""
+
+from test_framework.messages import (
+ MSG_BLOCK,
+ MSG_CMPCT_BLOCK,
+ MSG_WITNESS_FLAG,
+ CBlock,
+ CBlockHeader,
+ CInv,
+ from_hex,
+ msg_block,
+ msg_getdata,
+ msg_headers,
+ msg_sendcmpct,
+)
+from test_framework.p2p import P2PInterface
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+
+class P2PCompactBlocksBlocksOnly(BitcoinTestFramework):
+ def set_test_params(self):
+ self.extra_args = [["-blocksonly"], [], [], []]
+ self.num_nodes = 4
+
+ def setup_network(self):
+ self.setup_nodes()
+ # Start network with everyone disconnected
+ self.sync_all()
+
+ def build_block_on_tip(self):
+ blockhash = self.nodes[2].generate(1)[0]
+ block_hex = self.nodes[2].getblock(blockhash=blockhash, verbosity=0)
+ block = from_hex(CBlock(), block_hex)
+ block.rehash()
+ return block
+
+ def run_test(self):
+ # Nodes will only request hb compact blocks mode when they're out of IBD
+ for node in self.nodes:
+ assert not node.getblockchaininfo()['initialblockdownload']
+
+ p2p_conn_blocksonly = self.nodes[0].add_p2p_connection(P2PInterface())
+ p2p_conn_high_bw = self.nodes[1].add_p2p_connection(P2PInterface())
+ p2p_conn_low_bw = self.nodes[3].add_p2p_connection(P2PInterface())
+ for conn in [p2p_conn_blocksonly, p2p_conn_high_bw, p2p_conn_low_bw]:
+ assert_equal(conn.message_count['sendcmpct'], 2)
+ conn.send_and_ping(msg_sendcmpct(announce=False, version=2))
+
+ # Nodes:
+ # 0 -> blocksonly
+ # 1 -> high bandwidth
+ # 2 -> miner
+ # 3 -> low bandwidth
+ #
+ # Topology:
+ # p2p_conn_blocksonly ---> node0
+ # p2p_conn_high_bw ---> node1
+ # p2p_conn_low_bw ---> node3
+ # node2 (no connections)
+ #
+ # node2 produces blocks that are passed to the rest of the nodes
+ # through the respective p2p connections.
+
+ self.log.info("Test that -blocksonly nodes do not select peers for BIP152 high bandwidth mode")
+
+ block0 = self.build_block_on_tip()
+
+ # A -blocksonly node should not request BIP152 high bandwidth mode upon
+ # receiving a new valid block at the tip.
+ p2p_conn_blocksonly.send_and_ping(msg_block(block0))
+ assert_equal(int(self.nodes[0].getbestblockhash(), 16), block0.sha256)
+ assert_equal(p2p_conn_blocksonly.message_count['sendcmpct'], 2)
+ assert_equal(p2p_conn_blocksonly.last_message['sendcmpct'].announce, False)
+
+ # A normal node participating in transaction relay should request BIP152
+ # high bandwidth mode upon receiving a new valid block at the tip.
+ p2p_conn_high_bw.send_and_ping(msg_block(block0))
+ assert_equal(int(self.nodes[1].getbestblockhash(), 16), block0.sha256)
+ p2p_conn_high_bw.wait_until(lambda: p2p_conn_high_bw.message_count['sendcmpct'] == 3)
+ assert_equal(p2p_conn_high_bw.last_message['sendcmpct'].announce, True)
+
+ # Don't send a block from the p2p_conn_low_bw so the low bandwidth node
+ # doesn't select it for BIP152 high bandwidth relay.
+ self.nodes[3].submitblock(block0.serialize().hex())
+
+ self.log.info("Test that -blocksonly nodes send getdata(BLOCK) instead"
+ " of getdata(CMPCT) in BIP152 low bandwidth mode")
+
+ block1 = self.build_block_on_tip()
+
+ p2p_conn_blocksonly.send_message(msg_headers(headers=[CBlockHeader(block1)]))
+ p2p_conn_blocksonly.sync_send_with_ping()
+ assert_equal(p2p_conn_blocksonly.last_message['getdata'].inv, [CInv(MSG_BLOCK | MSG_WITNESS_FLAG, block1.sha256)])
+
+ p2p_conn_high_bw.send_message(msg_headers(headers=[CBlockHeader(block1)]))
+ p2p_conn_high_bw.sync_send_with_ping()
+ assert_equal(p2p_conn_high_bw.last_message['getdata'].inv, [CInv(MSG_CMPCT_BLOCK, block1.sha256)])
+
+ self.log.info("Test that getdata(CMPCT) is still sent on BIP152 low bandwidth connections"
+ " when no -blocksonly nodes are involved")
+
+ p2p_conn_low_bw.send_and_ping(msg_headers(headers=[CBlockHeader(block1)]))
+ p2p_conn_low_bw.sync_with_ping()
+ assert_equal(p2p_conn_low_bw.last_message['getdata'].inv, [CInv(MSG_CMPCT_BLOCK, block1.sha256)])
+
+ self.log.info("Test that -blocksonly nodes still serve compact blocks")
+
+ def test_for_cmpctblock(block):
+ if 'cmpctblock' not in p2p_conn_blocksonly.last_message:
+ return False
+ return p2p_conn_blocksonly.last_message['cmpctblock'].header_and_shortids.header.rehash() == block.sha256
+
+ p2p_conn_blocksonly.send_message(msg_getdata([CInv(MSG_CMPCT_BLOCK, block0.sha256)]))
+ p2p_conn_blocksonly.wait_until(lambda: test_for_cmpctblock(block0))
+
+ # Request BIP152 high bandwidth mode from the -blocksonly node.
+ p2p_conn_blocksonly.send_and_ping(msg_sendcmpct(announce=True, version=2))
+
+ block2 = self.build_block_on_tip()
+ self.nodes[0].submitblock(block1.serialize().hex())
+ self.nodes[0].submitblock(block2.serialize().hex())
+ p2p_conn_blocksonly.wait_until(lambda: test_for_cmpctblock(block2))
+
+if __name__ == '__main__':
+ P2PCompactBlocksBlocksOnly().main()
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index cda0ae0eeb..3b01506986 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -8,6 +8,7 @@ from decimal import Decimal
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.util import (
assert_approx,
@@ -19,6 +20,7 @@ from test_framework.util import (
count_bytes,
find_vout_for_address,
)
+from test_framework.wallet_util import bytes_to_wif
def get_unspent(listunspent, amount):
@@ -132,6 +134,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.test_subtract_fee_with_presets()
self.test_transaction_too_large()
self.test_include_unsafe()
+ self.test_external_inputs()
self.test_22670()
def test_change_position(self):
@@ -983,6 +986,56 @@ class RawTransactionsTest(BitcoinTestFramework):
wallet.sendmany("", outputs)
self.generate(self.nodes[0], 10)
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):
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"])
signedtx = wallet.signrawtransactionwithwallet(fundedtx['hex'])
assert wallet.testmempoolaccept([signedtx['hex']])[0]["allowed"]
+ self.nodes[0].unloadwallet("unsafe")
def test_22670(self):
# In issue #22670, it was observed that ApproximateBestSubset may
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index f330bbf1c3..6b5b2c6a0f 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -8,6 +8,8 @@
from decimal import Decimal
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.util import (
assert_approx,
@@ -16,6 +18,7 @@ from test_framework.util import (
assert_raises_rpc_error,
find_output,
)
+from test_framework.wallet_util import bytes_to_wif
import json
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==')
+ # 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__':
PSBTTest().main()
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index f2637df35b..9f5bca6884 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -34,13 +34,14 @@ def assert_approx(v, vexp, vspan=0.00001):
raise AssertionError("%s > [%s..%s]" % (str(v), str(vexp - vspan), str(vexp + vspan)))
-def assert_fee_amount(fee, tx_size, fee_per_kB):
- """Assert the fee was in range"""
- target_fee = round(tx_size * fee_per_kB / 1000, 8)
+def assert_fee_amount(fee, tx_size, feerate_BTC_kvB):
+ """Assert the fee is in range."""
+ feerate_BTC_vB = feerate_BTC_kvB / 1000
+ target_fee = satoshi_round(tx_size * feerate_BTC_vB)
if fee < target_fee:
raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)" % (str(fee), str(target_fee)))
# allow the wallet's estimation to be at most 2 bytes off
- if fee > (tx_size + 2) * fee_per_kB / 1000:
+ if fee > (tx_size + 2) * feerate_BTC_vB:
raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)" % (str(fee), str(target_fee)))
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 2c2aaf6020..bb84962b75 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -98,6 +98,7 @@ BASE_SCRIPTS = [
'rpc_fundrawtransaction.py --legacy-wallet',
'rpc_fundrawtransaction.py --descriptors',
'p2p_compactblocks.py',
+ 'p2p_compactblocks_blocksonly.py',
'feature_segwit.py --legacy-wallet',
# vv Tests less than 2m vv
'wallet_basic.py --legacy-wallet',
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index f57f2a44bd..599e506f98 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -582,23 +582,17 @@ class WalletTest(BitcoinTestFramework):
assert label in self.nodes[0].listlabels()
self.nodes[0].rpc.ensure_ascii = True # restore to default
- # maintenance tests
- maintenance = [
- '-rescan',
- '-reindex',
- ]
+ # -reindex tests
chainlimit = 6
- for m in maintenance:
- self.log.info("Test " + m)
- self.stop_nodes()
- # set lower ancestor limit for later
- self.start_node(0, [m, "-limitancestorcount=" + str(chainlimit)])
- self.start_node(1, [m, "-limitancestorcount=" + str(chainlimit)])
- self.start_node(2, [m, "-limitancestorcount=" + str(chainlimit)])
- if m == '-reindex':
- # reindex will leave rpc warm up "early"; Wait for it to finish
- self.wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
- assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])
+ self.log.info("Test -reindex")
+ self.stop_nodes()
+ # set lower ancestor limit for later
+ self.start_node(0, ['-reindex', "-limitancestorcount=" + str(chainlimit)])
+ self.start_node(1, ['-reindex', "-limitancestorcount=" + str(chainlimit)])
+ self.start_node(2, ['-reindex', "-limitancestorcount=" + str(chainlimit)])
+ # reindex will leave rpc warm up "early"; Wait for it to finish
+ self.wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])
+ assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])
# Exercise listsinceblock with the last two blocks
coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0])
diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py
index 74f584f2cd..974ce7f381 100755
--- a/test/functional/wallet_hd.py
+++ b/test/functional/wallet_hd.py
@@ -103,7 +103,7 @@ class WalletHDTest(BitcoinTestFramework):
self.sync_all()
# Needs rescan
- self.restart_node(1, extra_args=self.extra_args[1] + ['-rescan'])
+ self.nodes[1].rescanblockchain()
assert_equal(self.nodes[1].getbalance(), NUM_HD_ADDS + 1)
# Try a RPC based rescan
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index aecdaf821f..7b23235945 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -9,6 +9,7 @@ from itertools import product
from test_framework.authproxy import JSONRPCException
from test_framework.descriptors import descsum_create
+from test_framework.key import ECKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -16,6 +17,7 @@ from test_framework.util import (
assert_greater_than,
assert_raises_rpc_error,
)
+from test_framework.wallet_util import bytes_to_wif
class WalletSendTest(BitcoinTestFramework):
def set_test_params(self):
@@ -35,7 +37,7 @@ class WalletSendTest(BitcoinTestFramework):
conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=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):
+ expect_error=None, solving_data=None):
assert (amount is None) != (data is None)
from_balance_before = from_wallet.getbalances()["mine"]["trusted"]
@@ -94,6 +96,8 @@ class WalletSendTest(BitcoinTestFramework):
options["replaceable"] = replaceable
if subtract_fee_from_outputs is not None:
options["subtract_fee_from_outputs"] = subtract_fee_from_outputs
+ if solving_data is not None:
+ options["solving_data"] = solving_data
if len(options.keys()) == 0:
options = None
@@ -476,6 +480,46 @@ class WalletSendTest(BitcoinTestFramework):
res = self.test_send(from_wallet=w5, to_wallet=w0, amount=1, include_unsafe=True)
assert res["complete"]
+ self.log.info("External outputs")
+ eckey = ECKey()
+ eckey.generate()
+ privkey = bytes_to_wif(eckey.get_bytes())
+
+ self.nodes[1].createwallet("extsend")
+ ext_wallet = self.nodes[1].get_wallet_rpc("extsend")
+ self.nodes[1].createwallet("extfund")
+ ext_fund = self.nodes[1].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 = ext_fund.importdescriptors([{"desc": desc, "timestamp": "now"}])
+ else:
+ res = ext_fund.importmulti([{"desc": desc, "timestamp": "now"}])
+ assert res[0]["success"]
+ addr = self.nodes[0].deriveaddresses(desc)[0]
+ addr_info = ext_fund.getaddressinfo(addr)
+
+ self.nodes[0].sendtoaddress(addr, 10)
+ self.nodes[0].sendtoaddress(ext_wallet.getnewaddress(), 10)
+ self.nodes[0].generate(6)
+ ext_utxo = ext_fund.listunspent(addresses=[addr])[0]
+
+ # An external input without solving data should result in an error
+ self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, expect_error=(-4, "Insufficient funds"))
+
+ # But funding should work when the solving data is provided
+ res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"]]})
+ signed = ext_wallet.walletprocesspsbt(res["psbt"])
+ signed = ext_fund.walletprocesspsbt(res["psbt"])
+ assert signed["complete"]
+ self.nodes[0].finalizepsbt(signed["psbt"])
+
+ res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"descriptors": [desc]})
+ signed = ext_wallet.walletprocesspsbt(res["psbt"])
+ signed = ext_fund.walletprocesspsbt(res["psbt"])
+ assert signed["complete"]
+ self.nodes[0].finalizepsbt(signed["psbt"])
if __name__ == '__main__':
WalletSendTest().main()