commit c9ffe8d48a489028c4bed9f83e1374c84759b082
parent c4d31674abec59b8fddbbd480dfe9d96c9cca9e8
Author: ThomasV <thomasv@electrum.org>
Date: Sat, 17 Feb 2018 16:38:39 +0100
Merge pull request #3915 from spesmilo/add_transaction
Add transaction
Diffstat:
M | lib/wallet.py | | | 96 | +++++++++++++++++++++++++++++++++++++++---------------------------------------- |
1 file changed, 47 insertions(+), 49 deletions(-)
diff --git a/lib/wallet.py b/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, items in self.txi.items():
+ for addr, l in items.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 = txi.get('prevout_hash')
+ prevout_n = txi.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,20 @@ 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:
+ # FIXME the test here should be for "not all is_mine"; cannot detect conflict in some cases
+ 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 +773,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 +792,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 +812,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 +830,19 @@ 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)
+ for addr, l in self.txi[tx_hash].items():
+ for ser, v in l:
outpoint_to_txid_map.pop(ser, None)
- else:
- for ser, hh in list(outpoint_to_txid_map.items()):
- if hh == tx_hash:
- 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, None)
undo_spend(self.pruned_txo)
undo_spend(self.spent_outpoints)
@@ -873,13 +872,17 @@ 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)
+ # but remove completely if not is_mine
+ if self.txi[tx_hash] == {}:
+ # FIXME the test here should be for "not all is_mine"; cannot detect conflict in some cases
+ self.remove_transaction(tx_hash)
self.history[addr] = hist
for tx_hash, tx_height in hist:
@@ -981,14 +984,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)