aboutsummaryrefslogtreecommitdiff
path: root/test/functional
diff options
context:
space:
mode:
authorfanquake <fanquake@gmail.com>2023-10-11 12:37:16 +0200
committerfanquake <fanquake@gmail.com>2023-10-11 12:50:43 +0200
commit744157ef1a0b61ceb714cc27c9ae158907aecdc9 (patch)
treec20108a70ae9e19bd89d9a8be3329adf79fab08f /test/functional
parent154404e33fe53e06b13057df314ab1eb2d5ea0f5 (diff)
parent74c77825e5ab68bfa575dad86444506c43ef6c06 (diff)
Merge bitcoin/bitcoin#28602: descriptors: Disallow hybrid and uncompressed keys when inferring
74c77825e5ab68bfa575dad86444506c43ef6c06 test: Unit test for inferring scripts with hybrid and uncompressed keys (Andrew Chow) f895f97014ac5fac46d27725c1ea7decf7ff76d4 test: Scripts with hybrid pubkeys are migrated to watchonly (Andrew Chow) 37b9b734770e855b9beff3b5085125f1420dd072 descriptors: Move InferScript's pubkey validity checks to InferPubkey (Andrew Chow) b7485f11ab3a0f1860b261f222362af3301e0781 descriptors: Check result of InferPubkey (Andrew Chow) Pull request description: `InferDescriptor` was not always checking that the pubkey it was placing into the descriptor was an allowed pubkey. For example, given a P2WPKH script that uses an uncompressed pubkey, it would produce a `wpkh()` with the uncompressed key. Additionally, the hybrid key check was only being done for `pk()` scripts, where it should've been done for all scripts. This PR moves the key checking into `InferPubkey`. If the key is not valid for the context, then `nullptr` is returned and the inferring will fall through to the defaults of either `raw()` or `addr()`. This also resolves an issue with migrating legacy wallets that contain hybrid pubkeys as such watchonly scripts will become `raw()` or `addr()` and go to the watchonly wallet. Note that a legacy wallet cannot sign for hybrid pubkeys. A test has been added for the migration case. Also added unit tests for `InferDescriptor` itself as the edge cases with that function are not covered by the descriptor roundtrip test. ACKs for top commit: furszy: ACK 74c77825 Sjors: utACK 74c77825e5ab68bfa575dad86444506c43ef6c06 Tree-SHA512: ed5f63e42a2e46120245a6b0288b90d2a6912860814c6c08fe393332add1cb364dc5eca72f16980352143570aef0c07bf1a91acd294099463bd028b6ce2fe40c
Diffstat (limited to 'test/functional')
-rwxr-xr-xtest/functional/wallet_migration.py60
1 files changed, 59 insertions, 1 deletions
diff --git a/test/functional/wallet_migration.py b/test/functional/wallet_migration.py
index bcd71197bf..286dcb5fda 100755
--- a/test/functional/wallet_migration.py
+++ b/test/functional/wallet_migration.py
@@ -6,8 +6,13 @@
import random
import shutil
-from test_framework.address import script_to_p2sh
+from test_framework.address import (
+ script_to_p2sh,
+ key_to_p2pkh,
+ key_to_p2wpkh,
+)
from test_framework.descriptors import descsum_create
+from test_framework.key import ECPubKey
from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import COIN, CTransaction, CTxOut
from test_framework.script_util import key_to_p2pkh_script, script_to_p2sh_script, script_to_p2wsh_script
@@ -770,6 +775,58 @@ class WalletMigrationTest(BitcoinTestFramework):
wallet.unloadwallet()
+ def test_hybrid_pubkey(self):
+ self.log.info("Test migration when wallet contains a hybrid pubkey")
+
+ wallet = self.create_legacy_wallet("hybrid_keys")
+
+ # Get the hybrid pubkey for one of the keys in the wallet
+ normal_pubkey = wallet.getaddressinfo(wallet.getnewaddress())["pubkey"]
+ first_byte = bytes.fromhex(normal_pubkey)[0] + 4 # Get the hybrid pubkey first byte
+ parsed_pubkey = ECPubKey()
+ parsed_pubkey.set(bytes.fromhex(normal_pubkey))
+ parsed_pubkey.compressed = False
+ hybrid_pubkey_bytes = bytearray(parsed_pubkey.get_bytes())
+ hybrid_pubkey_bytes[0] = first_byte # Make it hybrid
+ hybrid_pubkey = hybrid_pubkey_bytes.hex()
+
+ # Import the hybrid pubkey
+ wallet.importpubkey(hybrid_pubkey)
+ p2pkh_addr = key_to_p2pkh(hybrid_pubkey)
+ p2pkh_addr_info = wallet.getaddressinfo(p2pkh_addr)
+ assert_equal(p2pkh_addr_info["iswatchonly"], True)
+ assert_equal(p2pkh_addr_info["ismine"], False) # Things involving hybrid pubkeys are not spendable
+
+ # Also import the p2wpkh for the pubkey to make sure we don't migrate it
+ p2wpkh_addr = key_to_p2wpkh(hybrid_pubkey)
+ wallet.importaddress(p2wpkh_addr)
+
+ migrate_info = wallet.migratewallet()
+
+ # Both addresses should only appear in the watchonly wallet
+ p2pkh_addr_info = wallet.getaddressinfo(p2pkh_addr)
+ assert_equal(p2pkh_addr_info["iswatchonly"], False)
+ assert_equal(p2pkh_addr_info["ismine"], False)
+ p2wpkh_addr_info = wallet.getaddressinfo(p2wpkh_addr)
+ assert_equal(p2wpkh_addr_info["iswatchonly"], False)
+ assert_equal(p2wpkh_addr_info["ismine"], False)
+
+ watchonly_wallet = self.nodes[0].get_wallet_rpc(migrate_info["watchonly_name"])
+ watchonly_p2pkh_addr_info = watchonly_wallet.getaddressinfo(p2pkh_addr)
+ assert_equal(watchonly_p2pkh_addr_info["iswatchonly"], False)
+ assert_equal(watchonly_p2pkh_addr_info["ismine"], True)
+ watchonly_p2wpkh_addr_info = watchonly_wallet.getaddressinfo(p2wpkh_addr)
+ assert_equal(watchonly_p2wpkh_addr_info["iswatchonly"], False)
+ assert_equal(watchonly_p2wpkh_addr_info["ismine"], True)
+
+ # There should only be raw or addr descriptors
+ for desc in watchonly_wallet.listdescriptors()["descriptors"]:
+ if desc["desc"].startswith("raw(") or desc["desc"].startswith("addr("):
+ continue
+ assert False, "Hybrid pubkey watchonly wallet has more than just raw() and addr()"
+
+ wallet.unloadwallet()
+
def run_test(self):
self.generate(self.nodes[0], 101)
@@ -787,6 +844,7 @@ class WalletMigrationTest(BitcoinTestFramework):
self.test_addressbook()
self.test_migrate_raw_p2sh()
self.test_conflict_txs()
+ self.test_hybrid_pubkey()
if __name__ == '__main__':
WalletMigrationTest().main()