1
0

hardware wallets: create base class for HW Clients. add some type hints

This commit is contained in:
SomberNight
2019-11-11 17:04:12 +01:00
parent 2fec17760d
commit f8c84fbb1e
10 changed files with 75 additions and 50 deletions

View File

@@ -18,7 +18,7 @@ from electrum.util import bfh, bh2u, versiontuple, UserFacingException
from electrum.base_wizard import ScriptTypeNotSupported
from electrum.logging import get_logger
from ..hw_wallet import HW_PluginBase
from ..hw_wallet import HW_PluginBase, HardwareClientBase
from ..hw_wallet.plugin import LibraryFoundButUnusable, only_hook_if_libraries_available
if TYPE_CHECKING:
@@ -60,9 +60,8 @@ except ImportError:
CKCC_SIMULATED_PID = CKCC_PID ^ 0x55aa
class CKCCClient:
# Challenge: I haven't found anywhere that defines a base class for this 'client',
# nor an API (interface) to be met. Winging it. Gets called from lib/plugins.py mostly?
class CKCCClient(HardwareClientBase):
def __init__(self, plugin, handler, dev_path, is_simulator=False):
self.device = plugin.device
@@ -463,11 +462,9 @@ class Coldcard_KeyStore(Hardware_KeyStore):
self.handler.show_error(exc)
class ColdcardPlugin(HW_PluginBase):
keystore_class = Coldcard_KeyStore
minimum_library = (0, 7, 7)
client = None
DEVICE_IDS = [
(COINKITE_VID, CKCC_PID),

View File

@@ -27,12 +27,13 @@ from electrum import constants
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput
from electrum.i18n import _
from electrum.keystore import Hardware_KeyStore
from ..hw_wallet import HW_PluginBase
from electrum.util import to_string, UserCancelled, UserFacingException, bfh
from electrum.base_wizard import ScriptTypeNotSupported, HWD_SETUP_NEW_WALLET
from electrum.network import Network
from electrum.logging import get_logger
from ..hw_wallet import HW_PluginBase, HardwareClientBase
_logger = get_logger(__name__)
@@ -63,7 +64,7 @@ MIN_MAJOR_VERSION = 5
ENCRYPTION_PRIVKEY_KEY = 'encryptionprivkey'
CHANNEL_ID_KEY = 'comserverchannelid'
class DigitalBitbox_Client():
class DigitalBitbox_Client(HardwareClientBase):
def __init__(self, plugin, hidDevice):
self.plugin = plugin

View File

@@ -1,2 +1,2 @@
from .plugin import HW_PluginBase
from .plugin import HW_PluginBase, HardwareClientBase
from .cmdline import CmdLineHandler

View File

@@ -24,9 +24,9 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from typing import TYPE_CHECKING, Dict, List, Union, Tuple, Sequence
from typing import TYPE_CHECKING, Dict, List, Union, Tuple, Sequence, Optional, Type
from electrum.plugin import BasePlugin, hook
from electrum.plugin import BasePlugin, hook, Device, DeviceMgr
from electrum.i18n import _
from electrum.bitcoin import is_address, TYPE_SCRIPT, opcodes
from electrum.util import bfh, versiontuple, UserFacingException
@@ -39,11 +39,7 @@ if TYPE_CHECKING:
class HW_PluginBase(BasePlugin):
# Derived classes provide:
#
# class-static variables: client_class, firmware_URL, handler_class,
# libraries_available, libraries_URL, minimum_firmware,
# wallet_class, ckd_public, types, HidTransport
keystore_class: Type['Hardware_KeyStore']
minimum_library = (0, )
@@ -56,11 +52,11 @@ class HW_PluginBase(BasePlugin):
def is_enabled(self):
return True
def device_manager(self):
def device_manager(self) -> 'DeviceMgr':
return self.parent.device_manager
@hook
def close_wallet(self, wallet):
def close_wallet(self, wallet: 'Abstract_Wallet'):
for keystore in wallet.get_keystores():
if isinstance(keystore, self.keystore_class):
self.device_manager().unpair_xpub(keystore.xpub)
@@ -141,6 +137,38 @@ class HW_PluginBase(BasePlugin):
def is_outdated_fw_ignored(self) -> bool:
return self._ignore_outdated_fw
def create_client(self, device: 'Device', handler) -> Optional['HardwareClientBase']:
raise NotImplementedError()
def get_xpub(self, device_id, derivation: str, xtype, wizard) -> str:
raise NotImplementedError()
class HardwareClientBase:
def is_pairable(self) -> bool:
raise NotImplementedError()
def close(self):
raise NotImplementedError()
def timeout(self, cutoff) -> None:
pass
def is_initialized(self) -> bool:
"""True if initialized, False if wiped."""
raise NotImplementedError()
def label(self) -> str:
"""The name given by the user to the device."""
raise NotImplementedError()
def has_usable_connection_with_device(self) -> bool:
raise NotImplementedError()
def get_xpub(self, bip32_path: str, xtype) -> str:
raise NotImplementedError()
def is_any_tx_output_on_change_branch(tx: PartialTransaction) -> bool:
return any([txout.is_change for txout in tx.outputs()])

View File

@@ -7,6 +7,7 @@ from electrum.util import UserCancelled
from electrum.keystore import bip39_normalize_passphrase
from electrum.bip32 import BIP32Node, convert_bip32_path_to_list_of_uint32
from electrum.logging import Logger
from electrum.plugins.hw_wallet.plugin import HardwareClientBase
class GuiMixin(object):
@@ -94,7 +95,7 @@ class GuiMixin(object):
return self.proto.CharacterAck(**char_info)
class KeepKeyClientBase(GuiMixin, Logger):
class KeepKeyClientBase(HardwareClientBase, GuiMixin, Logger):
def __init__(self, handler, plugin, proto):
assert hasattr(self, 'tx_api') # ProtocolMixin already constructed?
@@ -112,11 +113,9 @@ class KeepKeyClientBase(GuiMixin, Logger):
return "%s/%s" % (self.label(), self.features.device_id)
def label(self):
'''The name given by the user to the device.'''
return self.features.label
def is_initialized(self):
'''True if initialized, False if wiped.'''
return self.features.initialized
def is_pairable(self):

View File

@@ -16,7 +16,7 @@ from electrum.util import bfh, bh2u, versiontuple, UserFacingException
from electrum.base_wizard import ScriptTypeNotSupported
from electrum.logging import get_logger
from ..hw_wallet import HW_PluginBase
from ..hw_wallet import HW_PluginBase, HardwareClientBase
from ..hw_wallet.plugin import is_any_tx_output_on_change_branch
@@ -60,7 +60,7 @@ def test_pin_unlocked(func):
return catch_exception
class Ledger_Client():
class Ledger_Client(HardwareClientBase):
def __init__(self, hidDevice):
self.dongleObject = btchip(hidDevice)
self.preflightDone = False

View File

@@ -7,6 +7,7 @@ from electrum.util import UserCancelled
from electrum.keystore import bip39_normalize_passphrase
from electrum.bip32 import BIP32Node, convert_bip32_path_to_list_of_uint32
from electrum.logging import Logger
from electrum.plugins.hw_wallet.plugin import HardwareClientBase
class GuiMixin(object):
@@ -96,7 +97,7 @@ class GuiMixin(object):
return self.proto.WordAck(word=word)
class SafeTClientBase(GuiMixin, Logger):
class SafeTClientBase(HardwareClientBase, GuiMixin, Logger):
def __init__(self, handler, plugin, proto):
assert hasattr(self, 'tx_api') # ProtocolMixin already constructed?
@@ -114,11 +115,9 @@ class SafeTClientBase(GuiMixin, Logger):
return "%s/%s" % (self.label(), self.features.device_id)
def label(self):
'''The name given by the user to the device.'''
return self.features.label
def is_initialized(self):
'''True if initialized, False if wiped.'''
return self.features.initialized
def is_pairable(self):

View File

@@ -7,7 +7,7 @@ from electrum.util import UserCancelled, UserFacingException
from electrum.keystore import bip39_normalize_passphrase
from electrum.bip32 import BIP32Node, convert_bip32_path_to_list_of_uint32 as parse_path
from electrum.logging import Logger
from electrum.plugins.hw_wallet.plugin import OutdatedHwFirmwareException
from electrum.plugins.hw_wallet.plugin import OutdatedHwFirmwareException, HardwareClientBase
from trezorlib.client import TrezorClient
from trezorlib.exceptions import TrezorFailure, Cancelled, OutdatedFirmwareError
@@ -28,7 +28,7 @@ MESSAGES = {
}
class TrezorClientBase(Logger):
class TrezorClientBase(HardwareClientBase, Logger):
def __init__(self, transport, handler, plugin):
if plugin.is_outdated_fw_ignored():
TrezorClient.is_outdated = lambda *args, **kwargs: False
@@ -86,11 +86,9 @@ class TrezorClientBase(Logger):
return "%s/%s" % (self.label(), self.features.device_id)
def label(self):
'''The name given by the user to the device.'''
return self.features.label
def is_initialized(self):
'''True if initialized, False if wiped.'''
return self.features.initialized
def is_pairable(self):