Browse Source

update ledger plugin

master
ThomasV 9 years ago
parent
commit
9c7fd44b58
  1. 2
      plugins/ledger/__init__.py
  2. 110
      plugins/ledger/ledger.py
  3. 21
      plugins/ledger/qt.py

2
plugins/ledger/__init__.py

@ -3,5 +3,5 @@ from electrum.i18n import _
fullname = 'Ledger Wallet' fullname = 'Ledger Wallet'
description = 'Provides support for Ledger hardware wallet' description = 'Provides support for Ledger hardware wallet'
requires = [('btchip', 'github.com/ledgerhq/btchip-python')] requires = [('btchip', 'github.com/ledgerhq/btchip-python')]
registers_keystore = ('hardware', 'btchip', _("Ledger wallet")) registers_keystore = ('hardware', 'ledger', _("Ledger wallet"))
available_for = ['qt', 'cmdline'] available_for = ['qt', 'cmdline']

110
plugins/ledger/ledger.py

@ -27,26 +27,19 @@ except ImportError:
class Ledger_KeyStore(Hardware_KeyStore): class Ledger_KeyStore(Hardware_KeyStore):
wallet_type = 'btchip'
device = 'Ledger' device = 'Ledger'
def __init__(self): def __init__(self, d):
Hardware_KeyStore.__init__(self) Hardware_KeyStore.__init__(self, d)
# Errors and other user interaction is done through the wallet's # Errors and other user interaction is done through the wallet's
# handler. The handler is per-window and preserved across # handler. The handler is per-window and preserved across
# device reconnects # device reconnects
self.handler = None
self.force_watching_only = False self.force_watching_only = False
self.device_checked = False self.device_checked = False
self.signing = False self.signing = False
self.client = None
def get_derivation(self): def get_client(self):
return "m/44'/0'/%d'"%self.account_id return self.plugin.get_client()
def load(self, storage, name):
self.xpub = storage.get('master_public_keys', {}).get(name)
self.account_id = int(storage.get('account_id'))
def init_xpub(self): def init_xpub(self):
client = self.get_client() client = self.get_client()
@ -67,42 +60,6 @@ class Ledger_KeyStore(Hardware_KeyStore):
# Strip the leading "m/" # Strip the leading "m/"
return BIP32_HW_Wallet.address_id(self, address)[2:] return BIP32_HW_Wallet.address_id(self, address)[2:]
def get_public_key(self, bip32_path):
# bip32_path is of the form 44'/0'/1'
# S-L-O-W - we don't handle the fingerprint directly, so compute
# it manually from the previous node
# This only happens once so it's bearable
self.get_client() # prompt for the PIN before displaying the dialog if necessary
self.handler.show_message("Computing master public key")
try:
splitPath = bip32_path.split('/')
if splitPath[0] == 'm':
splitPath = splitPath[1:]
bip32_path = bip32_path[2:]
fingerprint = 0
if len(splitPath) > 1:
prevPath = "/".join(splitPath[0:len(splitPath) - 1])
nodeData = self.get_client().getWalletPublicKey(prevPath)
publicKey = compress_public_key(nodeData['publicKey'])
h = hashlib.new('ripemd160')
h.update(hashlib.sha256(publicKey).digest())
fingerprint = unpack(">I", h.digest()[0:4])[0]
nodeData = self.get_client().getWalletPublicKey(bip32_path)
publicKey = compress_public_key(nodeData['publicKey'])
depth = len(splitPath)
lastChild = splitPath[len(splitPath) - 1].split('\'')
if len(lastChild) == 1:
childnum = int(lastChild[0])
else:
childnum = 0x80000000 | int(lastChild[0])
xpub = "0488B21E".decode('hex') + chr(depth) + self.i4b(fingerprint) + self.i4b(childnum) + str(nodeData['chainCode']) + str(publicKey)
except Exception, e:
self.give_error(e, True)
finally:
self.handler.clear_dialog()
return EncodeBase58Check(xpub)
def decrypt_message(self, pubkey, message, password): def decrypt_message(self, pubkey, message, password):
self.give_error("Not supported") self.give_error("Not supported")
@ -318,6 +275,21 @@ class Ledger_KeyStore(Hardware_KeyStore):
return False, None, None return False, None, None
return True, response, response return True, response, response
class LedgerPlugin(HW_PluginBase):
libraries_available = BTCHIP
keystore_class = Ledger_KeyStore
hw_type='ledger'
client = None
def btchip_is_connected(self, keystore):
try:
self.get_client().getFirmwareVersion()
except Exception as e:
self.print_error("get_client", str(e))
return False
return True
def get_client(self, force_pair=True, noPin=False): def get_client(self, force_pair=True, noPin=False):
aborted = False aborted = False
client = self.client client = self.client
@ -389,15 +361,39 @@ class Ledger_KeyStore(Hardware_KeyStore):
return self.client return self.client
def get_public_key(self, bip32_path):
# bip32_path is of the form 44'/0'/1'
# S-L-O-W - we don't handle the fingerprint directly, so compute
# it manually from the previous node
# This only happens once so it's bearable
self.get_client() # prompt for the PIN before displaying the dialog if necessary
self.handler.show_message("Computing master public key")
try:
splitPath = bip32_path.split('/')
if splitPath[0] == 'm':
splitPath = splitPath[1:]
bip32_path = bip32_path[2:]
fingerprint = 0
if len(splitPath) > 1:
prevPath = "/".join(splitPath[0:len(splitPath) - 1])
nodeData = self.get_client().getWalletPublicKey(prevPath)
publicKey = compress_public_key(nodeData['publicKey'])
h = hashlib.new('ripemd160')
h.update(hashlib.sha256(publicKey).digest())
fingerprint = unpack(">I", h.digest()[0:4])[0]
nodeData = self.get_client().getWalletPublicKey(bip32_path)
publicKey = compress_public_key(nodeData['publicKey'])
depth = len(splitPath)
lastChild = splitPath[len(splitPath) - 1].split('\'')
if len(lastChild) == 1:
childnum = int(lastChild[0])
else:
childnum = 0x80000000 | int(lastChild[0])
xpub = "0488B21E".decode('hex') + chr(depth) + self.i4b(fingerprint) + self.i4b(childnum) + str(nodeData['chainCode']) + str(publicKey)
except Exception, e:
self.give_error(e, True)
finally:
self.handler.clear_dialog()
class LedgerPlugin(HW_PluginBase): return EncodeBase58Check(xpub)
libraries_available = BTCHIP
keystore_class = Ledger_KeyStore
def btchip_is_connected(self, keystore):
try:
keystore.get_client().getFirmwareVersion()
except Exception as e:
self.print_error("get_client", str(e))
return False
return True

21
plugins/ledger/qt.py

@ -25,12 +25,21 @@ class Plugin(LedgerPlugin):
window.show_error(_("Ledger device not detected.\nContinuing in watching-only mode.")) window.show_error(_("Ledger device not detected.\nContinuing in watching-only mode."))
wallet.force_watching_only = True wallet.force_watching_only = True
def on_create_wallet(self, keystore, wizard): def create_keystore(self, hw_type, derivation, wizard):
assert type(keystore) == self.keystore_class from electrum.keystore import hardware_keystore
keystore.handler = BTChipQTHandler(wizard) # create keystore
keystore.init_xpub() handler = BTChipQTHandler(wizard)
print keystore.xpub client = self.get_client()
wizard.create_wallet(keystore, None) xpub = self.get_public_key(derivation)
d = {
'xpub': self.xpub,
'type': 'hardware',
'hw_type': hw_type,
'derivation': derivation
}
k = hardware_keystore(hw_type, d)
return k
class BTChipQTHandler(QtHandlerBase): class BTChipQTHandler(QtHandlerBase):

Loading…
Cancel
Save