66
77import random
88import shutil
9- from test_framework .address import script_to_p2sh
9+ from test_framework .address import (
10+ script_to_p2sh ,
11+ key_to_p2pkh ,
12+ key_to_p2wpkh ,
13+ )
1014from test_framework .descriptors import descsum_create
15+ from test_framework .key import ECPubKey
1116from test_framework .test_framework import BitcoinTestFramework
1217from test_framework .messages import COIN , CTransaction , CTxOut
1318from test_framework .script_util import key_to_p2pkh_script , script_to_p2sh_script , script_to_p2wsh_script
@@ -770,6 +775,58 @@ def test_conflict_txs(self):
770775
771776 wallet .unloadwallet ()
772777
778+ def test_hybrid_pubkey (self ):
779+ self .log .info ("Test migration when wallet contains a hybrid pubkey" )
780+
781+ wallet = self .create_legacy_wallet ("hybrid_keys" )
782+
783+ # Get the hybrid pubkey for one of the keys in the wallet
784+ normal_pubkey = wallet .getaddressinfo (wallet .getnewaddress ())["pubkey" ]
785+ first_byte = bytes .fromhex (normal_pubkey )[0 ] + 4 # Get the hybrid pubkey first byte
786+ parsed_pubkey = ECPubKey ()
787+ parsed_pubkey .set (bytes .fromhex (normal_pubkey ))
788+ parsed_pubkey .compressed = False
789+ hybrid_pubkey_bytes = bytearray (parsed_pubkey .get_bytes ())
790+ hybrid_pubkey_bytes [0 ] = first_byte # Make it hybrid
791+ hybrid_pubkey = hybrid_pubkey_bytes .hex ()
792+
793+ # Import the hybrid pubkey
794+ wallet .importpubkey (hybrid_pubkey )
795+ p2pkh_addr = key_to_p2pkh (hybrid_pubkey )
796+ p2pkh_addr_info = wallet .getaddressinfo (p2pkh_addr )
797+ assert_equal (p2pkh_addr_info ["iswatchonly" ], True )
798+ assert_equal (p2pkh_addr_info ["ismine" ], False ) # Things involving hybrid pubkeys are not spendable
799+
800+ # Also import the p2wpkh for the pubkey to make sure we don't migrate it
801+ p2wpkh_addr = key_to_p2wpkh (hybrid_pubkey )
802+ wallet .importaddress (p2wpkh_addr )
803+
804+ migrate_info = wallet .migratewallet ()
805+
806+ # Both addresses should only appear in the watchonly wallet
807+ p2pkh_addr_info = wallet .getaddressinfo (p2pkh_addr )
808+ assert_equal (p2pkh_addr_info ["iswatchonly" ], False )
809+ assert_equal (p2pkh_addr_info ["ismine" ], False )
810+ p2wpkh_addr_info = wallet .getaddressinfo (p2wpkh_addr )
811+ assert_equal (p2wpkh_addr_info ["iswatchonly" ], False )
812+ assert_equal (p2wpkh_addr_info ["ismine" ], False )
813+
814+ watchonly_wallet = self .nodes [0 ].get_wallet_rpc (migrate_info ["watchonly_name" ])
815+ watchonly_p2pkh_addr_info = watchonly_wallet .getaddressinfo (p2pkh_addr )
816+ assert_equal (watchonly_p2pkh_addr_info ["iswatchonly" ], False )
817+ assert_equal (watchonly_p2pkh_addr_info ["ismine" ], True )
818+ watchonly_p2wpkh_addr_info = watchonly_wallet .getaddressinfo (p2wpkh_addr )
819+ assert_equal (watchonly_p2wpkh_addr_info ["iswatchonly" ], False )
820+ assert_equal (watchonly_p2wpkh_addr_info ["ismine" ], True )
821+
822+ # There should only be raw or addr descriptors
823+ for desc in watchonly_wallet .listdescriptors ()["descriptors" ]:
824+ if desc ["desc" ].startswith ("raw(" ) or desc ["desc" ].startswith ("addr(" ):
825+ continue
826+ assert False , "Hybrid pubkey watchonly wallet has more than just raw() and addr()"
827+
828+ wallet .unloadwallet ()
829+
773830 def run_test (self ):
774831 self .generate (self .nodes [0 ], 101 )
775832
@@ -787,6 +844,7 @@ def run_test(self):
787844 self .test_addressbook ()
788845 self .test_migrate_raw_p2sh ()
789846 self .test_conflict_txs ()
847+ self .test_hybrid_pubkey ()
790848
791849if __name__ == '__main__' :
792850 WalletMigrationTest ().main ()
0 commit comments