URI: 
       tbip44 - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit f4b390a79f127d0bbe5c178bd849ec87b85f1a5a
   DIR parent 1d4631d6470930224e407bffae884a6b4edc514f
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Wed, 13 Aug 2014 16:05:43 +0200
       
       bip44
       
       Diffstat:
         M electrum                            |       5 ++---
         M gui/qt/installwizard.py             |      42 ++++++++++++++++----------------
         M lib/version.py                      |       7 +++++--
         M lib/wallet.py                       |     188 +++++++++++++++----------------
       
       4 files changed, 121 insertions(+), 121 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -87,7 +87,6 @@ def arg_parser():
            parser.add_option("-G", "--gap", dest="gap_limit", default=None, help="gap limit")
            parser.add_option("-W", "--password", dest="password", default=None, help="set password for usage with commands (currently only implemented for create command, do not use it for longrunning gui session since the password is visible in /proc)")
            parser.add_option("-1", "--oneserver", action="store_true", dest="oneserver", default=False, help="connect to one server only")
       -    parser.add_option("--bip32", action="store_true", dest="bip32", default=False, help="bip32 (not final)")
            parser.add_option("--2of3", action="store_true", dest="2of3", default=False, help="create 2of3 wallet")
            parser.add_option("--mpk", dest="mpk", default=False, help="restore from master public key")
            parser.add_option("-m", action="store_true", dest="hide_gui", default=False, help="hide GUI on startup")
       t@@ -288,7 +287,7 @@ if __name__ == '__main__':
                            sys.exit("Error: Invalid seed")
                        wallet = Wallet.from_seed(seed, storage)
                        wallet.add_seed(seed, password)
       -                wallet.create_accounts(password)
       +                wallet.create_main_account(password)
        
                    if not options.offline:
                        network = Network(config)
       t@@ -309,7 +308,7 @@ if __name__ == '__main__':
                        wallet = Wallet(storage)
                        seed = wallet.make_seed()
                        wallet.add_seed(seed, password)
       -                wallet.create_accounts(password)
       +                wallet.create_main_account(password)
                        wallet.synchronize()
                        print_msg("Your wallet generation seed is:\n\"%s\"" % seed)
                        print_msg("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.")
   DIR diff --git a/gui/qt/installwizard.py b/gui/qt/installwizard.py
       t@@ -404,25 +404,25 @@ class InstallWizard(QDialog):
                        wallet.add_seed(seed, password)
        
                    elif action == 'add_cosigner':
       -                xpub_hot = wallet.master_public_keys.get("m/")
       -                r = self.multi_mpk_dialog(xpub_hot, 1)
       +                xpub1 = wallet.master_public_keys.get("x1/")
       +                r = self.multi_mpk_dialog(xpub1, 1)
                        if not r:
                            return
       -                xpub_cold = r[0]
       -                wallet.add_master_public_key("cold/", xpub_cold)
       +                xpub2 = r[0]
       +                wallet.add_master_public_key("x2/", xpub2)
        
                    elif action == 'add_two_cosigners':
       -                xpub_hot = wallet.master_public_keys.get("m/")
       -                r = self.multi_mpk_dialog(xpub_hot, 2)
       +                xpub1 = wallet.master_public_keys.get("x1/")
       +                r = self.multi_mpk_dialog(xpub1, 2)
                        if not r:
                            return
       -                xpub1, xpub2 = r
       -                wallet.add_master_public_key("cold/", xpub1)
       -                wallet.add_master_public_key("remote/", xpub2)
       +                xpub2, xpub3 = r
       +                wallet.add_master_public_key("x2/", xpub2)
       +                wallet.add_master_public_key("x3/", xpub3)
        
                    elif action == 'create_accounts':
                        try:
       -                    wallet.create_accounts(password)
       +                    wallet.create_main_account(password)
                        except BaseException as e:
                            QMessageBox.information(None, _('Error'), str(e), _('OK'))
                            return
       t@@ -476,7 +476,7 @@ class InstallWizard(QDialog):
                            password = self.password_dialog()
                            wallet = Wallet.from_seed(text, self.storage)
                            wallet.add_seed(text, password)
       -                    wallet.create_accounts(password)
       +                    wallet.create_main_account(password)
                        elif Wallet.is_xprv(text):
                            password = self.password_dialog()
                            wallet = Wallet.from_xprv(text, password, self.storage)
       t@@ -507,17 +507,17 @@ class InstallWizard(QDialog):
                            if Wallet.is_seed(text2):
                                wallet.add_cold_seed(text2, password)
                            else:
       -                        wallet.add_master_public_key("cold/", text2)
       +                        wallet.add_master_public_key("x2/", text2)
                        else:
                            assert Wallet.is_xpub(text1)
                            if Wallet.is_seed(text2):
                                wallet.add_seed(text2, password)
       -                        wallet.add_master_public_key("cold/", text1)
       +                        wallet.add_master_public_key("x2/", text1)
                            else:
       -                        wallet.add_master_public_key("m/", text1)
       -                        wallet.add_master_public_key("cold/", text2)
       +                        wallet.add_master_public_key("x1/", text1)
       +                        wallet.add_master_public_key("x2/", text2)
        
       -                wallet.create_accounts(password)
       +                wallet.create_main_account(password)
        
        
                    elif t in ['2of3']:
       t@@ -536,17 +536,17 @@ class InstallWizard(QDialog):
                            if Wallet.is_seed(text2):
                                wallet.add_cold_seed(text2, password)
                            else:
       -                        wallet.add_master_public_key("cold/", text2)
       +                        wallet.add_master_public_key("x2/", text2)
        
                        elif Wallet.is_xpub(text1):
                            if Wallet.is_seed(text2):
                                wallet.add_seed(text2, password)
       -                        wallet.add_master_public_key("cold/", text1)
       +                        wallet.add_master_public_key("x2/", text1)
                            else:
       -                        wallet.add_master_public_key("m/", text1)
       -                        wallet.add_master_public_key("cold/", text2)
       +                        wallet.add_master_public_key("x1/", text1)
       +                        wallet.add_master_public_key("x2/", text2)
        
       -                wallet.create_accounts(password)
       +                wallet.create_main_account(password)
        
                    else:
                        wallet = run_hook('installwizard_restore', self, self.storage)
   DIR diff --git a/lib/version.py b/lib/version.py
       t@@ -1,5 +1,8 @@
        ELECTRUM_VERSION = "1.9.8"  # version of the client package
        PROTOCOL_VERSION = '0.9'    # protocol version requested
       -NEW_SEED_VERSION = 7        # bip32 wallets
       +NEW_SEED_VERSION = 8        # bip32 wallets
        OLD_SEED_VERSION = 4        # old electrum deterministic generation
       -SEED_PREFIX      = '01'     # the hash of the mnemonic seed must begin with this
       +
       +
       +# The hash of the mnemonic seed must begin with this
       +SEED_PREFIX      = '01'     # for BIP44
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -1204,68 +1204,58 @@ class Deterministic_Wallet(Abstract_Wallet):
                if not self.accounts:
                    return 'create_accounts'
        
       +    def get_master_public_keys(self):
       +        out = {}
       +        for k, account in self.accounts.items():
       +            name = self.get_account_name(k)
       +            mpk_text = '\n\n'.join( account.get_master_pubkeys() )
       +            out[name] = mpk_text
       +        return out
        
        
        class BIP32_Wallet(Deterministic_Wallet):
       -    # bip32 derivation
       +    # Wallet with a single BIP32 account, no seed
       +    # gap limit 20
        
            def __init__(self, storage):
                Deterministic_Wallet.__init__(self, storage)
                self.master_public_keys  = storage.get('master_public_keys', {})
                self.master_private_keys = storage.get('master_private_keys', {})
       +        self.gap_limit = 20
        
            def default_account(self):
       -        return self.accounts["m/0'"]
       +        return self.accounts['0']
        
            def is_watching_only(self):
                return not bool(self.master_private_keys)
        
       -    def can_create_accounts(self):
       -        return 'm/' in self.master_private_keys.keys()
       -
            def get_master_public_key(self):
       -        return self.master_public_keys.get("m/")
       -
       -    def get_master_public_keys(self):
       -        out = {}
       -        for k, account in self.accounts.items():
       -            name = self.get_account_name(k)
       -            mpk_text = '\n\n'.join( account.get_master_pubkeys() )
       -            out[name] = mpk_text
       -        return out
       +        return self.master_public_keys.get(self.root_name)
        
            def get_master_private_key(self, account, password):
                k = self.master_private_keys.get(account)
                if not k: return
       -        xpriv = pw_decode( k, password)
       -        return xpriv
       +        xprv = pw_decode(k, password)
       +        return xprv
        
            def check_password(self, password):
       -        xpriv = self.get_master_private_key( "m/", password )
       -        xpub = self.master_public_keys["m/"]
       +        xpriv = self.get_master_private_key(self.root_name, password)
       +        xpub = self.master_public_keys[self.root_name]
                assert deserialize_xkey(xpriv)[3] == deserialize_xkey(xpub)[3]
        
            def create_xprv_wallet(self, xprv, password):
                xpub = bitcoin.xpub_from_xprv(xprv)
                account = BIP32_Account({'xpub':xpub})
       -        account_id = 'm/' + bitcoin.get_xkey_name(xpub)
                self.storage.put('seed_version', self.seed_version, True)
       -        self.add_master_private_key(account_id, xprv, password)
       -        self.add_master_public_key(account_id, xpub)
       -        self.add_account(account_id, account)
       +        self.add_master_private_key(self.root_name, xprv, password)
       +        self.add_master_public_key(self.root_name, xpub)
       +        self.add_account('0', account)
        
            def create_xpub_wallet(self, xpub):
                account = BIP32_Account({'xpub':xpub})
       -        account_id = 'm/' + bitcoin.get_xkey_name(xpub)
                self.storage.put('seed_version', self.seed_version, True)
       -        self.add_master_public_key(account_id, xpub)
       -        self.add_account(account_id, account)
       -
       -    def create_accounts(self, password):
       -        # First check the password is valid (this raises if it isn't).
       -        if not self.is_watching_only():
       -            self.check_password(password)
       -        self.create_account('Main account', password)
       +        self.add_master_public_key(self.root_name, xpub)
       +        self.add_account('0', account)
        
            def add_master_public_key(self, name, xpub):
                self.master_public_keys[name] = xpub
       t@@ -1275,6 +1265,19 @@ class BIP32_Wallet(Deterministic_Wallet):
                self.master_private_keys[name] = pw_encode(xpriv, password)
                self.storage.put('master_private_keys', self.master_private_keys, True)
        
       +    def add_master_keys(self, root, derivation, password):
       +        x = self.master_private_keys.get(root)
       +        if x:
       +            master_xpriv = pw_decode(x, password )
       +            xpriv, xpub = bip32_private_derivation(master_xpriv, root, derivation)
       +            self.add_master_public_key(derivation, xpub)
       +            self.add_master_private_key(derivation, xpriv, password)
       +        else:
       +            master_xpub = self.master_public_keys[root]
       +            xpub = bip32_public_derivation(master_xpub, root, derivation)
       +            self.add_master_public_key(derivation, xpub)
       +        return xpub
       +
            def can_sign(self, tx):
                if self.is_watching_only():
                    return False
       t@@ -1292,11 +1295,19 @@ class BIP32_Wallet(Deterministic_Wallet):
        
        
        class BIP32_HD_Wallet(BIP32_Wallet):
       -    # sequence of accounts
       +    # wallet that can create accounts
       +
       +    def create_main_account(self, password):
       +        # First check the password is valid (this raises if it isn't).
       +        if not self.is_watching_only():
       +            self.check_password(password)
       +        self.create_account('Main account', password)
       +
       +    def can_create_accounts(self):
       +        return self.root_name in self.master_private_keys.keys()
        
            def create_account(self, name, password):
       -        i = self.num_accounts()
       -        account_id = self.account_id(i)
       +        account_id = "%d"%self.num_accounts()
                account = self.make_account(account_id, password)
                self.add_account(account_id, account)
                if name:
       t@@ -1328,8 +1339,7 @@ class BIP32_HD_Wallet(BIP32_Wallet):
                        self.next_addresses.pop(account_id)
        
            def next_account_address(self, password):
       -        i = self.num_accounts()
       -        account_id = self.account_id(i)
       +        account_id = '%d'%self.num_accounts()
                addr = self.next_addresses.get(account_id)
                if not addr:
                    account = self.make_account(account_id, password)
       t@@ -1338,12 +1348,10 @@ class BIP32_HD_Wallet(BIP32_Wallet):
                    self.storage.put('next_addresses', self.next_addresses)
                return account_id, addr
        
       -    def account_id(self, i):
       -        return "m/%d'"%i
       -
            def make_account(self, account_id, password):
                """Creates and saves the master keys, but does not save the account"""
       -        xpub = self.add_master_keys("m/", account_id, password)
       +        derivation = self.root_name + "%d'"%int(account_id)
       +        xpub = self.add_master_keys(self.root_name, derivation, password)
                account = BIP32_Account({'xpub':xpub})
                return account
        
       t@@ -1355,29 +1363,23 @@ class BIP32_HD_Wallet(BIP32_Wallet):
                    keys.append(k)
                i = 0
                while True:
       -            account_id = self.account_id(i)
       -            if account_id not in keys: break
       +            account_id = '%d'%i
       +            if account_id not in keys:
       +                break
                    i += 1
                return i
        
       -    def add_master_keys(self, root, account_id, password):
       -        x = self.master_private_keys.get(root)
       -        if x:
       -            master_xpriv = pw_decode(x, password )
       -            xpriv, xpub = bip32_private_derivation(master_xpriv, root, account_id)
       -            self.add_master_public_key(account_id, xpub)
       -            self.add_master_private_key(account_id, xpriv, password)
       -        else:
       -            master_xpub = self.master_public_keys[root]
       -            xpub = bip32_public_derivation(master_xpub, root, account_id)
       -            self.add_master_public_key(account_id, xpub)
       -        return xpub
       -
       -
        
       -class NewWallet(BIP32_HD_Wallet):
       +class BIP39_Wallet(BIP32_Wallet):
            # BIP39 seed generation
        
       +    def create_master_keys(self, password):
       +        seed = self.get_seed(password)
       +        xprv, xpub = bip32_root(seed)
       +        xprv, xpub = bip32_private_derivation(xprv, "m/", self.root_derivation)
       +        self.add_master_public_key(self.root_name, xpub)
       +        self.add_master_private_key(self.root_name, xprv, password)
       +
            @classmethod
            def make_seed(self, custom_entropy=1):
                import mnemonic
       t@@ -1405,45 +1407,41 @@ class NewWallet(BIP32_HD_Wallet):
                import unicodedata
                return NEW_SEED_VERSION, unicodedata.normalize('NFC', unicode(seed.strip()))
        
       -    def create_master_keys(self, password):
       -        seed = self.get_seed(password)
       -        xpriv, xpub = bip32_root(seed)
       -        self.add_master_public_key("m/", xpub)
       -        self.add_master_private_key("m/", xpriv, password)
        
        
       +class NewWallet(BIP32_HD_Wallet, BIP39_Wallet):
       +    # bip 44
       +    root_name = 'root/'
       +    root_derivation = "m/44'/0'"
        
        
       -class Wallet_2of2(NewWallet):
       -    """ This class is used for multisignature addresses"""
       +class Wallet_2of2(BIP39_Wallet):
       +    # Wallet with multisig addresses. 
       +    # Cannot create accounts
       +    root_name = "x1/"
       +    root_derivation = "m/44'/0'"
        
            def __init__(self, storage):
       -        NewWallet.__init__(self, storage)
       +        BIP39_Wallet.__init__(self, storage)
                self.storage.put('wallet_type', '2of2', True)
        
       -    def default_account(self):
       -        return self.accounts['m/']
       -
       -    def can_create_accounts(self):
       -        return False
       -
            def can_import(self):
                return False
        
       -    def create_account(self, name, password):
       -        xpub1 = self.master_public_keys.get("m/")
       -        xpub2 = self.master_public_keys.get("cold/")
       +    def create_main_account(self, password):
       +        xpub1 = self.master_public_keys.get("x1/")
       +        xpub2 = self.master_public_keys.get("x2/")
                account = BIP32_Account_2of2({'xpub':xpub1, 'xpub2':xpub2})
       -        self.add_account('m/', account)
       +        self.add_account('0', account)
        
            def get_master_public_keys(self):
       -        xpub1 = self.master_public_keys.get("m/")
       -        xpub2 = self.master_public_keys.get("cold/")
       -        return {'hot':xpub1, 'cold':xpub2}
       +        xpub1 = self.master_public_keys.get("x1/")
       +        xpub2 = self.master_public_keys.get("x2/")
       +        return {'x1':xpub1, 'x2':xpub2}
        
            def get_action(self):
       -        xpub1 = self.master_public_keys.get("m/")
       -        xpub2 = self.master_public_keys.get("cold/")
       +        xpub1 = self.master_public_keys.get("x1/")
       +        xpub2 = self.master_public_keys.get("x2/")
                if xpub1 is None:
                    return 'create_seed'
                if xpub2 is None:
       t@@ -1459,23 +1457,23 @@ class Wallet_2of3(Wallet_2of2):
                Wallet_2of2.__init__(self, storage)
                self.storage.put('wallet_type', '2of3', True)
        
       -    def create_account(self, name, password):
       -        xpub1 = self.master_public_keys.get("m/")
       -        xpub2 = self.master_public_keys.get("cold/")
       -        xpub3 = self.master_public_keys.get("remote/")
       +    def create_main_account(self, password):
       +        xpub1 = self.master_public_keys.get("x1/")
       +        xpub2 = self.master_public_keys.get("x2/")
       +        xpub3 = self.master_public_keys.get("x3/")
                account = BIP32_Account_2of3({'xpub':xpub1, 'xpub2':xpub2, 'xpub3':xpub3})
       -        self.add_account('m/', account)
       +        self.add_account('0', account)
        
            def get_master_public_keys(self):
       -        xpub1 = self.master_public_keys.get("m/")
       -        xpub2 = self.master_public_keys.get("cold/")
       -        xpub3 = self.master_public_keys.get("remote/")
       -        return {'hot':xpub1, 'cold':xpub2, 'remote':xpub3}
       +        xpub1 = self.master_public_keys.get("x1/")
       +        xpub2 = self.master_public_keys.get("x2/")
       +        xpub3 = self.master_public_keys.get("x3/")
       +        return {'x1':xpub1, 'x2':xpub2, 'x3':xpub3}
        
            def get_action(self):
       -        xpub1 = self.master_public_keys.get("m/")
       -        xpub2 = self.master_public_keys.get("cold/")
       -        xpub3 = self.master_public_keys.get("remote/")
       +        xpub1 = self.master_public_keys.get("x1/")
       +        xpub2 = self.master_public_keys.get("x2/")
       +        xpub3 = self.master_public_keys.get("x3/")
                if xpub1 is None:
                    return 'create_seed'
                if xpub2 is None or xpub3 is None:
       t@@ -1523,7 +1521,7 @@ class OldWallet(Deterministic_Wallet):
            def get_master_public_keys(self):
                return {'Main Account':self.get_master_public_key()}
        
       -    def create_accounts(self, password):
       +    def create_main_account(self, password):
                mpk = self.storage.get("master_public_key")
                self.create_account(mpk)
        
       t@@ -1574,7 +1572,7 @@ class Wallet(object):
                config = storage.config
        
                self.wallet_types = [ 
       -            #('standard', ("Standard wallet"),          NewWallet if config.get('bip32') else OldWallet),
       +            ('standard', ("Standard wallet"),          NewWallet),
                    ('imported', ("Imported wallet"),          Imported_Wallet),
                    ('2of2',     ("Multisig wallet (2 of 2)"), Wallet_2of2),
                    ('2of3',     ("Multisig wallet (2 of 3)"), Wallet_2of3)
       t@@ -1586,7 +1584,7 @@ class Wallet(object):
                        return WalletClass(storage)
        
                if not storage.file_exists:
       -            seed_version = NEW_SEED_VERSION if config.get('bip32') is True else OLD_SEED_VERSION
       +            seed_version = NEW_SEED_VERSION
                else:
                    seed_version = storage.get('seed_version')
                    if not seed_version: