Merge pull request #5830 from SomberNight/20191209_wallet_perf

wallet perf: significant speedup for make_unsigned_transaction and rel.
This commit is contained in:
ThomasV 2019-12-09 17:30:10 +01:00 committed by GitHub
commit 0828454ef1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 15 deletions

View file

@ -369,7 +369,7 @@ class BaseWizard(Logger):
elif purpose == HWD_SETUP_DECRYPT_WALLET: elif purpose == HWD_SETUP_DECRYPT_WALLET:
derivation = get_derivation_used_for_hw_device_encryption() derivation = get_derivation_used_for_hw_device_encryption()
xpub = self.plugin.get_xpub(device_info.device.id_, derivation, 'standard', self) xpub = self.plugin.get_xpub(device_info.device.id_, derivation, 'standard', self)
password = keystore.Xpub.get_pubkey_from_xpub(xpub, ()) password = keystore.Xpub.get_pubkey_from_xpub(xpub, ()).hex()
try: try:
storage.decrypt(password) storage.decrypt(password)
except InvalidPassword: except InvalidPassword:

View file

@ -273,6 +273,7 @@ class BIP32Node(NamedTuple):
"""Returns the fingerprint of this node. """Returns the fingerprint of this node.
Note that self.fingerprint is of the *parent*. Note that self.fingerprint is of the *parent*.
""" """
# TODO cache this
return hash_160(self.eckey.get_public_key_bytes(compressed=True))[0:4] return hash_160(self.eckey.get_public_key_bytes(compressed=True))[0:4]

View file

