store transactions in serialized form
This commit is contained in:
parent
67283f0b1b
commit
f0b255acba
|
@ -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():
|
||||
|
|
|
@ -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))
|
||||
|
|
179
lib/wallet.py
179
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
|
||||
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,20 +402,22 @@ 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']
|
||||
|
@ -455,11 +425,13 @@ class Wallet:
|
|||
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:
|
||||
txh = self.tx_height.get(tx_hash)
|
||||
if txh and txh != tx_height:
|
||||
print_error( "changing height for tx", tx_hash )
|
||||
tx['height'] = tx_height
|
||||
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:
|
||||
txh = self.tx_height.get(tx_hash)
|
||||
if txh and txh != tx_height:
|
||||
print_error( "changing height for tx", tx_hash )
|
||||
tx['height'] = tx_height
|
||||
self.tx_height[tx_hash] = 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
|
||||
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue