From 3d781a2d1b93d33b7232ba95b8f11e583e8ad436 Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Sun, 10 Jan 2016 19:53:28 +0900 Subject: [PATCH] BIP32_HD_Wallet: Fix address derivation Unfortunately there was root_name and root_derivation confusion in the past for classes derived from BIP_32_HD_Wallet. Address derivation used root_name and so would begin with 'x/' whereas it should have begun with root_derivation, and so started with 'm/'. This fixes that old wart and removes some fudges from the trezor code that used to work around it. --- lib/wallet.py | 20 +++++++------------- plugins/ledger/ledger.py | 4 ++++ plugins/trezor/client.py | 2 +- plugins/trezor/plugin.py | 1 - 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/wallet.py b/lib/wallet.py index 57132ee0..fa87376b 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -1668,19 +1668,15 @@ class BIP32_HD_Wallet(BIP32_Wallet): # drop unused master public key to avoid duplicate errors acc2 = storage.get('next_account2', None) if acc2: - storage.put('next_account2', None) self.master_public_keys.pop(self.root_name + acc2[0] + "'", None) - self.storage.put('master_public_keys', self.master_public_keys) + storage.put('next_account2', None) + storage.put('master_public_keys', self.master_public_keys) def next_account_number(self): assert (set(self.accounts.keys()) == set(['%d' % n for n in range(len(self.accounts))])) return len(self.accounts) - def next_derivation(self): - account_id = '%d' % self.next_account_number() - return self.root_name + account_id + "'", account_id - def show_account(self, account_id): return self.account_is_used(account_id) or account_id in self.labels @@ -1711,8 +1707,10 @@ class BIP32_HD_Wallet(BIP32_Wallet): self.create_next_account(password) def create_next_account(self, password, label=None): - derivation, account_id = self.next_derivation() - xpub, xprv = self.derive_xkeys(self.root_name, derivation, password) + account_id = '%d' % self.next_account_number() + derivation = self.account_derivation(account_id) + root_name = self.root_derivation.split('/')[0] # NOT self.root_name! + xpub, xprv = self.derive_xkeys(root_name, derivation, password) self.add_master_public_key(derivation, xpub) if xprv: self.add_master_private_key(derivation, xprv, password) @@ -1728,13 +1726,9 @@ class BIP32_HD_Wallet(BIP32_Wallet): def accounts_all_used(self): return all(self.account_is_used(acc_id) for acc_id in self.accounts) - @classmethod - def prefix(self): - return "/".join(self.root_derivation.split("/")[1:]) - @classmethod def account_derivation(self, account_id): - return self.prefix() + "/" + account_id + "'" + return self.root_derivation + "/" + account_id + "'" @classmethod def address_derivation(self, account_id, change, address_index): diff --git a/plugins/ledger/ledger.py b/plugins/ledger/ledger.py index e0ce5957..396c1b01 100644 --- a/plugins/ledger/ledger.py +++ b/plugins/ledger/ledger.py @@ -63,6 +63,10 @@ class BTChipWallet(BIP44_Wallet): assert not self.has_seed() return self.force_watching_only + def address_id(self, address): + # Strip the leading "m/" + return BIP44_Wallet.address_id(self, address)[2:] + def get_client(self, noPin=False): if not BTCHIP: self.give_error('please install github.com/btchip/btchip-python') diff --git a/plugins/trezor/client.py b/plugins/trezor/client.py index 2b6ca2cc..35d87950 100644 --- a/plugins/trezor/client.py +++ b/plugins/trezor/client.py @@ -98,7 +98,7 @@ def trezor_client_class(protocol_mixin, base_client, proto): '''Convert bip32 path to list of uint32 integers with prime flags 0/-1/1' -> [0, 0x80000001, 0x80000001]''' path = [] - for x in n.split('/'): + for x in n.split('/')[1:]: prime = 0 if x.endswith("'"): x = x.replace('\'', '') diff --git a/plugins/trezor/plugin.py b/plugins/trezor/plugin.py index cbbbbae4..c37d352a 100644 --- a/plugins/trezor/plugin.py +++ b/plugins/trezor/plugin.py @@ -100,7 +100,6 @@ class TrezorCompatibleWallet(BIP44_Wallet): # When creating a wallet we need to ask the device for the # master public key - derivation = derivation.replace(self.root_name, self.prefix() + "/") xpub = self.get_public_key(derivation) return xpub, None