electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit fcd9752f190d02a04f31137a5910988de2405e68
parent ea62027599d6efaf6ac6fe3b963e04a833a9950b
Author: SomberNight <somber.night@protonmail.com>
Date:   Tue, 10 Dec 2019 20:41:47 +0100

keystore: change derive_pubkey API to return bytes

Diffstat:
Melectrum/keystore.py | 23+++++++++--------------
Melectrum/plugins/coldcard/coldcard.py | 8++++----
Melectrum/tests/test_wallet_vertical.py | 8++++----
Melectrum/wallet.py | 18+++++++++---------
4 files changed, 26 insertions(+), 31 deletions(-)

diff --git a/electrum/keystore.py b/electrum/keystore.py @@ -336,7 +336,7 @@ class MasterPublicKeyMixin(ABC): pass @abstractmethod - def derive_pubkey(self, for_change: int, n: int) -> str: + def derive_pubkey(self, for_change: int, n: int) -> bytes: pass def get_pubkey_derivation(self, pubkey: bytes, @@ -346,7 +346,7 @@ class MasterPublicKeyMixin(ABC): def test_der_suffix_against_pubkey(der_suffix: Sequence[int], pubkey: bytes) -> bool: if len(der_suffix) != 2: return False - if pubkey.hex() != self.derive_pubkey(*der_suffix): + if pubkey != self.derive_pubkey(*der_suffix): return False return True @@ -452,10 +452,9 @@ class Xpub(MasterPublicKeyMixin): self._root_fingerprint = root_fingerprint self._derivation_prefix = normalize_bip32_derivation(derivation_prefix) - # note: this helper method exists as derive_pubkey returns hex strings, - # and it saves space to cache bytes instead @lru_cache(maxsize=None) - def _derive_pubkey_bytes(self, for_change: int, n: int) -> bytes: + def derive_pubkey(self, for_change: int, n: int) -> bytes: + for_change = int(for_change) assert for_change in (0, 1) xpub = self.xpub_change if for_change else self.xpub_receive if xpub is None: @@ -467,11 +466,6 @@ class Xpub(MasterPublicKeyMixin): self.xpub_receive = xpub return self.get_pubkey_from_xpub(xpub, (n,)) - def derive_pubkey(self, for_change: int, n: int) -> str: - for_change = int(for_change) - assert for_change in (0, 1) - return self._derive_pubkey_bytes(for_change, n).hex() - @classmethod def get_pubkey_from_xpub(self, xpub: str, sequence) -> bytes: node = BIP32Node.from_xkey(xpub).subkey_at_public_derivation(sequence) @@ -618,13 +612,14 @@ class Old_KeyStore(MasterPublicKeyMixin, Deterministic_KeyStore): return string_to_number(sha256d(("%d:%d:"%(n, for_change)).encode('ascii') + bfh(mpk))) @classmethod - def get_pubkey_from_mpk(self, mpk, for_change, n): - z = self.get_sequence(mpk, for_change, n) + def get_pubkey_from_mpk(cls, mpk, for_change, n) -> bytes: + z = cls.get_sequence(mpk, for_change, n) master_public_key = ecc.ECPubkey(bfh('04'+mpk)) public_key = master_public_key + z*ecc.generator() - return public_key.get_public_key_hex(compressed=False) + return public_key.get_public_key_bytes(compressed=False) - def derive_pubkey(self, for_change, n) -> str: + @lru_cache(maxsize=None) + def derive_pubkey(self, for_change, n) -> bytes: for_change = int(for_change) assert for_change in (0, 1) return self.get_pubkey_from_mpk(self.mpk, for_change, n) diff --git a/electrum/plugins/coldcard/coldcard.py b/electrum/plugins/coldcard/coldcard.py @@ -610,15 +610,15 @@ class ColdcardPlugin(HW_PluginBase): # all those keys pubkey_deriv_info = wallet.get_public_keys_with_deriv_info(address) - pubkeys = sorted([pk for pk in list(pubkey_deriv_info)]) + pubkey_hexes = sorted([pk.hex() for pk in list(pubkey_deriv_info)]) xfp_paths = [] - for pubkey_hex in pubkey_deriv_info: - ks, der_suffix = pubkey_deriv_info[pubkey_hex] + for pubkey in pubkey_deriv_info: + ks, der_suffix = pubkey_deriv_info[pubkey] fp_bytes, der_full = ks.get_fp_and_derivation_to_be_used_in_partial_tx(der_suffix, only_der_suffix=False) xfp_int = xfp_int_from_xfp_bytes(fp_bytes) xfp_paths.append([xfp_int] + list(der_full)) - script = bfh(wallet.pubkeys_to_scriptcode(pubkeys)) + script = bfh(wallet.pubkeys_to_scriptcode(pubkey_hexes)) keystore.show_p2sh_address(wallet.m, script, xfp_paths, txin_type) diff --git a/electrum/tests/test_wallet_vertical.py b/electrum/tests/test_wallet_vertical.py @@ -415,8 +415,8 @@ class TestWalletKeystoreAddressIntegrityForMainnet(ElectrumTestCase): return ks ks = create_keystore_from_bip32seed(xtype='standard') - self.assertEqual('033a05ec7ae9a9833b0696eb285a762f17379fa208b3dc28df1c501cf84fe415d0', ks.derive_pubkey(0, 0)) - self.assertEqual('02bf27f41683d84183e4e930e66d64fc8af5508b4b5bf3c473c505e4dbddaeed80', ks.derive_pubkey(1, 0)) + self.assertEqual('033a05ec7ae9a9833b0696eb285a762f17379fa208b3dc28df1c501cf84fe415d0', ks.derive_pubkey(0, 0).hex()) + self.assertEqual('02bf27f41683d84183e4e930e66d64fc8af5508b4b5bf3c473c505e4dbddaeed80', ks.derive_pubkey(1, 0).hex()) ks = create_keystore_from_bip32seed(xtype='standard') # p2pkh w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) @@ -503,8 +503,8 @@ class TestWalletKeystoreAddressIntegrityForTestnet(TestCaseForTestnet): return ks ks = create_keystore_from_bip32seed(xtype='standard') - self.assertEqual('033a05ec7ae9a9833b0696eb285a762f17379fa208b3dc28df1c501cf84fe415d0', ks.derive_pubkey(0, 0)) - self.assertEqual('02bf27f41683d84183e4e930e66d64fc8af5508b4b5bf3c473c505e4dbddaeed80', ks.derive_pubkey(1, 0)) + self.assertEqual('033a05ec7ae9a9833b0696eb285a762f17379fa208b3dc28df1c501cf84fe415d0', ks.derive_pubkey(0, 0).hex()) + self.assertEqual('02bf27f41683d84183e4e930e66d64fc8af5508b4b5bf3c473c505e4dbddaeed80', ks.derive_pubkey(1, 0).hex()) ks = create_keystore_from_bip32seed(xtype='standard') # p2pkh w = WalletIntegrityHelper.create_standard_wallet(ks, config=self.config) diff --git a/electrum/wallet.py b/electrum/wallet.py @@ -454,8 +454,8 @@ class Abstract_Wallet(AddressSynchronizer, ABC): def get_public_keys(self, address: str) -> Sequence[str]: pass - def get_public_keys_with_deriv_info(self, address: str) -> Dict[str, Tuple[KeyStoreWithMPK, Sequence[int]]]: - """Returns a map: pubkey_hex -> (keystore, derivation_suffix)""" + def get_public_keys_with_deriv_info(self, address: str) -> Dict[bytes, Tuple[KeyStoreWithMPK, Sequence[int]]]: + """Returns a map: pubkey -> (keystore, derivation_suffix)""" return {} def get_tx_info(self, tx) -> TxWalletDetails: @@ -2152,12 +2152,12 @@ class Deterministic_Wallet(Abstract_Wallet): if not self.is_mine(address): return pubkey_deriv_info = self.get_public_keys_with_deriv_info(address) - txinout.pubkeys = sorted([bfh(pk) for pk in list(pubkey_deriv_info)]) - for pubkey_hex in pubkey_deriv_info: - ks, der_suffix = pubkey_deriv_info[pubkey_hex] + txinout.pubkeys = sorted([pk for pk in list(pubkey_deriv_info)]) + for pubkey in pubkey_deriv_info: + ks, der_suffix = pubkey_deriv_info[pubkey] fp_bytes, der_full = ks.get_fp_and_derivation_to_be_used_in_partial_tx(der_suffix, only_der_suffix=only_der_suffix) - txinout.bip32_paths[bfh(pubkey_hex)] = (fp_bytes, der_full) + txinout.bip32_paths[pubkey] = (fp_bytes, der_full) def create_new_address(self, for_change: bool = False): assert type(for_change) is bool @@ -2254,7 +2254,7 @@ class Simple_Deterministic_Wallet(Simple_Wallet, Deterministic_Wallet): return self.keystore.get_master_public_key() def derive_pubkeys(self, c, i): - return [self.keystore.derive_pubkey(c, i)] + return [self.keystore.derive_pubkey(c, i).hex()] @@ -2278,7 +2278,7 @@ class Multisig_Wallet(Deterministic_Wallet): Deterministic_Wallet.__init__(self, storage, config=config) def get_public_keys(self, address): - return list(self.get_public_keys_with_deriv_info(address)) + return [pk.hex() for pk in self.get_public_keys_with_deriv_info(address)] def pubkeys_to_address(self, pubkeys): redeem_script = self.pubkeys_to_scriptcode(pubkeys) @@ -2310,7 +2310,7 @@ class Multisig_Wallet(Deterministic_Wallet): raise UnknownTxinType(f'unexpected txin_type {txin_type}') def derive_pubkeys(self, c, i): - return [k.derive_pubkey(c, i) for k in self.get_keystores()] + return [k.derive_pubkey(c, i).hex() for k in self.get_keystores()] def load_keystore(self): self.keystores = {}