URI: 
       tMerge branch 'separate_wallets' - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 31a32d423f8aeb4d188bb1779b928496c7b16395
   DIR parent c4774e0be24fc408fa2fdc991b43aaaa49433236
  HTML Author: thomasv <thomasv@gitorious>
       Date:   Sat,  6 Apr 2013 21:12:28 +0200
       
       Merge branch 'separate_wallets'
       
       Diffstat:
         M electrum                            |       3 ++-
         M gui/gui_classic.py                  |      48 +++++++++++++++++++++++++------
         M lib/bitcoin.py                      |       5 ++++-
         M lib/commands.py                     |      23 ++++++++++++-----------
         M lib/wallet.py                       |     104 ++++++++++++++++++++-----------
       
       5 files changed, 124 insertions(+), 59 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -323,7 +323,8 @@ if __name__ == '__main__':
                args = [cmd, options.show_all, options.show_balance, options.show_labels]
        
            elif cmd in ['payto', 'mktx']:
       -        args = [ 'mktx', args[1], Decimal(args[2]), Decimal(options.tx_fee) if options.tx_fee else None, options.change_addr, options.from_addr ]
       +        domain = [options.from_addr] if options.from_addr else None
       +        args = [ 'mktx', args[1], Decimal(args[2]), Decimal(options.tx_fee) if options.tx_fee else None, options.change_addr, domain ]
        
            elif cmd == 'help':
                if len(args) < 2:
   DIR diff --git a/gui/gui_classic.py b/gui/gui_classic.py
       t@@ -285,8 +285,9 @@ class ElectrumWindow(QMainWindow):
                self.lite = None
                self.wallet = wallet
                self.config = config
       -        self.init_plugins()
       +        self.current_account = self.config.get("current_account", None)
        
       +        self.init_plugins()
                self.create_status_bar()
        
                self.wallet.interface.register_callback('updated', lambda: self.emit(QtCore.SIGNAL('update_wallet')))
       t@@ -429,7 +430,7 @@ class ElectrumWindow(QMainWindow):
                        text = _("Synchronizing...")
                        icon = QIcon(":icons/status_waiting.png")
                    else:
       -                c, u = self.wallet.get_balance()
       +                c, u = self.wallet.get_account_balance(self.current_account)
                        text =  _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
                        if u: text +=  "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
                        text += self.create_quote_text(Decimal(c+u)/100000000)
       t@@ -607,7 +608,7 @@ class ElectrumWindow(QMainWindow):
            def update_history_tab(self):
        
                self.history_list.clear()
       -        for item in self.wallet.get_tx_history():
       +        for item in self.wallet.get_tx_history(self.current_account):
                    tx_hash, conf, is_mine, value, fee, balance, timestamp = item
                    if conf:
                        try:
       t@@ -666,6 +667,7 @@ class ElectrumWindow(QMainWindow):
                grid.setColumnMinimumWidth(3,300)
                grid.setColumnStretch(5,1)
        
       +
                self.payto_e = QLineEdit()
                grid.addWidget(QLabel(_('Pay to')), 1, 0)
                grid.addWidget(self.payto_e, 1, 1, 1, 3)
       t@@ -724,8 +726,8 @@ class ElectrumWindow(QMainWindow):
                    self.funds_error = False
        
                    if self.amount_e.text() == '!':
       -                c, u = self.wallet.get_balance()
       -                inputs, total, fee = self.wallet.choose_tx_inputs( c + u, 0 )
       +                c, u = self.wallet.get_account_balance(self.current_account)
       +                inputs, total, fee = self.wallet.choose_tx_inputs( c + u, 0, self.current_account)
                        fee = self.wallet.estimated_fee(inputs)
                        amount = c + u - fee
                        self.amount_e.setText( str( Decimal( amount ) / 100000000 ) )
       t@@ -737,7 +739,7 @@ class ElectrumWindow(QMainWindow):
                    if not is_fee: fee = None
                    if amount is None:
                        return
       -            inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
       +            inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee, self.current_account )
                    if not is_fee:
                        self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
                    if inputs:
       t@@ -802,7 +804,7 @@ class ElectrumWindow(QMainWindow):
                    return
        
                try:
       -            tx = self.wallet.mktx( [(to_address, amount)], password, fee)
       +            tx = self.wallet.mktx( [(to_address, amount)], password, fee, account=self.current_account)
                except BaseException, e:
                    self.show_message(str(e))
                    return
       t@@ -1091,8 +1093,16 @@ class ElectrumWindow(QMainWindow):
                    for i,width in enumerate(self.column_widths['receive'][self.expert_mode]):
                        l.setColumnWidth(i, width)        
        
       +        if self.current_account is None:
       +            account_items = self.wallet.accounts.items()
       +        elif self.current_account != -1:
       +            account_items = [(self.current_account, self.wallet.accounts.get(self.current_account))]
       +        else:
       +            account_items = []
        
       -        for k, account in self.wallet.accounts.items():
       +        print self.current_account
       +            
       +        for k, account in account_items:
                    name = account.get('name',str(k))
                    c,u = self.wallet.get_account_balance(k)
                    account_item = QTreeWidgetItem( [ name, '', format_satoshis(c+u), ''] )
       t@@ -1127,7 +1137,8 @@ class ElectrumWindow(QMainWindow):
                                item.setBackgroundColor(1, QColor('red'))
                            seq_item.addChild(item)
        
       -        if self.wallet.imported_keys:
       +
       +        if self.wallet.imported_keys and (self.current_account is None or self.current_account == -1):
                    c,u = self.wallet.get_imported_balance()
                    account_item = QTreeWidgetItem( [ _('Imported'), '', format_satoshis(c+u), ''] )
                    l.addTopLevelItem(account_item)
       t@@ -1183,6 +1194,17 @@ class ElectrumWindow(QMainWindow):
                console.updateNamespace(methods)
                return console
        
       +    def change_account(self,s):
       +        if s == _("All accounts"):
       +            self.current_account = None
       +        else:
       +            accounts = self.wallet.get_accounts()
       +            for k, v in accounts.items():
       +                if v == s:
       +                    self.current_account = k
       +        self.update_history_tab()
       +        self.update_status()
       +        self.update_receive_tab()
        
            def create_status_bar(self):
                self.status_text = ""
       t@@ -1194,6 +1216,14 @@ class ElectrumWindow(QMainWindow):
                if(update_notification.new_version):
                    sb.addPermanentWidget(update_notification)
        
       +        accounts = self.wallet.get_accounts()
       +        if len(accounts) > 1:
       +            from_combo = QComboBox()
       +            from_combo.addItems([_("All accounts")] + accounts.values())
       +            from_combo.setCurrentIndex(0)
       +            self.connect(from_combo,SIGNAL("activated(QString)"),self.change_account) 
       +            sb.addPermanentWidget(from_combo)
       +
                if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7):
                    sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), _("Switch to Lite Mode"), self.go_lite ) )
                if self.wallet.seed:
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -804,6 +804,7 @@ class Transaction:
        
            def get_value(self, addresses, prevout_values):
                # return the balance for that tx
       +        is_relevant = False
                is_send = False
                is_pruned = False
                is_partial = False
       t@@ -813,6 +814,7 @@ class Transaction:
                    addr = item.get('address')
                    if addr in addresses:
                        is_send = True
       +                is_relevant = True
                        key = item['prevout_hash']  + ':%d'%item['prevout_n']
                        value = prevout_values.get( key )
                        if value is None:
       t@@ -829,6 +831,7 @@ class Transaction:
                    v_out += value
                    if addr in addresses:
                        v_out_mine += value
       +                is_relevant = True
        
                if is_pruned:
                    # some inputs are mine:
       t@@ -850,7 +853,7 @@ class Transaction:
                        # all inputs are mine
                        fee = v_out - v_in
        
       -        return is_send, v, fee
       +        return is_relevant, is_send, v, fee
        
            def as_dict(self):
                import json
   DIR diff --git a/lib/commands.py b/lib/commands.py
       t@@ -208,7 +208,7 @@ class Commands:
                    return False
        
        
       -    def _mktx(self, to_address, amount, fee = None, change_addr = None, from_addr = None):
       +    def _mktx(self, to_address, amount, fee = None, change_addr = None, domain = None):
        
                if not is_valid(to_address):
                    raise BaseException("Invalid Bitcoin address", to_address)
       t@@ -217,12 +217,13 @@ class Commands:
                    if not is_valid(change_addr):
                        raise BaseException("Invalid Bitcoin address", change_addr)
        
       -        if from_addr:
       -            if not is_valid(from_addr):
       -                raise BaseException("invalid Bitcoin address", from_addr)
       +        if domain is not None:
       +            for addr in domain:
       +                if not is_valid(addr):
       +                    raise BaseException("invalid Bitcoin address", addr)
                    
       -            if not self.wallet.is_mine(from_addr):
       -                raise BaseException("address not in wallet")
       +                if not self.wallet.is_mine(addr):
       +                    raise BaseException("address not in wallet", addr)
        
                for k, v in self.wallet.labels.items():
                    if v == to_address:
       t@@ -234,16 +235,16 @@ class Commands:
        
                amount = int(100000000*amount)
                if fee: fee = int(100000000*fee)
       -        return self.wallet.mktx( [(to_address, amount)], self.password, fee , change_addr, from_addr)
       +        return self.wallet.mktx( [(to_address, amount)], self.password, fee , change_addr, domain)
        
        
       -    def mktx(self, to_address, amount, fee = None, change_addr = None, from_addr = None):
       -        tx = self._mktx(to_address, amount, fee, change_addr, from_addr)
       +    def mktx(self, to_address, amount, fee = None, change_addr = None, domain = None):
       +        tx = self._mktx(to_address, amount, fee, change_addr, domain)
                return tx.as_dict()
        
        
       -    def payto(self, to_address, amount, fee = None, change_addr = None, from_addr = None):
       -        tx = self._mktx(to_address, amount, fee, change_addr, from_addr)
       +    def payto(self, to_address, amount, fee = None, change_addr = None, domain = None):
       +        tx = self._mktx(to_address, amount, fee, change_addr, domain)
                r, h = self.wallet.sendtx( tx )
                return h
        
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -165,11 +165,10 @@ class Wallet:
                self.config.set_key('accounts', self.accounts, True)
        
        
       -    def addresses(self, include_change = False):
       -        o = self.imported_keys.keys()
       -        for a in self.accounts.values():
       -            o += a[0]
       -            if include_change: o += a[1]
       +    def addresses(self, include_change = True):
       +        o = self.get_account_addresses(-1, include_change)
       +        for a in self.accounts.keys():
       +            o += self.get_account_addresses(a, include_change)
                return o
        
        
       t@@ -400,7 +399,7 @@ class Wallet:
        
            def fill_addressbook(self):
                for tx_hash, tx in self.transactions.items():
       -            is_send, _, _ = self.get_tx_value(tx)
       +            is_relevant, is_send, _, _ = self.get_tx_value(tx)
                    if is_send:
                        for addr, v in tx.outputs:
                            if not self.is_mine(addr) and addr not in self.addressbook:
       t@@ -421,10 +420,9 @@ class Wallet:
                return flags
                
        
       -    def get_tx_value(self, tx, addresses=None):
       -        if addresses is None: addresses = self.addresses(True)
       -        return tx.get_value(addresses, self.prevout_values)
       -
       +    def get_tx_value(self, tx, account=None):
       +        domain = self.get_account_addresses(account)
       +        return tx.get_value(domain, self.prevout_values)
        
            
            def update_tx_outputs(self, tx_hash):
       t@@ -480,9 +478,25 @@ class Wallet:
                        u += v
                return c, u
        
       -    def get_account_addresses(self, a):
       -        ac = self.accounts[a]
       -        return ac[0] + ac[1]
       +
       +    def get_accounts(self):
       +        accounts = {}
       +        for k, account in self.accounts.items():
       +            accounts[k] = account.get('name')
       +        if self.imported_keys:
       +            accounts[-1] = 'Imported keys'
       +        return accounts
       +
       +    def get_account_addresses(self, a, include_change=True):
       +        if a is None:
       +            o = self.addresses(True)
       +        elif a == -1:
       +            o = self.imported_keys.keys()
       +        else:
       +            ac = self.accounts[a]
       +            o = ac[0][:]
       +            if include_change: o += ac[1]
       +        return o
        
            def get_imported_balance(self):
                cc = uu = 0
       t@@ -493,6 +507,11 @@ class Wallet:
                return cc, uu
        
            def get_account_balance(self, account):
       +        if account is None:
       +            return self.get_balance()
       +        elif account == -1:
       +            return self.get_imported_balance()
       +        
                conf = unconf = 0
                for addr in self.get_account_addresses(account): 
                    c, u = self.get_addr_balance(addr)
       t@@ -531,14 +550,13 @@ class Wallet:
        
        
        
       -    def choose_tx_inputs( self, amount, fixed_fee, from_addr = None ):
       +    def choose_tx_inputs( self, amount, fixed_fee, account = None ):
                """ todo: minimize tx size """
                total = 0
                fee = self.fee if fixed_fee is None else fixed_fee
       -
       +        domain = self.get_account_addresses(account)
                coins = []
                prioritized_coins = []
       -        domain = [from_addr] if from_addr else self.addresses(True)
                for i in self.frozen_addresses:
                    if i in domain: domain.remove(i)
        
       t@@ -571,13 +589,17 @@ class Wallet:
                return fee
        
        
       -    def add_tx_change( self, outputs, amount, fee, total, change_addr=None ):
       +    def add_tx_change( self, inputs, outputs, amount, fee, total, change_addr=None, account=0 ):
       +        "add change to a transaction"
                change_amount = total - ( amount + fee )
                if change_amount != 0:
       -            # normally, the update thread should ensure that the last change address is unused
                    if not change_addr:
       -                change_addresses = self.accounts[0][1]
       -                change_addr = change_addresses[-self.gap_limit_for_change]
       +                if not self.use_change or account == -1:
       +                    change_addr = inputs[-1]['address']
       +                else:
       +                    if account is None: account = 0
       +                    change_addr = self.accounts[account][1][-self.gap_limit_for_change]
       +
                    # Insert the change output at a random position in the outputs
                    posn = random.randint(0, len(outputs))
                    outputs[posn:posn] = [( change_addr,  change_amount)]
       t@@ -588,6 +610,7 @@ class Wallet:
                with self.lock:
                    return self.history.get(address)
        
       +
            def get_status(self, h):
                if not h: return None
                if h == ['*']: return '*'
       t@@ -597,10 +620,8 @@ class Wallet:
                return hashlib.sha256( status ).digest().encode('hex')
        
        
       -
            def receive_tx_callback(self, tx_hash, tx, tx_height):
        
       -
                if not self.check_new_tx(tx_hash, tx):
                    # may happen due to pruning
                    print_error("received transaction that is no longer referenced in history", tx_hash)
       t@@ -631,7 +652,7 @@ class Wallet:
                            if self.verifier: self.verifier.add(tx_hash, tx_height)
        
        
       -    def get_tx_history(self):
       +    def get_tx_history(self, account=None):
                with self.transaction_lock:
                    history = self.transactions.items()
                    history.sort(key = lambda x: self.verifier.verified_tx.get(x[0]) if self.verifier.verified_tx.get(x[0]) else (1e12,0,0))
       t@@ -639,21 +660,23 @@ class Wallet:
            
                    balance = 0
                    for tx_hash, tx in history:
       -                is_mine, v, fee = self.get_tx_value(tx)
       +                is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
                        if v is not None: balance += v
       -            c, u = self.get_balance()
       +
       +            c, u = self.get_account_balance(account)
        
                    if balance != c+u:
       -                #v_str = format_satoshis( c+u - balance, True, self.num_zeros)
                        result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
        
                    balance = c + u - balance
                    for tx_hash, tx in history:
       -                conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
       -                is_mine, value, fee = self.get_tx_value(tx)
       +                is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
       +                if not is_relevant:
       +                    continue
                        if value is not None:
                            balance += value
        
       +                conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
                        result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
        
                return result
       t@@ -670,7 +693,7 @@ class Wallet:
                tx = self.transactions.get(tx_hash)
                default_label = ''
                if tx:
       -            is_mine, _, _ = self.get_tx_value(tx)
       +            is_relevant, is_mine, _, _ = self.get_tx_value(tx)
                    if is_mine:
                        for o in tx.outputs:
                            o_addr, _ = o
       t@@ -705,20 +728,27 @@ class Wallet:
                return default_label
        
        
       -    def mktx(self, outputs, password, fee=None, change_addr=None, from_addr= None):
       -
       +    def mktx(self, outputs, password, fee=None, change_addr=None, account=None ):
       +        """
       +        create a transaction
       +        account parameter:
       +           None means use all accounts
       +           -1 means imported keys
       +           0, 1, etc are seed accounts
       +        """
       +        
                for address, x in outputs:
                    assert is_valid(address)
        
                amount = sum( map(lambda x:x[1], outputs) )
       -        inputs, total, fee = self.choose_tx_inputs( amount, fee, from_addr )
       +        
       +        domain = self.get_account_addresses(account)
       +            
       +        inputs, total, fee = self.choose_tx_inputs( amount, fee, domain )
                if not inputs:
                    raise ValueError("Not enough funds")
        
       -        if not self.use_change and not change_addr:
       -            change_addr = inputs[-1]['address']
       -            print_error( "Sending change to", change_addr )
       -        outputs = self.add_tx_change(outputs, amount, fee, total, change_addr)
       +        outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr, account)
        
                tx = Transaction.from_io(inputs, outputs)
        
       t@@ -726,7 +756,7 @@ class Wallet:
                for i in range(len(tx.inputs)):
                    txin = tx.inputs[i]
                    address = txin['address']
       -            if address in self.imported_keys.keys(): 
       +            if address in self.imported_keys.keys():
                        pk_addresses.append(address)
                        continue
                    account, sequence = self.get_address_index(address)