URI: 
       tstore transactions in serialized form - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit f0b255acba74b5539b847a3fb10874514c701604
   DIR parent 67283f0b1b040b20a93501ecbf7f3068643fddb6
  HTML Author: thomasv <thomasv@gitorious>
       Date:   Fri, 22 Feb 2013 19:22:22 +0100
       
       store transactions in serialized form
       
       Diffstat:
         M lib/bitcoin.py                      |      56 +++++++++++++++++++++++++++++++
         M lib/gui_qt.py                       |       4 ++--
         M lib/wallet.py                       |     191 ++++++++++++-------------------
       
       3 files changed, 134 insertions(+), 117 deletions(-)
       ---
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -597,6 +597,62 @@ class Transaction:
                return self.d
            
        
       +    def has_address(self, addr):
       +        print self.inputs
       +        print self.outputs
       +        
       +        found = False
       +        for txin in self.inputs:
       +            if addr == txin.get('address'): 
       +                found = True
       +                break
       +        for txout in self.outputs:
       +            if addr == txout[0]:
       +                found = True
       +                break
       +        return found
       +
       +
       +    def get_value(self, addresses, prevout_values):
       +        # return the balance for that tx
       +        is_send = False
       +        is_pruned = False
       +        v_in = v_out = v_out_mine = 0
       +
       +        for item in self.inputs:
       +            addr = item.get('address')
       +            if addr in addresses:
       +                is_send = True
       +                key = item['prevout_hash']  + ':%d'%item['prevout_n']
       +                value = prevout_values.get( key )
       +                if value is None:
       +                    is_pruned = True
       +                else:
       +                    v_in += value
       +            else:
       +                is_pruned = True
       +                    
       +        for item in self.outputs:
       +            addr, value = item
       +            v_out += value
       +            if addr in addresses:
       +                v_out_mine += value
       +
       +        if not is_pruned:
       +            # all inputs are mine:
       +            fee = v_out - v_in
       +            v = v_out_mine - v_in
       +        else:
       +            # some inputs are mine:
       +            fee = None
       +            if is_send:
       +                v = v_out_mine - v_out
       +            else:
       +                # no input is mine
       +                v = v_out_mine
       +            
       +        return is_send, v, fee
       +
        
        
        def test_bip32():
   DIR diff --git a/lib/gui_qt.py b/lib/gui_qt.py
       t@@ -1251,8 +1251,8 @@ class ElectrumWindow(QMainWindow):
                    if address in alias_targets: continue
                    label = self.wallet.labels.get(address,'')
                    n = 0 
       -            for item in self.wallet.transactions.values():
       -                if address in item['outputs'] : n=n+1
       +            #for item in self.wallet.transactions.values():
       +            #    if address in item['outputs'] : n=n+1
                    tx = "%d"%n
                    item = QTreeWidgetItem( [ address, label, tx] )
                    item.setFont(0, QFont(MONOSPACE_FONT))
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -74,9 +74,17 @@ class Wallet:
                self.addressbook           = config.get('contacts', [])
                self.imported_keys         = config.get('imported_keys',{})
                self.history               = config.get('addr_history',{})        # address -> list(txid, height)
       -        self.transactions          = config.get('transactions',{})        # txid -> deserialised
       +        self.tx_height             = config.get('tx_height',{})
        
       -        self.requested_amounts     = config.get('requested_amounts',{})   # txid -> deserialised
       +        self.transactions = {}
       +        tx = config.get('transactions',{})
       +        try:
       +            for k,v in tx.items(): self.transactions[k] = Transaction(v)
       +        except:
       +            print_msg("Warning: Cannot deserialize transactions. skipping")
       +        
       +
       +        self.requested_amounts     = config.get('requested_amounts',{}) 
        
                # not saved
                self.prevout_values = {}     # my own transaction outputs
       t@@ -308,7 +316,7 @@ class Wallet:
        
            def fill_addressbook(self):
                for tx_hash, tx in self.transactions.items():
       -            is_send, _, _ = self.get_tx_value(tx_hash)
       +            is_send, _, _ = self.get_tx_value(tx)
                    if is_send:
                        for o in tx['outputs']:
                            addr = o.get('address')
       t@@ -324,51 +332,9 @@ class Wallet:
                return flags
                
        
       -    def get_tx_value(self, tx_hash, addresses = None):
       -        # return the balance for that tx
       +    def get_tx_value(self, tx, addresses=None):
                if addresses is None: addresses = self.all_addresses()
       -        with self.lock:
       -            is_send = False
       -            is_pruned = False
       -            v_in = v_out = v_out_mine = 0
       -            d = self.transactions.get(tx_hash)
       -            if not d: 
       -                return 0, 0, 0
       -
       -            for item in d.get('inputs'):
       -                addr = item.get('address')
       -                if addr in addresses:
       -                    is_send = True
       -                    key = item['prevout_hash']  + ':%d'%item['prevout_n']
       -                    value = self.prevout_values.get( key )
       -                    if value is None:
       -                        is_pruned = True
       -                    else:
       -                        v_in += value
       -                else:
       -                    is_pruned = True
       -                    
       -            for item in d.get('outputs'):
       -                addr = item.get('address')
       -                value = item.get('value')
       -                v_out += value
       -                if addr in addresses:
       -                    v_out_mine += value
       -
       -        if not is_pruned:
       -            # all inputs are mine:
       -            fee = v_out - v_in
       -            v = v_out_mine - v_in
       -        else:
       -            # some inputs are mine:
       -            fee = None
       -            if is_send:
       -                v = v_out_mine - v_out
       -            else:
       -                # no input is mine
       -                v = v_out_mine
       -            
       -        return is_send, v, fee
       +        return tx.get_value(addresses, self.prevout_values)
        
        
            def get_tx_details(self, tx_hash):
       t@@ -414,13 +380,15 @@ class Wallet:
            
            def update_tx_outputs(self, tx_hash):
                tx = self.transactions.get(tx_hash)
       -        for item in tx.get('outputs'):
       -            value = item.get('value')
       -            key = tx_hash+ ':%d'%item.get('index')
       +        i = 0
       +        for item in tx.outputs:
       +            addr, value = item
       +            key = tx_hash+ ':%d'%i
                    with self.lock:
       -                self.prevout_values[key] = value 
       +                self.prevout_values[key] = value
       +            i += 1
        
       -        for item in tx.get('inputs'):
       +        for item in tx.inputs:
                    if self.is_mine(item.get('address')):
                        key = item['prevout_hash'] + ':%d'%item['prevout_n']
                        self.spent_outputs.append(key)
       t@@ -434,32 +402,36 @@ class Wallet:
                received_coins = []   # list of coins received at address
        
                for tx_hash, tx_height in h:
       -            d = self.transactions.get(tx_hash)
       -            if not d: continue
       -            for item in d.get('outputs'):
       -                addr = item.get('address')
       +            tx = self.transactions.get(tx_hash)
       +            if not tx: continue
       +            i = 0
       +            for item in tx.outputs:
       +                addr, value = item
                        if addr == address:
       -                    key = tx_hash + ':%d'%item['index']
       +                    key = tx_hash + ':%d'%i
                            received_coins.append(key)
       +                i +=1
        
                for tx_hash, tx_height in h:
       -            d = self.transactions.get(tx_hash)
       -            if not d: continue
       +            tx = self.transactions.get(tx_hash)
       +            if not tx: continue
                    v = 0
        
       -            for item in d.get('inputs'):
       +            for item in tx.inputs:
                        addr = item.get('address')
                        if addr == address:
                            key = item['prevout_hash']  + ':%d'%item['prevout_n']
                            value = self.prevout_values.get( key )
                            if key in received_coins: 
                                v -= value
       -                    
       -            for item in d.get('outputs'):
       -                addr = item.get('address')
       -                key = tx_hash + ':%d'%item['index']
       +
       +            i = 0
       +            for item in tx.outputs:
       +                addr, value = item
       +                key = tx_hash + ':%d'%i
                        if addr == address:
       -                    v += item.get('value')
       +                    v += value
       +                i += 1
        
                    if tx_height:
                        c += v
       t@@ -591,15 +563,16 @@ class Wallet:
        
        
        
       -    def receive_tx_callback(self, tx_hash, tx):
       +    def receive_tx_callback(self, tx_hash, tx, tx_height):
        
                if not self.check_new_tx(tx_hash, tx):
                    raise BaseException("error: received transaction is not consistent with history"%tx_hash)
        
                with self.lock:
                    self.transactions[tx_hash] = tx
       +            self.tx_height[tx_hash] = tx_height
        
       -        tx_height = tx.get('height')
       +        #tx_height = tx.get('height')
                if self.verifier and tx_height>0: 
                    self.verifier.add(tx_hash, tx_height)
        
       t@@ -623,22 +596,21 @@ class Wallet:
                            # add it in case it was previously unconfirmed
                            if self.verifier: self.verifier.add(tx_hash, tx_height)
                            # set the height in case it changed
       -                    tx = self.transactions.get(tx_hash)
       -                    if tx:
       -                        if tx.get('height') != tx_height:
       -                            print_error( "changing height for tx", tx_hash )
       -                            tx['height'] = tx_height
       +                    txh = self.tx_height.get(tx_hash)
       +                    if txh and txh != tx_height:
       +                        print_error( "changing height for tx", tx_hash )
       +                        self.tx_height[tx_hash] = tx_height
        
        
            def get_tx_history(self):
                with self.lock:
       -            history = self.transactions.values()
       -        history.sort(key = lambda x: x.get('height') if x.get('height') else 1e12)
       +            history = self.transactions.items()
       +        history.sort(key = lambda x: self.tx_height.get(x[0],1e12) )
                result = []
            
                balance = 0
       -        for tx in history:
       -            is_mine, v, fee = self.get_tx_value(tx['tx_hash'])
       +        for tx_hash, tx in history:
       +            is_mine, v, fee = self.get_tx_value(tx)
                    if v is not None: balance += v
                c, u = self.get_balance()
        
       t@@ -647,10 +619,9 @@ class Wallet:
                    result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
        
                balance = c + u - balance
       -        for tx in history:
       -            tx_hash = tx['tx_hash']
       +        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_hash)
       +            is_mine, value, fee = self.get_tx_value(tx)
                    if value is not None:
                        balance += value
        
       t@@ -679,23 +650,23 @@ class Wallet:
                tx = self.transactions.get(tx_hash)
                default_label = ''
                if tx:
       -            is_mine, _, _ = self.get_tx_value(tx_hash)
       +            is_mine, _, _ = self.get_tx_value(tx)
                    if is_mine:
       -                for o in tx['outputs']:
       -                    o_addr = o.get('address')
       +                for o in tx.outputs:
       +                    o_addr, _ = o
                            if not self.is_mine(o_addr):
                                try:
                                    default_label = self.labels[o_addr]
                                except KeyError:
                                    default_label = o_addr
                    else:
       -                for o in tx['outputs']:
       -                    o_addr = o.get('address')
       +                for o in tx.outputs:
       +                    o_addr, _ = o
                            if self.is_mine(o_addr) and not self.is_change(o_addr):
                                break
                        else:
       -                    for o in tx['outputs']:
       -                        o_addr = o.get('address')
       +                    for o in tx.outputs:
       +                        o_addr, _ = o
                                if self.is_mine(o_addr):
                                    break
                            else:
       t@@ -956,6 +927,10 @@ class Wallet:
                    return False
        
            def save(self):
       +        tx = {}
       +        for k,v in self.transactions.items():
       +            tx[k] = str(v)
       +            
                s = {
                    'use_encryption': self.use_encryption,
                    'use_change': self.use_change,
       t@@ -973,7 +948,8 @@ class Wallet:
                    'frozen_addresses': self.frozen_addresses,
                    'prioritized_addresses': self.prioritized_addresses,
                    'gap_limit': self.gap_limit,
       -            'transactions': self.transactions,
       +            'transactions': tx,
       +            'tx_height': self.tx_height,
                    'requested_amounts': self.requested_amounts,
                }
                for k, v in s.items():
       t@@ -986,7 +962,7 @@ class Wallet:
                # review stored transactions and send them to the verifier
                # (they are not necessarily in the history, because history items might have have been pruned)
                for tx_hash, tx in self.transactions.items():
       -            tx_height = tx.get('height')
       +            tx_height = self.tx_height[tx_hash]
                    if tx_height <1:
                        print_error( "skipping", tx_hash, tx_height )
                        continue
       t@@ -1002,24 +978,12 @@ class Wallet:
                            # add it in case it was previously unconfirmed
                            self.verifier.add(tx_hash, tx_height)
                            # set the height in case it changed
       -                    tx = self.transactions.get(tx_hash)
       -                    if tx:
       -                        if tx.get('height') != tx_height:
       -                            print_error( "changing height for tx", tx_hash )
       -                            tx['height'] = tx_height
       -
       -
       -    def is_addr_in_tx(self, addr, tx):
       -        found = False
       -        for txin in tx.get('inputs'):
       -            if addr == txin.get('address'): 
       -                found = True
       -                break
       -        for txout in tx.get('outputs'):
       -            if addr == txout.get('address'): 
       -                found = True
       -                break
       -        return found
       +                    txh = self.tx_height.get(tx_hash)
       +                    if txh and txh != tx_height:
       +                        print_error( "changing height for tx", tx_hash )
       +                        self.tx_height[tx_hash] = tx_height
       +
       +
        
        
            def check_new_history(self, addr, hist):
       t@@ -1029,7 +993,7 @@ class Wallet:
                    for tx_hash, height in hist:
                        tx = self.transactions.get(tx_hash)
                        if not tx: continue
       -                if not self.is_addr_in_tx(addr,tx):
       +                if not tx.has_address(addr):
                            return False
        
                # check that we are not "orphaning" a transaction
       t@@ -1053,7 +1017,7 @@ class Wallet:
                        if not tx: continue
                        
                        # already verified?
       -                if tx.get('height'):
       +                if self.tx_height.get(tx_hash):
                            continue
                        # unconfirmed tx
                        print_error("new history is orphaning transaction:", tx_hash)
       t@@ -1071,7 +1035,7 @@ class Wallet:
                            for item in h:
                                if item.get('tx_hash') == tx_hash:
                                    height = item.get('height')
       -                            tx['height'] = height
       +                            self.tx_height[tx_hash] = height
                        if height:
                            print_error("found height for", tx_hash, height)
                            self.verifier.add(tx_hash, height)
       t@@ -1097,7 +1061,7 @@ class Wallet:
        
                # 2 check that referencing addresses are in the tx
                for addr in addresses:
       -            if not self.is_addr_in_tx(addr, tx):
       +            if not tx.has_address(addr):
                        return False
        
                return True
       t@@ -1250,13 +1214,10 @@ class WalletSynchronizer(threading.Thread):
                        tx_height = params[1]
                        assert tx_hash == hash_encode(Hash(result.decode('hex')))
                        tx = Transaction(result)
       -                d = tx.deserialize()
       -                d['height'] = tx_height
       -                d['tx_hash'] = tx_hash
       -                self.wallet.receive_tx_callback(tx_hash, d)
       +                self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
                        self.was_updated = True
                        requested_tx.remove( (tx_hash, tx_height) )
       -                print_error("received tx:", d)
       +                print_error("received tx:", tx)
        
                    elif method == 'blockchain.transaction.broadcast':
                        self.wallet.tx_result = result