Repository: spesmilo/electrum
Issue: AssertionError: 


Crash Report
============

This crash report was reported through the automatic crash reporting system 🤖

Traceback
--------------

```Python traceback
  File "/home/user/wspace/electrum/lib/util.py", line 780, in run_with_except_hook
    run_original(*args2, **kwargs2)
  File "/home/user/wspace/electrum/lib/network.py", line 978, in run
    self.wait_on_sockets()
  File "/home/user/wspace/electrum/lib/network.py", line 960, in wait_on_sockets
    self.process_responses(interface)
  File "/home/user/wspace/electrum/lib/network.py", line 623, in process_responses
    self.process_response(interface, response, callbacks)
  File "/home/user/wspace/electrum/lib/network.py", line 572, in process_response
    callback(response)
  File "/home/user/wspace/electrum/lib/synchronizer.py", line 142, in tx_response
    self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
  File "/home/user/wspace/electrum/lib/wallet.py", line 871, in receive_tx_callback
    self.add_transaction(tx_hash, tx)
  File "/home/user/wspace/electrum/lib/wallet.py", line 774, in add_transaction
    conflicting_txns = self.get_conflicting_transactions(tx)
  File "/home/user/wspace/electrum/lib/wallet.py", line 748, in get_conflicting_transactions
    assert spending_tx_hash in self.transactions

AssertionError: 
```

Reporter
------------

This issue was reported by 4 user(s):

| Electrum Version  | Operating System  | Wallet Type  | Locale |
|---|---|---|---|
| 3.1  | Linux-4.9.0-3-amd64-x86_64-with-debian-9.3 | standard | en_GB |
| 3.1  | Linux-4.15.4-x86_64-Intel-R-_Xeon-R-_CPU_E3-1230_v5_@_3.40GHz-with-slackware-14.2 | standard | de_DE |
| 3.1  | Linux-4.13.0-32-generic-x86_64-with-Ubuntu-18.04-bionic | standard | en_US |
| 3.1  | Darwin-17.4.0-x86_64-i386-64bit | standard | zh_CN |


Additional Information
------------------------


> received a incoming transaction from bitstamp. Version is git head from a few days ago c9ffe8d

---


> i let "network" window on maybe.

---


> 1. from https://testnet.manu.backend.hamburg/faucet get some test coins, 
> 2. send from one address to another in my own wallet (1 isn't get confirmed)
> 3. wait for some minutes

---



Changed files:
lib/wallet.py

diff --git a/lib/wallet.py b/lib/wallet.py
index 4d513ea26..1d99f57fa 100644
--- a/lib/wallet.py
+++ b/lib/wallet.py
@@ -733,7 +733,8 @@ class Abstract_Wallet(PrintError):
     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
-        spent with tx.
+        spent with tx. If the tx is already in wallet history, that will not be
+        reported as a conflict.
         """
         conflicting_txns = set()
         with self.transaction_lock:
@@ -747,12 +748,20 @@ class Abstract_Wallet(PrintError):
                 # this outpoint (ser) has already been spent, by spending_tx
                 assert spending_tx_hash in self.transactions
                 conflicting_txns |= {spending_tx_hash}
+            txid = tx.txid()
+            if txid in conflicting_txns:
+                # this tx is already in history, so it conflicts with itself
+                if len(conflicting_txns) > 1:
+                    raise Exception('Found conflicting transactions already in wallet history.')
+                conflicting_txns -= {txid}
             return conflicting_txns
 
     def add_transaction(self, tx_hash, tx):
         with self.transaction_lock:
-            if tx in self.transactions:
-                return True
+            # NOTE: returning if tx in self.transactions might seem like a good idea
+            # BUT we track is_mine inputs in a txn, and during subsequent calls
+            # of add_transaction tx, we might learn of more-and-more inputs of
+            # being is_mine, as we roll the gap_limit forward
             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()])
@@ -800,7 +809,6 @@ class Abstract_Wallet(PrintError):
                     prevout_hash = txi['prevout_hash']
                     prevout_n = txi['prevout_n']
                     ser = prevout_hash + ':%d'%prevout_n
-                    self.spent_outpoints[ser] = tx_hash
                 # find value from prev output
                 if addr and self.is_mine(addr):
                     dd = self.txo.get(prevout_hash, {})
@@ -809,6 +817,8 @@ class Abstract_Wallet(PrintError):
                             if d.get(addr) is None:
                                 d[addr] = []
                             d[addr].append((ser, v))
+                            # we only track is_mine spends
+                            self.spent_outpoints[ser] = tx_hash
                             break
                     else:
                         self.pruned_txo[ser] = tx_hash