update ledger plugin
This commit is contained in:
@@ -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']
|
||||||
|
|||||||
@@ -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):
|
||||||
class LedgerPlugin(HW_PluginBase):
|
# bip32_path is of the form 44'/0'/1'
|
||||||
libraries_available = BTCHIP
|
# S-L-O-W - we don't handle the fingerprint directly, so compute
|
||||||
keystore_class = Ledger_KeyStore
|
# it manually from the previous node
|
||||||
|
# This only happens once so it's bearable
|
||||||
def btchip_is_connected(self, keystore):
|
self.get_client() # prompt for the PIN before displaying the dialog if necessary
|
||||||
|
self.handler.show_message("Computing master public key")
|
||||||
try:
|
try:
|
||||||
keystore.get_client().getFirmwareVersion()
|
splitPath = bip32_path.split('/')
|
||||||
except Exception as e:
|
if splitPath[0] == 'm':
|
||||||
self.print_error("get_client", str(e))
|
splitPath = splitPath[1:]
|
||||||
return False
|
bip32_path = bip32_path[2:]
|
||||||
return True
|
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)
|
||||||
|
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user