URI: 
       tbip32 - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 19553a056d7b78dd4ed2e6a9c8fbbf5f5dcf5062
   DIR parent 164c746f51732f04325913382445f24e1af2608d
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Thu,  1 Aug 2013 20:08:56 +0200
       
       bip32
       
       Diffstat:
         M gui/gui_classic.py                  |      18 ++++++++++++++++--
         A lib/account.py                      |     234 +++++++++++++++++++++++++++++++
         M lib/bitcoin.py                      |     200 +++++--------------------------
         M lib/simple_config.py                |       2 +-
         M lib/wallet.py                       |     182 ++++++++++++++++++++++---------
       
       5 files changed, 412 insertions(+), 224 deletions(-)
       ---
   DIR diff --git a/gui/gui_classic.py b/gui/gui_classic.py
       t@@ -401,6 +401,9 @@ class ElectrumWindow(QMainWindow):
                new_contact = wallet_menu.addAction(_("&New contact"))
                new_contact.triggered.connect(self.new_contact_dialog)
        
       +        new_account = wallet_menu.addAction(_("&New account"))
       +        new_account.triggered.connect(self.new_account_dialog)
       +
                import_menu = menubar.addMenu(_("&Import"))
                in_labels = import_menu.addAction(_("&Labels"))
                in_labels.triggered.connect(self.do_import_labels)
       t@@ -972,6 +975,7 @@ class ElectrumWindow(QMainWindow):
                try:
                    tx = self.wallet.mktx( [(to_address, amount)], password, fee, account=self.current_account)
                except BaseException, e:
       +            traceback.print_exc(file=sys.stdout)
                    self.show_message(str(e))
                    return
        
       t@@ -1263,7 +1267,7 @@ class ElectrumWindow(QMainWindow):
                    account_items = []
        
                for k, account in account_items:
       -            name = account.get('name',str(k))
       +            name = account.get_name()
                    c,u = self.wallet.get_account_balance(k)
                    account_item = QTreeWidgetItem( [ name, '', self.format_amount(c+u), ''] )
                    l.addTopLevelItem(account_item)
       t@@ -1280,7 +1284,7 @@ class ElectrumWindow(QMainWindow):
                        is_red = False
                        gap = 0
        
       -                for address in account[is_change]:
       +                for address in account.get_addresses(is_change):
                            h = self.wallet.history.get(address,[])
                    
                            if h == []:
       t@@ -1424,6 +1428,16 @@ class ElectrumWindow(QMainWindow):
                    else:
                        QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
        
       +    def new_account_dialog(self):
       +        text, ok = QInputDialog.getText(self, _('New Account'), _('Name') + ':')
       +        name = unicode(text)
       +        if ok:
       +            self.wallet.create_new_account(name)
       +            self.wallet.synchronize()
       +            self.update_contacts_tab()
       +            self.update_history_tab()
       +            self.update_completions()
       +
            def show_master_public_key(self):
                dialog = QDialog(self)
                dialog.setModal(1)
   DIR diff --git a/lib/account.py b/lib/account.py
       t@@ -0,0 +1,234 @@
       +"""
       +todolist:
       + * passwords, private keys storage
       + * multisig service
       + * compatibility with old addresses for restore
       + * gui
       + 
       +        an account may use one or several MPKs.
       +        due to the type 1 derivations, we need to pass the mpk to this function
       +        None : all accounts
       +        -1 : imported
       +        0,1... : seeded sequences
       +
       +        each account has a public and private master key
       +"""
       +
       +from bitcoin import *
       +
       +
       +class Account(object):
       +    def __init__(self, v):
       +        self.addresses = v.get('0', [])
       +        self.change = v.get('1', [])
       +        self.name = v.get('name', 'unnamed')
       +
       +    def dump(self):
       +        return {'0':self.addresses, '1':self.change, 'name':self.name}
       +
       +    def get_name(self):
       +        return self.name
       +
       +    def get_addresses(self, for_change):
       +        return self.change[:] if for_change else self.addresses[:]
       +
       +    def create_new_address(self, for_change):
       +        addresses = self.change if for_change else self.addresses
       +        n = len(addresses)
       +        address = self.get_new_address( for_change, n)
       +        addresses.append(address)
       +        print address
       +        return address
       +
       +    def get_new_address(self, for_change, n):
       +        pass
       +        
       +
       +
       +
       +class OldAccount(Account):
       +    """  Privatekey(type,n) = Master_private_key + H(n|S|type)  """
       +
       +    def __init__(self, mpk, mpk2 = None, mpk3 = None):
       +        self.mpk = mpk
       +        self.mpk2 = mpk2
       +        self.mpk3 = mpk3
       +
       +    @classmethod
       +    def mpk_from_seed(klass, seed):
       +        curve = SECP256k1
       +        secexp = klass.stretch_key(seed)
       +        master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
       +        master_public_key = master_private_key.get_verifying_key().to_string().encode('hex')
       +        return master_public_key
       +
       +    @classmethod
       +    def stretch_key(self,seed):
       +        oldseed = seed
       +        for i in range(100000):
       +            seed = hashlib.sha256(seed + oldseed).digest()
       +        return string_to_number( seed )
       +
       +    def get_sequence(self, sequence, mpk):
       +        for_change, n = sequence
       +        return string_to_number( Hash( "%d:%d:"%(n,for_change) + mpk.decode('hex') ) )
       +
       +    def get_address(self, sequence):
       +        if not self.mpk2:
       +            pubkey = self.get_pubkey(sequence)
       +            address = public_key_to_bc_address( pubkey.decode('hex') )
       +        elif not self.mpk3:
       +            pubkey1 = self.get_pubkey(sequence)
       +            pubkey2 = self.get_pubkey(sequence, mpk = self.mpk2)
       +            address = Transaction.multisig_script([pubkey1, pubkey2], 2)["address"]
       +        else:
       +            pubkey1 = self.get_pubkey(sequence)
       +            pubkey2 = self.get_pubkey(sequence, mpk = self.mpk2)
       +            pubkey3 = self.get_pubkey(sequence, mpk = self.mpk3)
       +            address = Transaction.multisig_script([pubkey1, pubkey2, pubkey3], 2)["address"]
       +        return address
       +
       +    def get_pubkey(self, sequence, mpk=None):
       +        curve = SECP256k1
       +        if mpk is None: mpk = self.mpk
       +        z = self.get_sequence(sequence, mpk)
       +        master_public_key = ecdsa.VerifyingKey.from_string( mpk.decode('hex'), curve = SECP256k1 )
       +        pubkey_point = master_public_key.pubkey.point + z*curve.generator
       +        public_key2 = ecdsa.VerifyingKey.from_public_point( pubkey_point, curve = SECP256k1 )
       +        return '04' + public_key2.to_string().encode('hex')
       +
       +    def get_private_key_from_stretched_exponent(self, sequence, secexp):
       +        order = generator_secp256k1.order()
       +        secexp = ( secexp + self.get_sequence(sequence, self.mpk) ) % order
       +        pk = number_to_string( secexp, generator_secp256k1.order() )
       +        compressed = False
       +        return SecretToASecret( pk, compressed )
       +        
       +    def get_private_key(self, sequence, seed):
       +        secexp = self.stretch_key(seed)
       +        return self.get_private_key_from_stretched_exponent(sequence, secexp)
       +
       +    def get_private_keys(self, sequence_list, seed):
       +        secexp = self.stretch_key(seed)
       +        return [ self.get_private_key_from_stretched_exponent( sequence, secexp) for sequence in sequence_list]
       +
       +    def check_seed(self, seed):
       +        curve = SECP256k1
       +        secexp = self.stretch_key(seed)
       +        master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
       +        master_public_key = master_private_key.get_verifying_key().to_string().encode('hex')
       +        if master_public_key != self.mpk:
       +            print_error('invalid password (mpk)')
       +            raise BaseException('Invalid password')
       +        return True
       +
       +    def get_input_info(self, sequence):
       +        if not self.mpk2:
       +            pk_addr = self.get_address(sequence)
       +            redeemScript = None
       +        elif not self.mpk3:
       +            pubkey1 = self.get_pubkey(sequence)
       +            pubkey2 = self.get_pubkey(sequence,mpk=self.mpk2)
       +            pk_addr = public_key_to_bc_address( pubkey1.decode('hex') ) # we need to return that address to get the right private key
       +            redeemScript = Transaction.multisig_script([pubkey1, pubkey2], 2)['redeemScript']
       +        else:
       +            pubkey1 = self.get_pubkey(sequence)
       +            pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2)
       +            pubkey3 = self.get_pubkey(sequence, mpk=self.mpk3)
       +            pk_addr = public_key_to_bc_address( pubkey1.decode('hex') ) # we need to return that address to get the right private key
       +            redeemScript = Transaction.multisig_script([pubkey1, pubkey2, pubkey3], 2)['redeemScript']
       +        return pk_addr, redeemScript
       +
       +
       +
       +class BIP32_Account(Account):
       +
       +    def __init__(self, v):
       +        Account.__init__(self, v)
       +        self.c = v['c'].decode('hex')
       +        self.K = v['K'].decode('hex')
       +        self.cK = v['cK'].decode('hex')
       +
       +    def dump(self):
       +        d = Account.dump(self)
       +        d['c'] = self.c.encode('hex')
       +        d['K'] = self.K.encode('hex')
       +        d['cK'] = self.cK.encode('hex')
       +        return d
       +
       +    def get_new_address(self, for_change, n):
       +        pubkey = self.get_pubkey(for_change, n)
       +        address = public_key_to_bc_address( pubkey )
       +        return address
       +
       +    def get_pubkey(self, for_change, n):
       +        K = self.K
       +        chain = self.c
       +        for i in [for_change, n]:
       +            K, K_compressed, chain = CKD_prime(K, chain, i)
       +        return K_compressed
       +
       +    def get_address(self, sequence):
       +        for_change, n = sequence
       +        pubkey = self.get_pubkey(for_change, n)
       +        address = public_key_to_bc_address( pubkey )
       +        return address
       +
       +    def get_private_key(self, sequence, master_k):
       +        chain = self.c
       +        k = master_k
       +        for i in sequence:
       +            k, chain = CKD(k, chain, i)
       +        return SecretToASecret(k, True)
       +
       +    def get_private_keys(self, sequence_list, seed):
       +        return [ self.get_private_key( sequence, seed) for sequence in sequence_list]
       +
       +    def check_seed(self, seed):
       +        master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed)
       +        assert self.mpk == (master_public_key.encode('hex'), master_chain.encode('hex'))
       +
       +    def get_input_info(self, sequence):
       +        pk_addr = self.get_address(sequence)
       +        redeemScript = None
       +        return pk_addr, redeemScript
       +
       +
       +
       +class BIP32_Account_2of2(BIP32_Account):
       +
       +    def __init__(self, v):
       +        BIP32_Account.__init__(self, v)
       +        self.c2 = v['c2'].decode('hex')
       +        self.K2 = v['K2'].decode('hex')
       +        self.cK2 = v['cK2'].decode('hex')
       +
       +    def dump(self):
       +        d = BIP32_Account.dump(self)
       +        d['c2'] = self.c2.encode('hex')
       +        d['K2'] = self.K2.encode('hex')
       +        d['cK2'] = self.cK2.encode('hex')
       +        return d
       +
       +    def get_pubkey2(self, for_change, n):
       +        K = self.K2
       +        chain = self.c2
       +        for i in [for_change, n]:
       +            K, K_compressed, chain = CKD_prime(K, chain, i)
       +        return K_compressed
       +
       +    def get_new_address(self, for_change, n):
       +        pubkey1 = self.get_pubkey(for_change, n)
       +        pubkey2 = self.get_pubkey2(for_change, n)
       +        address = Transaction.multisig_script([pubkey1.encode('hex'), pubkey2.encode('hex')], 2)["address"]
       +        return address
       +
       +    def get_input_info(self, sequence):
       +        chain, i = sequence
       +        pubkey1 = self.get_pubkey(chain, i)
       +        pubkey2 = self.get_pubkey2(chain, i)
       +        # fixme
       +        pk_addr = None # public_key_to_bc_address( pubkey1 ) # we need to return that address to get the right private key
       +        redeemScript = Transaction.multisig_script([pubkey1.encode('hex'), pubkey2.encode('hex')], 2)['redeemScript']
       +        return pk_addr, redeemScript
       +
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -427,169 +427,29 @@ def CKD_prime(K, c, n):
        
        
        
       -class ElectrumSequence:
       -    """  Privatekey(type,n) = Master_private_key + H(n|S|type)  """
       -
       -    def __init__(self, mpk, mpk2 = None, mpk3 = None):
       -        self.mpk = mpk
       -        self.mpk2 = mpk2
       -        self.mpk3 = mpk3
       +def bip32_private_derivation(k, c, branch, sequence):
       +    assert sequence.startswith(branch)
       +    sequence = sequence[len(branch):]
       +    for n in sequence.split('/'):
       +        if n == '': continue
       +        n = int(n[:-1]) + BIP32_PRIME if n[-1] == "'" else int(n)
       +        k, c = CKD(k, c, n)
       +    K, K_compressed = get_pubkeys_from_secret(k)
       +    return k.encode('hex'), c.encode('hex'), K.encode('hex'), K_compressed.encode('hex')
        
       -    @classmethod
       -    def mpk_from_seed(klass, seed):
       -        curve = SECP256k1
       -        secexp = klass.stretch_key(seed)
       -        master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
       -        master_public_key = master_private_key.get_verifying_key().to_string().encode('hex')
       -        return master_public_key
        
       -    @classmethod
       -    def stretch_key(self,seed):
       -        oldseed = seed
       -        for i in range(100000):
       -            seed = hashlib.sha256(seed + oldseed).digest()
       -        return string_to_number( seed )
       -
       -    def get_sequence(self, sequence, mpk):
       -        for_change, n = sequence
       -        return string_to_number( Hash( "%d:%d:"%(n,for_change) + mpk.decode('hex') ) )
       -
       -    def get_address(self, sequence):
       -        if not self.mpk2:
       -            pubkey = self.get_pubkey(sequence)
       -            address = public_key_to_bc_address( pubkey.decode('hex') )
       -        elif not self.mpk3:
       -            pubkey1 = self.get_pubkey(sequence)
       -            pubkey2 = self.get_pubkey(sequence, mpk = self.mpk2)
       -            address = Transaction.multisig_script([pubkey1, pubkey2], 2)["address"]
       -        else:
       -            pubkey1 = self.get_pubkey(sequence)
       -            pubkey2 = self.get_pubkey(sequence, mpk = self.mpk2)
       -            pubkey3 = self.get_pubkey(sequence, mpk = self.mpk3)
       -            address = Transaction.multisig_script([pubkey1, pubkey2, pubkey3], 2)["address"]
       -        return address
       -
       -    def get_pubkey(self, sequence, mpk=None):
       -        curve = SECP256k1
       -        if mpk is None: mpk = self.mpk
       -        z = self.get_sequence(sequence, mpk)
       -        master_public_key = ecdsa.VerifyingKey.from_string( mpk.decode('hex'), curve = SECP256k1 )
       -        pubkey_point = master_public_key.pubkey.point + z*curve.generator
       -        public_key2 = ecdsa.VerifyingKey.from_public_point( pubkey_point, curve = SECP256k1 )
       -        return '04' + public_key2.to_string().encode('hex')
       -
       -    def get_private_key_from_stretched_exponent(self, sequence, secexp):
       -        order = generator_secp256k1.order()
       -        secexp = ( secexp + self.get_sequence(sequence, self.mpk) ) % order
       -        pk = number_to_string( secexp, generator_secp256k1.order() )
       -        compressed = False
       -        return SecretToASecret( pk, compressed )
       -        
       -    def get_private_key(self, sequence, seed):
       -        secexp = self.stretch_key(seed)
       -        return self.get_private_key_from_stretched_exponent(sequence, secexp)
       -
       -    def get_private_keys(self, sequence_list, seed):
       -        secexp = self.stretch_key(seed)
       -        return [ self.get_private_key_from_stretched_exponent( sequence, secexp) for sequence in sequence_list]
       -
       -    def check_seed(self, seed):
       -        curve = SECP256k1
       -        secexp = self.stretch_key(seed)
       -        master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
       -        master_public_key = master_private_key.get_verifying_key().to_string().encode('hex')
       -        if master_public_key != self.mpk:
       -            print_error('invalid password (mpk)')
       -            raise BaseException('Invalid password')
       -        return True
       -
       -    def get_input_info(self, sequence):
       -        if not self.mpk2:
       -            pk_addr = self.get_address(sequence)
       -            redeemScript = None
       -        elif not self.mpk3:
       -            pubkey1 = self.get_pubkey(sequence)
       -            pubkey2 = self.get_pubkey(sequence,mpk=self.mpk2)
       -            pk_addr = public_key_to_bc_address( pubkey1.decode('hex') ) # we need to return that address to get the right private key
       -            redeemScript = Transaction.multisig_script([pubkey1, pubkey2], 2)['redeemScript']
       -        else:
       -            pubkey1 = self.get_pubkey(sequence)
       -            pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2)
       -            pubkey3 = self.get_pubkey(sequence, mpk=self.mpk3)
       -            pk_addr = public_key_to_bc_address( pubkey1.decode('hex') ) # we need to return that address to get the right private key
       -            redeemScript = Transaction.multisig_script([pubkey1, pubkey2, pubkey3], 2)['redeemScript']
       -        return pk_addr, redeemScript
       +def bip32_public_derivation(c, K, branch, sequence):
       +    assert sequence.startswith(branch)
       +    sequence = sequence[len(branch):]
       +    for n in sequence.split('/'):
       +        n = int(n)
       +        K, cK, c = CKD_prime(K, c, n)
        
       +    return c.encode('hex'), K.encode('hex'), cK.encode('hex')
        
        
        
       -class BIP32Sequence:
        
       -    def __init__(self, mpk, mpk2 = None, mpk3 = None):
       -        self.mpk = mpk
       -        self.mpk2 = mpk2
       -        self.mpk3 = mpk3
       -    
       -    @classmethod
       -    def mpk_from_seed(klass, seed):
       -        master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed)
       -        return master_public_key.encode('hex'), master_chain.encode('hex')
       -
       -    def get_pubkey(self, sequence, mpk = None):
       -        if not mpk: mpk = self.mpk
       -        master_public_key, master_chain = mpk
       -        K = master_public_key.decode('hex')
       -        chain = master_chain.decode('hex')
       -        for i in sequence:
       -            K, K_compressed, chain = CKD_prime(K, chain, i)
       -        return K_compressed.encode('hex')
       -
       -    def get_address(self, sequence):
       -        if not self.mpk2:
       -            pubkey = self.get_pubkey(sequence)
       -            address = public_key_to_bc_address( pubkey.decode('hex') )
       -        elif not self.mpk3:
       -            pubkey1 = self.get_pubkey(sequence)
       -            pubkey2 = self.get_pubkey(sequence, mpk = self.mpk2)
       -            address = Transaction.multisig_script([pubkey1, pubkey2], 2)["address"]
       -        else:
       -            pubkey1 = self.get_pubkey(sequence)
       -            pubkey2 = self.get_pubkey(sequence, mpk = self.mpk2)
       -            pubkey3 = self.get_pubkey(sequence, mpk = self.mpk3)
       -            address = Transaction.multisig_script([pubkey1, pubkey2, pubkey3], 2)["address"]
       -        return address
       -
       -    def get_private_key(self, sequence, seed):
       -        master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed)
       -        chain = master_chain
       -        k = master_secret
       -        for i in sequence:
       -            k, chain = CKD(k, chain, i)
       -        return SecretToASecret(k, True)
       -
       -    def get_private_keys(self, sequence_list, seed):
       -        return [ self.get_private_key( sequence, seed) for sequence in sequence_list]
       -
       -    def check_seed(self, seed):
       -        master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed)
       -        assert self.mpk == (master_public_key.encode('hex'), master_chain.encode('hex'))
       -
       -    def get_input_info(self, sequence):
       -        if not self.mpk2:
       -            pk_addr = self.get_address(sequence)
       -            redeemScript = None
       -        elif not self.mpk3:
       -            pubkey1 = self.get_pubkey(sequence)
       -            pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2)
       -            pk_addr = public_key_to_bc_address( pubkey1.decode('hex') ) # we need to return that address to get the right private key
       -            redeemScript = Transaction.multisig_script([pubkey1, pubkey2], 2)['redeemScript']
       -        else:
       -            pubkey1 = self.get_pubkey(sequence)
       -            pubkey2 = self.get_pubkey(sequence, mpk=self.mpk2)
       -            pubkey3 = self.get_pubkey(sequence, mpk=self.mpk3)
       -            pk_addr = public_key_to_bc_address( pubkey1.decode('hex') ) # we need to return that address to get the right private key
       -            redeemScript = Transaction.multisig_script([pubkey1, pubkey2, pubkey3], 2)['redeemScript']
       -        return pk_addr, redeemScript
        
        ################################## transactions
        
       t@@ -734,9 +594,10 @@ class Transaction:
                    txin = self.inputs[i]
                    tx_for_sig = self.serialize( self.inputs, self.outputs, for_sig = i )
        
       -            if txin.get('redeemScript'):
       +            redeem_script = txin.get('redeemScript')
       +            if redeem_script:
                        # 1 parse the redeem script
       -                num, redeem_pubkeys = deserialize.parse_redeemScript(txin.get('redeemScript'))
       +                num, redeem_pubkeys = deserialize.parse_redeemScript(redeem_script)
                        self.inputs[i]["pubkeys"] = redeem_pubkeys
        
                        # build list of public/private keys
       t@@ -747,19 +608,25 @@ class Transaction:
                            pubkey = GetPubKey(pkey.pubkey, compressed)
                            keypairs[ pubkey.encode('hex') ] = sec
        
       +                print "keypairs", keypairs
       +                print redeem_script, redeem_pubkeys
       +
                        # list of already existing signatures
                        signatures = txin.get("signatures",[])
                        print_error("signatures",signatures)
        
                        for pubkey in redeem_pubkeys:
       -                    public_key = ecdsa.VerifyingKey.from_string(pubkey[2:].decode('hex'), curve = SECP256k1)
       -                    for s in signatures:
       -                        try:
       -                            public_key.verify_digest( s.decode('hex')[:-1], Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
       -                            break
       -                        except ecdsa.keys.BadSignatureError:
       -                            continue
       -                    else:
       +
       +                    # here we have compressed key.. it won't work
       +                    #public_key = ecdsa.VerifyingKey.from_string(pubkey[2:].decode('hex'), curve = SECP256k1)  
       +                    #for s in signatures:
       +                    #    try:
       +                    #        public_key.verify_digest( s.decode('hex')[:-1], Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
       +                    #        break
       +                    #    except ecdsa.keys.BadSignatureError:
       +                    #        continue
       +                    #else:
       +                    if 1:
                                # check if we have a key corresponding to the redeem script
                                if pubkey in keypairs.keys():
                                    # add signature
       t@@ -783,7 +650,6 @@ class Transaction:
                        compressed = is_compressed(sec)
                        pkey = regenerate_key(sec)
                        secexp = pkey.secret
       -
                        private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
                        public_key = private_key.get_verifying_key()
                        pkey = EC_KEY(secexp)
   DIR diff --git a/lib/simple_config.py b/lib/simple_config.py
       t@@ -206,7 +206,7 @@ a SimpleConfig instance then reads the wallet file.
        
            def save_wallet_config(self):
                # prevent the creation of incomplete wallets  
       -        if self.wallet_config.get('master_public_key') is None: 
       +        if self.wallet_config.get('master_public_keys') is None: 
                    return
        
                s = repr(self.wallet_config)
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -32,7 +32,7 @@ import time
        
        from util import print_msg, print_error, user_dir, format_satoshis
        from bitcoin import *
       -
       +from account import *
        
        # AES encryption
        EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
       t@@ -85,14 +85,12 @@ class Wallet:
        
                self.imported_keys         = config.get('imported_keys',{})
                self.history               = config.get('addr_history',{})        # address -> list(txid, height)
       -        self.accounts              = config.get('accounts', {})   # this should not include public keys
        
       -        self.SequenceClass = ElectrumSequence
       -        self.sequences = {}
       -        self.sequences[0] = self.SequenceClass(self.config.get('master_public_key'))
        
       -        if self.accounts.get(0) is None:
       -            self.accounts[0] = { 0:[], 1:[], 'name':'Main account' }
       +        self.master_public_keys = config.get('master_public_keys',{})
       +        self.master_private_keys = config.get('master_private_keys', {})
       +
       +        self.load_accounts(config)
        
                self.transactions = {}
                tx = config.get('transactions',{})
       t@@ -167,18 +165,88 @@ class Wallet:
                    seed = random_seed(128)
                self.seed = seed
        
       +
            def save_seed(self):
                self.config.set_key('seed', self.seed, True)
                self.config.set_key('seed_version', self.seed_version, True)
       -        mpk = self.SequenceClass.mpk_from_seed(self.seed)
       -        self.init_sequence(mpk)
       +
       +        master_k, master_c, master_K, master_cK = bip32_init(self.seed)
       +        
       +        k0, c0, K0, cK0 = bip32_private_derivation(master_k, master_c, "m/", "m/0'/")
       +        k1, c1, K1, cK1 = bip32_private_derivation(master_k, master_c, "m/", "m/1'/")
       +        k2, c2, K2, cK2 = bip32_private_derivation(master_k, master_c, "m/", "m/2'/")
       +
       +        self.master_public_keys = {
       +            "m/0'/": (c0, K0, cK0),
       +            "m/1'/": (c1, K1, cK1),
       +            "m/2'/": (c2, K2, cK2)
       +            }
       +        
       +        self.master_private_keys = {
       +            "m/0'/": k0,
       +            "m/1'/": k1
       +            }
       +        # send k2 to service
       +        
       +        self.config.set_key('master_public_keys', self.master_public_keys, True)
       +        self.config.set_key('master_private_keys', self.master_private_keys, True)
       +
       +        # create default account
       +        self.create_new_account('Main account')
       +
       +
       +    def create_new_account(self, name):
       +        keys = self.accounts.keys()
       +        i = 0
       +
       +        while True:
       +            derivation = "m/0'/%d'"%i
       +            if derivation not in keys: break
       +            i += 1
       +
       +        start = "m/0'/"
       +        master_c, master_K, master_cK = self.master_public_keys[start]
       +        master_k = self.master_private_keys[start] # needs decryption
       +        k, c, K, cK = bip32_private_derivation(master_k, master_c, start, derivation) # this is a type 1 derivation
       +        
       +        self.accounts[derivation] = BIP32_Account({ 'name':name, 'c':c, 'K':K, 'cK':cK })
       +        self.save_accounts()
       +
       +    def create_p2sh_account(self, name):
       +        keys = self.accounts.keys()
       +        i = 0
       +        while True:
       +            account_id = "m/1'/%d & m/2'/%d"%(i,i)
       +            if account_id not in keys: break
       +            i += 1
       +
       +        master_c1, master_K1, _ = self.master_public_keys["m/1'/"]
       +        c1, K1, cK1 = bip32_public_derivation(master_c1.decode('hex'), master_K1.decode('hex'), "m/1'/", "m/1'/%d"%i)
       +        
       +        master_c2, master_K2, _ = self.master_public_keys["m/2'/"]
       +        c2, K2, cK2 = bip32_public_derivation(master_c2.decode('hex'), master_K2.decode('hex'), "m/2'/", "m/2'/%d"%i)
       +        
       +        self.accounts[account_id] = BIP32_Account_2of2({ 'name':name, 'c':c1, 'K':K1, 'cK':cK1, 'c2':c2, 'K2':K2, 'cK2':cK2 })
       +        self.save_accounts()
       +
       +
       +    def save_accounts(self):
       +        d = {}
       +        for k, v in self.accounts.items():
       +            d[k] = v.dump()
       +        self.config.set_key('accounts', d, True)
       +
       +
       +    def load_accounts(self, config):
       +        d = config.get('accounts', {})
       +        self.accounts = {}
       +        for k, v in d.items():
       +            if '&' in k:
       +                self.accounts[k] = BIP32_Account_2of2(v)
       +            else:
       +                self.accounts[k] = BIP32_Account(v)
        
        
       -    def init_sequence(self, mpk):
       -        self.config.set_key('master_public_key', mpk, True)
       -        self.sequences[0] = self.SequenceClass(mpk)
       -        self.accounts[0] = { 0:[], 1:[], 'name':'Main account' }
       -        self.config.set_key('accounts', self.accounts, True)
        
        
            def addresses(self, include_change = True):
       t@@ -198,6 +266,7 @@ class Wallet:
                return s[0] == 1
        
            def get_master_public_key(self):
       +        raise
                return self.config.get("master_public_key")
        
            def get_address_index(self, address):
       t@@ -205,7 +274,7 @@ class Wallet:
                    return -1, None
                for account in self.accounts.keys():
                    for for_change in [0,1]:
       -                addresses = self.accounts[account][for_change]
       +                addresses = self.accounts[account].get_addresses(for_change)
                        for addr in addresses:
                            if address == addr:
                                return account, (for_change, addresses.index(addr))
       t@@ -214,12 +283,12 @@ class Wallet:
        
            def get_public_key(self, address):
                account, sequence = self.get_address_index(address)
       -        return self.sequences[account].get_pubkey( sequence )
       +        return self.accounts[account].get_pubkey( sequence )
        
        
            def decode_seed(self, password):
                seed = pw_decode(self.seed, password)
       -        self.sequences[0].check_seed(seed)
       +        #todo:  #self.sequences[0].check_seed(seed)
                return seed
                
            def get_private_key(self, address, password):
       t@@ -230,19 +299,27 @@ class Wallet:
                # decode seed in any case, in order to test the password
                seed = self.decode_seed(password)
                out = {}
       -        l_sequences = []
       -        l_addresses = []
                for address in addresses:
                    if address in self.imported_keys.keys():
                        out[address] = pw_decode( self.imported_keys[address], password )
                    else:
                        account, sequence = self.get_address_index(address)
       -                if account == 0:
       -                    l_sequences.append(sequence)
       -                    l_addresses.append(address)
       +                print "found index", address, account, sequence
       +                if account == "m/0'/0'":
       +                    # FIXME: this is ugly
       +                    master_k = self.master_private_keys["m/0'/"]
       +                    master_c, _, _ = self.master_public_keys["m/0'/"]
       +                    master_k, master_c = CKD(master_k, master_c, 0 + BIP32_PRIME)
       +                    pk = self.accounts["m/0'/0'"].get_private_key(sequence, master_k)
       +                    out[address] = pk
       +
       +                elif account == "m/1'/1 & m/2'/1":
       +                    master_k = self.master_private_keys["m/1'/"]
       +                    master_c, master_K, _ = self.master_public_keys["m/1'/"]
       +                    master_k, master_c = CKD(master_k.decode('hex'), master_c.decode('hex'), 1)
       +                    pk = self.accounts[account].get_private_key(sequence, master_k)
       +                    out[address] = pk
        
       -        pk = self.sequences[0].get_private_keys(l_sequences, seed)
       -        for i, address in enumerate(l_addresses): out[address] = pk[i]                     
                return out
        
        
       t@@ -281,8 +358,8 @@ class Wallet:
                    if txin.get('KeyID'):
                        account, name, sequence = txin.get('KeyID')
                        if name != 'Electrum': continue
       -                sec = self.sequences[account].get_private_key(sequence, seed)
       -                addr = self.sequences[account].get_address(sequence)
       +                sec = self.accounts[account].get_private_key(sequence, seed)
       +                addr = self.accounts[account].get_address(sequence)
                        txin['address'] = addr
                        private_keys[addr] = sec
        
       t@@ -313,20 +390,6 @@ class Wallet:
                    print_error("Verification error: {0}".format(e))
                    return False
        
       -    def create_new_address(self, account, for_change):
       -        addresses = self.accounts[account][for_change]
       -        n = len(addresses)
       -        address = self.get_new_address( account, for_change, n)
       -        self.accounts[account][for_change].append(address)
       -        self.history[address] = []
       -        print_msg(address)
       -        return address
       -        
       -
       -    def get_new_address(self, account, for_change, n):
       -        return self.sequences[account].get_address((for_change, n))
       -        print address
       -        return address
        
            def change_gap_limit(self, value):
                if value >= self.gap_limit:
       t@@ -345,7 +408,7 @@ class Wallet:
        
                    self.gap_limit = value
                    self.config.set_key('gap_limit', self.gap_limit, True)
       -            self.config.set_key('accounts', self.accounts, True)
       +            self.save_accounts()
                    return True
                else:
                    return False
       t@@ -363,7 +426,7 @@ class Wallet:
                nmax = 0
        
                for account in self.accounts.values():
       -            addresses = account[0]
       +            addresses = account.get_addresses(0)
                    k = self.num_unused_trailing_addresses(addresses)
                    for a in addresses[0:-k]:
                        if self.history.get(a):
       t@@ -391,16 +454,22 @@ class Wallet:
        
            def synchronize_sequence(self, account, for_change):
                limit = self.gap_limit_for_change if for_change else self.gap_limit
       -        addresses = self.accounts[account][for_change]
                new_addresses = []
                while True:
       +            addresses = account.get_addresses(for_change)
                    if len(addresses) < limit:
       -                new_addresses.append( self.create_new_address(account, for_change) )
       +                address = account.create_new_address(for_change)
       +                self.history[address] = []
       +                new_addresses.append( address )
                        continue
       +
                    if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
                        break
                    else:
       -                new_addresses.append( self.create_new_address(account, for_change) )
       +                address = account.create_new_address(for_change)
       +                self.history[address] = []
       +                new_addresses.append( address )
       +
                return new_addresses
                
        
       t@@ -412,10 +481,10 @@ class Wallet:
        
            def synchronize(self):
                new = []
       -        for account in self.accounts.keys():
       +        for account in self.accounts.values():
                    new += self.synchronize_account(account)
                if new:
       -            self.config.set_key('accounts', self.accounts, True)
       +            self.save_accounts()
                    self.config.set_key('addr_history', self.history, True)
                return new
        
       t@@ -522,7 +591,7 @@ class Wallet:
            def get_accounts(self):
                accounts = {}
                for k, account in self.accounts.items():
       -            accounts[k] = account.get('name')
       +            accounts[k] = account.name
                if self.imported_keys:
                    accounts[-1] = 'Imported keys'
                return accounts
       t@@ -534,8 +603,8 @@ class Wallet:
                    o = self.imported_keys.keys()
                else:
                    ac = self.accounts[a]
       -            o = ac[0][:]
       -            if include_change: o += ac[1]
       +            o = ac.get_addresses(0)
       +            if include_change: o += ac.get_addresses(1)
                return o
        
            def get_imported_balance(self):
       t@@ -818,14 +887,20 @@ class Wallet:
                        pk_addresses.append(address)
                        continue
                    account, sequence = self.get_address_index(address)
       -            txin['KeyID'] = (account, 'Electrum', sequence) # used by the server to find the key
       -            pk_addr, redeemScript = self.sequences[account].get_input_info(sequence)
       +
       +            txin['KeyID'] = (account, 'BIP32', sequence) # used by the server to find the key
       +
       +            _, redeemScript = self.accounts[account].get_input_info(sequence)
       +            
                    if redeemScript: txin['redeemScript'] = redeemScript
       -            pk_addresses.append(pk_addr)
       +            pk_addresses.append(address)
       +
       +        print "pk_addresses", pk_addresses
        
                # get all private keys at once.
                if self.seed:
                    private_keys = self.get_private_keys(pk_addresses, password)
       +            print "private keys", private_keys
                    tx.sign(private_keys)
        
                for address, x in outputs:
       t@@ -920,7 +995,6 @@ class Wallet:
                s = {
                    'use_change': self.use_change,
                    'fee_per_kb': self.fee,
       -            'accounts': self.accounts,
                    'addr_history': self.history, 
                    'labels': self.labels,
                    'contacts': self.addressbook,