simplify local transactions:
- restrict conflict detection own inputs - save local transactions only if they are own
This commit is contained in:
parent
c4d31674ab
commit
586074cb0f
103
lib/wallet.py
103
lib/wallet.py
|
@ -278,12 +278,10 @@ class Abstract_Wallet(PrintError):
|
|||
@profiler
|
||||
def build_spent_outpoints(self):
|
||||
self.spent_outpoints = {}
|
||||
for txid, tx in self.transactions.items():
|
||||
for txi in tx.inputs():
|
||||
ser = Transaction.get_outpoint_from_txin(txi)
|
||||
if ser is None:
|
||||
continue
|
||||
self.spent_outpoints[ser] = txid
|
||||
for txid in self.txi:
|
||||
for addr, l in self.txi[txid].items():
|
||||
for ser, v in l:
|
||||
self.spent_outpoints[ser] = txid
|
||||
|
||||
@profiler
|
||||
def check_history(self):
|
||||
|
@ -709,7 +707,12 @@ class Abstract_Wallet(PrintError):
|
|||
h.append((tx_hash, tx_height))
|
||||
return h
|
||||
|
||||
def find_pay_to_pubkey_address(self, prevout_hash, prevout_n):
|
||||
def get_txin_address(self, txi):
|
||||
addr = txi.get('address')
|
||||
if addr != "(pubkey)":
|
||||
return addr
|
||||
prevout_hash = x.get('prevout_hash')
|
||||
prevout_n = x.get('prevout_n')
|
||||
dd = self.txo.get(prevout_hash, {})
|
||||
for addr, l in dd.items():
|
||||
for n, v, is_cb in l:
|
||||
|
@ -717,6 +720,16 @@ class Abstract_Wallet(PrintError):
|
|||
self.print_error("found pay-to-pubkey address:", addr)
|
||||
return addr
|
||||
|
||||
def get_txout_address(self, txo):
|
||||
_type, x, v = txo
|
||||
if _type == TYPE_ADDRESS:
|
||||
addr = x
|
||||
elif _type == TYPE_PUBKEY:
|
||||
addr = bitcoin.public_key_to_p2pkh(bfh(x))
|
||||
else:
|
||||
addr = None
|
||||
return addr
|
||||
|
||||
def get_conflicting_transactions(self, tx):
|
||||
"""Returns a set of transaction hashes from the wallet history that are
|
||||
directly conflicting with tx, i.e. they have common outpoints being
|
||||
|
@ -737,11 +750,19 @@ class Abstract_Wallet(PrintError):
|
|||
return conflicting_txns
|
||||
|
||||
def add_transaction(self, tx_hash, tx):
|
||||
if tx in self.transactions:
|
||||
return True
|
||||
is_coinbase = tx.inputs()[0]['type'] == 'coinbase'
|
||||
related = False
|
||||
with self.transaction_lock:
|
||||
if tx in self.transactions:
|
||||
return True
|
||||
is_coinbase = tx.inputs()[0]['type'] == 'coinbase'
|
||||
tx_height = self.get_tx_height(tx_hash)[0]
|
||||
is_mine = any([self.is_mine(txin['address']) for txin in tx.inputs()])
|
||||
# do not save if tx is local and not mine
|
||||
if tx_height == TX_HEIGHT_LOCAL and not is_mine:
|
||||
return False
|
||||
# raise exception if unrelated to wallet
|
||||
is_for_me = any([self.is_mine(self.get_txout_address(txo)) for txo in tx.outputs()])
|
||||
if not is_mine and not is_for_me:
|
||||
raise UnrelatedTransactionException()
|
||||
# Find all conflicting transactions.
|
||||
# In case of a conflict,
|
||||
# 1. confirmed > mempool > local
|
||||
|
@ -751,7 +772,6 @@ class Abstract_Wallet(PrintError):
|
|||
# or drop this txn
|
||||
conflicting_txns = self.get_conflicting_transactions(tx)
|
||||
if conflicting_txns:
|
||||
tx_height = self.get_tx_height(tx_hash)[0]
|
||||
existing_mempool_txn = any(
|
||||
self.get_tx_height(tx_hash2)[0] in (TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT)
|
||||
for tx_hash2 in conflicting_txns)
|
||||
|
@ -771,21 +791,17 @@ class Abstract_Wallet(PrintError):
|
|||
to_remove |= self.get_depending_transactions(conflicting_tx_hash)
|
||||
for tx_hash2 in to_remove:
|
||||
self.remove_transaction(tx_hash2)
|
||||
|
||||
# add inputs
|
||||
self.txi[tx_hash] = d = {}
|
||||
for txi in tx.inputs():
|
||||
addr = txi.get('address')
|
||||
addr = self.get_txin_address(txi)
|
||||
if txi['type'] != 'coinbase':
|
||||
prevout_hash = txi['prevout_hash']
|
||||
prevout_n = txi['prevout_n']
|
||||
ser = prevout_hash + ':%d'%prevout_n
|
||||
self.spent_outpoints[ser] = tx_hash
|
||||
if addr == "(pubkey)":
|
||||
addr = self.find_pay_to_pubkey_address(prevout_hash, prevout_n)
|
||||
# find value from prev output
|
||||
if addr and self.is_mine(addr):
|
||||
related = True
|
||||
dd = self.txo.get(prevout_hash, {})
|
||||
for n, v, is_cb in dd.get(addr, []):
|
||||
if n == prevout_n:
|
||||
|
@ -795,20 +811,13 @@ class Abstract_Wallet(PrintError):
|
|||
break
|
||||
else:
|
||||
self.pruned_txo[ser] = tx_hash
|
||||
|
||||
# add outputs
|
||||
self.txo[tx_hash] = d = {}
|
||||
for n, txo in enumerate(tx.outputs()):
|
||||
v = txo[2]
|
||||
ser = tx_hash + ':%d'%n
|
||||
_type, x, v = txo
|
||||
if _type == TYPE_ADDRESS:
|
||||
addr = x
|
||||
elif _type == TYPE_PUBKEY:
|
||||
addr = bitcoin.public_key_to_p2pkh(bfh(x))
|
||||
else:
|
||||
addr = None
|
||||
addr = self.get_txout_address(txo)
|
||||
if addr and self.is_mine(addr):
|
||||
related = True
|
||||
if d.get(addr) is None:
|
||||
d[addr] = []
|
||||
d[addr].append((n, v, is_coinbase))
|
||||
|
@ -820,30 +829,20 @@ class Abstract_Wallet(PrintError):
|
|||
if dd.get(addr) is None:
|
||||
dd[addr] = []
|
||||
dd[addr].append((ser, v))
|
||||
|
||||
if not related:
|
||||
raise UnrelatedTransactionException()
|
||||
|
||||
# save
|
||||
self.transactions[tx_hash] = tx
|
||||
return True
|
||||
|
||||
def remove_transaction(self, tx_hash):
|
||||
def undo_spend(outpoint_to_txid_map):
|
||||
if tx:
|
||||
# if we have the tx, this should often be faster
|
||||
for txi in tx.inputs():
|
||||
ser = Transaction.get_outpoint_from_txin(txi)
|
||||
outpoint_to_txid_map.pop(ser, None)
|
||||
else:
|
||||
for ser, hh in list(outpoint_to_txid_map.items()):
|
||||
if hh == tx_hash:
|
||||
for addr, l in self.txi[tx_hash].items():
|
||||
for ser, v in l:
|
||||
if ser in outpoint_to_txid:
|
||||
outpoint_to_txid_map.pop(ser)
|
||||
|
||||
with self.transaction_lock:
|
||||
self.print_error("removing tx from history", tx_hash)
|
||||
#tx = self.transactions.pop(tx_hash)
|
||||
tx = self.transactions.get(tx_hash, None)
|
||||
self.transactions.pop(tx_hash)
|
||||
undo_spend(self.pruned_txo)
|
||||
undo_spend(self.spent_outpoints)
|
||||
|
||||
|
@ -873,13 +872,16 @@ class Abstract_Wallet(PrintError):
|
|||
|
||||
def receive_history_callback(self, addr, hist, tx_fees):
|
||||
with self.lock:
|
||||
old_hist = self.history.get(addr, [])
|
||||
old_hist = self.get_address_history(addr)
|
||||
for tx_hash, height in old_hist:
|
||||
if (tx_hash, height) not in hist:
|
||||
# make tx local
|
||||
self.unverified_tx.pop(tx_hash, None)
|
||||
self.verified_tx.pop(tx_hash, None)
|
||||
self.verifier.merkle_roots.pop(tx_hash, None)
|
||||
# make tx local if is_mine, else remove it
|
||||
if self.txi[tx_hash] != {}:
|
||||
self.unverified_tx.pop(tx_hash, None)
|
||||
self.verified_tx.pop(tx_hash, None)
|
||||
self.verifier.merkle_roots.pop(tx_hash, None)
|
||||
else:
|
||||
self.remove_transaction(tx_hash)
|
||||
self.history[addr] = hist
|
||||
|
||||
for tx_hash, tx_height in hist:
|
||||
|
@ -981,14 +983,9 @@ class Abstract_Wallet(PrintError):
|
|||
output_addresses = []
|
||||
for x in tx.inputs():
|
||||
if x['type'] == 'coinbase': continue
|
||||
addr = x.get('address')
|
||||
if addr == None: continue
|
||||
if addr == "(pubkey)":
|
||||
prevout_hash = x.get('prevout_hash')
|
||||
prevout_n = x.get('prevout_n')
|
||||
_addr = self.wallet.find_pay_to_pubkey_address(prevout_hash, prevout_n)
|
||||
if _addr:
|
||||
addr = _addr
|
||||
addr = self.get_txin_address(x)
|
||||
if addr is None:
|
||||
continue
|
||||
input_addresses.append(addr)
|
||||
for addr, v in tx.get_outputs():
|
||||
output_addresses.append(addr)
|
||||
|
|
Loading…
Reference in New Issue