diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -106,11 +106,11 @@ "rpc_deriveaddresses.py": [["--usecli"]], # FIXME: "rpc_psbt.py": [["--descriptors"]], "wallet_avoidreuse.py": [["--descriptors"]], - # FIXME: "wallet_basic.py": [["--descriptors"]], + "wallet_basic.py": [["--descriptors"]], "wallet_createwallet.py": [["--usecli"]], "wallet_encryption.py": [["--descriptors"]], "wallet_hd.py": [["--descriptors"]], - # FIXME: "wallet_keypool.py": [["--descriptors"]], + "wallet_keypool.py": [["--descriptors"]], "wallet_keypool_topup.py": [["--descriptors"]], "wallet_labels.py": [["--descriptors"]], "wallet_multiwallet.py": [["--usecli"]], diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -14,6 +14,7 @@ assert_raises_rpc_error, connect_nodes, count_bytes, + satoshi_round, ) from test_framework.wallet_util import test_address @@ -370,90 +371,92 @@ assert_raises_rpc_error(-1, "not an integer", self.nodes[0].generate, "2") - # This will raise an exception for the invalid private key format - assert_raises_rpc_error(-5, - "Invalid private key encoding", - self.nodes[0].importprivkey, - "invalid") - - # This will raise an exception for importing an address with the PS2H - # flag - temp_address = self.nodes[1].getnewaddress() - assert_raises_rpc_error(-5, - "Cannot use the p2sh flag with an address - use a script instead", - self.nodes[0].importaddress, - temp_address, - "label", - False, - True) - - # This will raise an exception for attempting to dump the private key - # of an address you do not own - assert_raises_rpc_error(-4, - "Private key for address", - self.nodes[0].dumpprivkey, - temp_address) - - # This will raise an exception for attempting to get the private key of - # an invalid Bitcoin address - assert_raises_rpc_error(-5, - "Invalid Bitcoin address", - self.nodes[0].dumpprivkey, - "invalid") - - # This will raise an exception for attempting to set a label for an - # invalid Bitcoin address - assert_raises_rpc_error(-5, - "Invalid Bitcoin address", - self.nodes[0].setlabel, - "invalid address", - "label") - - # This will raise an exception for importing an invalid address - assert_raises_rpc_error(-5, - "Invalid Bitcoin address or script", - self.nodes[0].importaddress, - "invalid") - - # This will raise an exception for attempting to import a pubkey that - # isn't in hex - assert_raises_rpc_error(-5, - "Pubkey must be a hex string", - self.nodes[0].importpubkey, - "not hex") - - # This will raise an exception for importing an invalid pubkey - assert_raises_rpc_error(-5, - "Pubkey is not a valid public key", - self.nodes[0].importpubkey, - "5361746f736869204e616b616d6f746f") - - # Import address and private key to check correct behavior of spendable unspents - # 1. Send some coins to generate new UTXO - address_to_import = self.nodes[2].getnewaddress() - txid = self.nodes[0].sendtoaddress(address_to_import, 1000000) - self.nodes[0].generate(1) - self.sync_all(self.nodes[0:3]) - - # 2. Import address from node2 to node1 - self.nodes[1].importaddress(address_to_import) - - # 3. Validate that the imported address is watch-only on node1 - assert self.nodes[1].getaddressinfo(address_to_import)["iswatchonly"] - - # 4. Check that the unspents after import are not spendable - assert_array_result(self.nodes[1].listunspent(), - {"address": address_to_import}, - {"spendable": False}) - - # 5. Import private key of the previously imported address on node1 - priv_key = self.nodes[2].dumpprivkey(address_to_import) - self.nodes[1].importprivkey(priv_key) - - # 6. Check that the unspents are now spendable on node1 - assert_array_result(self.nodes[1].listunspent(), - {"address": address_to_import}, - {"spendable": True}) + if not self.options.descriptors: + # This will raise an exception for the invalid private key format + assert_raises_rpc_error(-5, + "Invalid private key encoding", + self.nodes[0].importprivkey, + "invalid") + + # This will raise an exception for importing an address with the + # PS2H flag + temp_address = self.nodes[1].getnewaddress() + assert_raises_rpc_error(-5, + "Cannot use the p2sh flag with an address - use a script instead", + self.nodes[0].importaddress, + temp_address, + "label", + False, + True) + + # This will raise an exception for attempting to dump the private + # key of an address you do not own + assert_raises_rpc_error(-4, + "Private key for address", + self.nodes[0].dumpprivkey, + temp_address) + + # This will raise an exception for attempting to get the private key of + # an invalid Bitcoin address + assert_raises_rpc_error(-5, + "Invalid Bitcoin address", + self.nodes[0].dumpprivkey, + "invalid") + + # This will raise an exception for attempting to set a label for an + # invalid Bitcoin address + assert_raises_rpc_error(-5, + "Invalid Bitcoin address", + self.nodes[0].setlabel, + "invalid address", + "label") + + # This will raise an exception for importing an invalid address + assert_raises_rpc_error(-5, + "Invalid Bitcoin address or script", + self.nodes[0].importaddress, + "invalid") + + # This will raise an exception for attempting to import a pubkey that + # isn't in hex + assert_raises_rpc_error(-5, + "Pubkey must be a hex string", + self.nodes[0].importpubkey, + "not hex") + + # This will raise an exception for importing an invalid pubkey + assert_raises_rpc_error(-5, + "Pubkey is not a valid public key", + self.nodes[0].importpubkey, + "5361746f736869204e616b616d6f746f") + + # Import address and private key to check correct behavior of spendable unspents + # 1. Send some coins to generate new UTXO + address_to_import = self.nodes[2].getnewaddress() + txid = self.nodes[0].sendtoaddress(address_to_import, 1000000) + self.nodes[0].generate(1) + self.sync_all(self.nodes[0:3]) + + # 2. Import address from node2 to node1 + self.nodes[1].importaddress(address_to_import) + + # 3. Validate that the imported address is watch-only on node1 + assert self.nodes[1].getaddressinfo( + address_to_import)["iswatchonly"] + + # 4. Check that the unspents after import are not spendable + assert_array_result(self.nodes[1].listunspent(), + {"address": address_to_import}, + {"spendable": False}) + + # 5. Import private key of the previously imported address on node1 + priv_key = self.nodes[2].dumpprivkey(address_to_import) + self.nodes[1].importprivkey(priv_key) + + # 6. Check that the unspents are now spendable on node1 + assert_array_result(self.nodes[1].listunspent(), + {"address": address_to_import}, + {"spendable": True}) # Mine a block from node0 to an address from node1 coinbase_addr = self.nodes[1].getnewaddress() @@ -528,8 +531,10 @@ self.nodes[0].generate(1) node0_balance = self.nodes[0].getbalance() # Split into two chains - rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], { - chain_addrs[0]: node0_balance / 2 - Decimal('10000'), chain_addrs[1]: node0_balance / 2 - Decimal('10000')}) + amount = satoshi_round(node0_balance / 2 - Decimal('10000')) + rawtx = self.nodes[0].createrawtransaction( + [{"txid": singletxid, "vout": 0}], + {chain_addrs[0]: amount, chain_addrs[1]: amount}) signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) singletxid = self.nodes[0].sendrawtransaction( hexstring=signedtx["hex"], maxfeerate=0) @@ -562,9 +567,9 @@ # Double chain limit but require combining inputs, so we pass # SelectCoinsMinConf self.stop_node(0) - self.start_node(0, - self.extra_args[0] + ["-walletrejectlongchains", - "-limitancestorcount=" + str(2 * chainlimit)]) + extra_args = ["-acceptnonstdtxn=1", "-walletrejectlongchains", + "-limitancestorcount=" + str(2 * chainlimit)] + self.start_node(0, extra_args=extra_args) # wait until the wallet has submitted all transactions to the mempool self.wait_until( diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -23,18 +23,39 @@ addr_before_encrypting_data = nodes[ 0].getaddressinfo(addr_before_encrypting) wallet_info_old = nodes[0].getwalletinfo() - assert addr_before_encrypting_data[ - 'hdseedid'] == wallet_info_old['hdseedid'] + if not self.options.descriptors: + assert addr_before_encrypting_data['hdseedid'] == \ + wallet_info_old['hdseedid'] # Encrypt wallet and wait to terminate nodes[0].encryptwallet('test') + if self.options.descriptors: + # Import hardened derivation only descriptors + nodes[0].walletpassphrase('test', 10) + nodes[0].importdescriptors([ + { + "desc": "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#dfe4u060", + "timestamp": "now", + "range": [0, 0], + "active": True + }, + { + "desc": "pkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/2h/*h)#y9d74xe5", + "timestamp": "now", + "range": [0, 0], + "active": True, + "internal": True + }, + ]) + nodes[0].walletlock() # Keep creating keys addr = nodes[0].getnewaddress() addr_data = nodes[0].getaddressinfo(addr) wallet_info = nodes[0].getwalletinfo() - assert addr_before_encrypting_data[ - 'hdseedid'] != wallet_info['hdseedid'] - assert addr_data['hdseedid'] == wallet_info['hdseedid'] + assert addr_before_encrypting_data['hdmasterfingerprint'] != \ + addr_data['hdmasterfingerprint'] + if not self.options.descriptors: + assert addr_data['hdseedid'] == wallet_info['hdseedid'] assert_raises_rpc_error( -12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) @@ -44,6 +65,9 @@ nodes[0].keypoolrefill(6) nodes[0].walletlock() wi = nodes[0].getwalletinfo() + + # This would be 18 for Core if using --descriptors. But we don't have + # the BECH32 and P2SH_SEGWIT keypools. assert_equal(wi['keypoolsize_hd_internal'], 6) assert_equal(wi['keypoolsize'], 6) @@ -87,11 +111,15 @@ nodes[0].walletpassphrase('test', 100) nodes[0].keypoolrefill(100) wi = nodes[0].getwalletinfo() + + # This would be 300 for Core if using --descriptors. But we don't have + # the BECH32 and P2SH_SEGWIT keypools. assert_equal(wi['keypoolsize_hd_internal'], 100) assert_equal(wi['keypoolsize'], 100) # create a blank wallet - nodes[0].createwallet(wallet_name='w2', blank=True) + nodes[0].createwallet(wallet_name='w2', blank=True, + disable_private_keys=True) w2 = nodes[0].get_wallet_rpc('w2') # refer to initial wallet as w1 @@ -99,9 +127,12 @@ # import private key and fund it address = addr.pop() - privkey = w1.dumpprivkey(address) - res = w2.importmulti( - [{'scriptPubKey': {'address': address}, 'keys': [privkey], 'timestamp': 'now'}]) + desc = w1.getaddressinfo(address)['desc'] + self.log.info(desc) + if self.options.descriptors: + res = w2.importdescriptors([{'desc': desc, 'timestamp': 'now'}]) + else: + res = w2.importmulti([{'desc': desc, 'timestamp': 'now'}]) assert_equal(res[0]['success'], True) w1.walletpassphrase('test', 100) @@ -152,9 +183,9 @@ res = w2.walletcreatefundedpsbt( inputs=[], outputs=[{destination: 100.00}], - options={"subtractFeeFromOutputs": [0], "feeRate": 494.90}) + options={"subtractFeeFromOutputs": [0], "feeRate": 492.96}) assert_equal("psbt" in res, True) - assert_equal(res["fee"], Decimal("94.53")) + assert_equal(res["fee"], Decimal("94.54")) # creating a 10,000 sat transaction with a manual change address should # be possible