electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit f0b255acba74b5539b847a3fb10874514c701604
parent 67283f0b1b040b20a93501ecbf7f3068643fddb6
Author: thomasv <thomasv@gitorious>
Date:   Fri, 22 Feb 2013 19:22:22 +0100

store transactions in serialized form

Diffstat:
Mlib/bitcoin.py | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/gui_qt.py | 4++--
Mlib/wallet.py | 191++++++++++++++++++++++++++++++++-----------------------------------------------
3 files changed, 134 insertions(+), 117 deletions(-)

diff --git a/lib/bitcoin.py b/lib/bitcoin.py @@ -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(): diff --git a/lib/gui_qt.py b/lib/gui_qt.py @@ -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)) diff --git a/lib/wallet.py b/lib/wallet.py @@ -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 @@ -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') @@ -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): @@ -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) @@ -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 @@ -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) @@ -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() @@ -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 @@ -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: @@ -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, @@ -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(): @@ -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 @@ -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): @@ -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 @@ -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) @@ -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) @@ -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 @@ -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