@ -28,6 +28,7 @@ from unicodedata import normalize
import hashlib import hashlib
import re import re
from typing import Tuple, TYPE_CHECKING, Union, Sequence, Optional, Dict, List, NamedTuple from typing import Tuple, TYPE_CHECKING, Union, Sequence, Optional, Dict, List, NamedTuple
from functools import lru_cache
from . import bitcoin, ecc, constants, bip32 from . import bitcoin, ecc, constants, bip32
from .bitcoin import deserialize_privkey, serialize_privkey from .bitcoin import deserialize_privkey, serialize_privkey
@ -142,8 +143,8 @@ class KeyStore(Logger):
if not test_der_suffix_against_pubkey(der_suffix, pubkey): if not test_der_suffix_against_pubkey(der_suffix, pubkey):
der_suffix = None der_suffix = None
# try fp against our intermediate fingerprint # try fp against our intermediate fingerprint
if (der_suffix is None and hasattr(self, 'xpub') and if (der_suffix is None and hasattr(self, 'get_bip32_node_for_xpub') and
fp_found == BIP32Node.from_xkey(self.xpub).calc_fingerprint_of_this_node()): fp_found == self.get_bip32_node_for_xpub().calc_fingerprint_of_this_node()):
der_suffix = path_found der_suffix = path_found
if not test_der_suffix_against_pubkey(der_suffix, pubkey): if not test_der_suffix_against_pubkey(der_suffix, pubkey):
der_suffix = None der_suffix = None
@ -330,6 +331,7 @@ class Xpub:
self.xpub = None self.xpub = None
self.xpub_receive = None self.xpub_receive = None
self.xpub_change = None self.xpub_change = None
self._xpub_bip32_node = None # type: Optional[BIP32Node]
# "key origin" info (subclass should persist these): # "key origin" info (subclass should persist these):
self._derivation_prefix = derivation_prefix # type: Optional[str] self._derivation_prefix = derivation_prefix # type: Optional[str]
@ -338,6 +340,13 @@ class Xpub:
def get_master_public_key(self): def get_master_public_key(self):
return self.xpub return self.xpub
def get_bip32_node_for_xpub(self) -> Optional[BIP32Node]:
if self._xpub_bip32_node is None:
if self.xpub is None:
return None
self._xpub_bip32_node = BIP32Node.from_xkey(self.xpub)
return self._xpub_bip32_node
def get_derivation_prefix(self) -> Optional[str]: def get_derivation_prefix(self) -> Optional[str]:
"""Returns to bip32 path from some root node to self.xpub """Returns to bip32 path from some root node to self.xpub
Note that the return value might be None; if it is unknown. Note that the return value might be None; if it is unknown.
@ -366,7 +375,7 @@ class Xpub:
der_prefix_ints = convert_bip32_path_to_list_of_uint32(der_prefix_str) der_prefix_ints = convert_bip32_path_to_list_of_uint32(der_prefix_str)
else: else:
# use intermediate fp, and claim der suffix is the full path # use intermediate fp, and claim der suffix is the full path
fingerprint_bytes = BIP32Node.from_xkey(self.xpub).calc_fingerprint_of_this_node() fingerprint_bytes = self.get_bip32_node_for_xpub().calc_fingerprint_of_this_node()
der_prefix_ints = convert_bip32_path_to_list_of_uint32('m') der_prefix_ints = convert_bip32_path_to_list_of_uint32('m')
der_full = der_prefix_ints + list(der_suffix) der_full = der_prefix_ints + list(der_suffix)
return fingerprint_bytes, der_full return fingerprint_bytes, der_full
@ -375,7 +384,7 @@ class Xpub:
assert self.xpub assert self.xpub
fp_bytes, der_full = self.get_fp_and_derivation_to_be_used_in_partial_tx(der_suffix=[], fp_bytes, der_full = self.get_fp_and_derivation_to_be_used_in_partial_tx(der_suffix=[],
only_der_suffix=only_der_suffix) only_der_suffix=only_der_suffix)
bip32node = BIP32Node.from_xkey(self.xpub) bip32node = self.get_bip32_node_for_xpub()
depth = len(der_full) depth = len(der_full)
child_number_int = der_full[-1] if len(der_full) >= 1 else 0 child_number_int = der_full[-1] if len(der_full) >= 1 else 0
child_number_bytes = child_number_int.to_bytes(length=4, byteorder="big") child_number_bytes = child_number_int.to_bytes(length=4, byteorder="big")
@ -390,7 +399,7 @@ class Xpub:
# try to derive ourselves from what we were given # try to derive ourselves from what we were given
child_node1 = root_node.subkey_at_private_derivation(derivation_prefix) child_node1 = root_node.subkey_at_private_derivation(derivation_prefix)
child_pubkey_bytes1 = child_node1.eckey.get_public_key_bytes(compressed=True) child_pubkey_bytes1 = child_node1.eckey.get_public_key_bytes(compressed=True)
child_node2 = BIP32Node.from_xkey(self.xpub) child_node2 = self.get_bip32_node_for_xpub()
child_pubkey_bytes2 = child_node2.eckey.get_public_key_bytes(compressed=True) child_pubkey_bytes2 = child_node2.eckey.get_public_key_bytes(compressed=True)
if child_pubkey_bytes1 != child_pubkey_bytes2: if child_pubkey_bytes1 != child_pubkey_bytes2:
raise Exception("(xpub, derivation_prefix, root_node) inconsistency") raise Exception("(xpub, derivation_prefix, root_node) inconsistency")
@ -402,12 +411,12 @@ class Xpub:
self._root_fingerprint = root_fingerprint self._root_fingerprint = root_fingerprint
self._derivation_prefix = normalize_bip32_derivation(derivation_prefix) self._derivation_prefix = normalize_bip32_derivation(derivation_prefix)
def derive_pubkey(self, for_change, n) -> str: @lru_cache(maxsize=None)
for_change = int(for_change) def _derive_pubkey_bytes(self, for_change: int, n: int) -> bytes:
assert for_change in (0, 1) assert for_change in (0, 1)
xpub = self.xpub_change if for_change else self.xpub_receive xpub = self.xpub_change if for_change else self.xpub_receive
if xpub is None: if xpub is None:
rootnode = BIP32Node.from_xkey(self.xpub) rootnode = self.get_bip32_node_for_xpub()
xpub = rootnode.subkey_at_public_derivation((for_change,)).to_xpub() xpub = rootnode.subkey_at_public_derivation((for_change,)).to_xpub()
if for_change: if for_change:
self.xpub_change = xpub self.xpub_change = xpub
@ -415,10 +424,15 @@ class Xpub:
self.xpub_receive = xpub self.xpub_receive = xpub
return self.get_pubkey_from_xpub(xpub, (n,)) 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 @classmethod
def get_pubkey_from_xpub(self, xpub, sequence): def get_pubkey_from_xpub(self, xpub: str, sequence) -> bytes:
node = BIP32Node.from_xkey(xpub).subkey_at_public_derivation(sequence) node = BIP32Node.from_xkey(xpub).subkey_at_public_derivation(sequence)
return node.eckey.get_public_key_hex(compressed=True) return node.eckey.get_public_key_bytes(compressed=True)
class BIP32_KeyStore(Deterministic_KeyStore, Xpub): class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
@ -447,7 +461,7 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
def check_password(self, password): def check_password(self, password):
xprv = pw_decode(self.xprv, password, version=self.pw_hash_version) xprv = pw_decode(self.xprv, password, version=self.pw_hash_version)
if BIP32Node.from_xkey(xprv).chaincode != BIP32Node.from_xkey(self.xpub).chaincode: if BIP32Node.from_xkey(xprv).chaincode != self.get_bip32_node_for_xpub().chaincode:
raise InvalidPassword() raise InvalidPassword()
def update_password(self, old_password, new_password): def update_password(self, old_password, new_password):
@ -692,7 +706,7 @@ class Hardware_KeyStore(KeyStore, Xpub):
client = self.plugin.get_client(self) client = self.plugin.get_client(self)
derivation = get_derivation_used_for_hw_device_encryption() derivation = get_derivation_used_for_hw_device_encryption()
xpub = client.get_xpub(derivation, "standard") xpub = client.get_xpub(derivation, "standard")
password = self.get_pubkey_from_xpub(xpub, ()) password = self.get_pubkey_from_xpub(xpub, ()).hex()
return password return password
def has_usable_connection_with_device(self) -> bool: def has_usable_connection_with_device(self) -> bool:

View file

@ -178,7 +178,7 @@ def get_connected_hw_devices(plugins):
return devices return devices
def get_password_for_hw_device_encrypted_storage(plugins): def get_password_for_hw_device_encrypted_storage(plugins) -> str:
devices = get_connected_hw_devices(plugins) devices = get_connected_hw_devices(plugins)
if len(devices) == 0: if len(devices) == 0:
print_msg("Error: No connected hw device found. Cannot decrypt this wallet.") print_msg("Error: No connected hw device found. Cannot decrypt this wallet.")
@ -194,7 +194,7 @@ def get_password_for_hw_device_encrypted_storage(plugins):
xpub = plugin.get_xpub(device_info.device.id_, derivation, 'standard', plugin.handler) xpub = plugin.get_xpub(device_info.device.id_, derivation, 'standard', plugin.handler)
except UserCancelled: except UserCancelled:
sys.exit(0) sys.exit(0)
password = keystore.Xpub.get_pubkey_from_xpub(xpub, ()) password = keystore.Xpub.get_pubkey_from_xpub(xpub, ()).hex()
return password return password