handle case where an unconfirmed transaction is pruned by the server. also, fix bug...
authorthomasv <thomasv@gitorious>
Fri, 23 Nov 2012 14:35:54 +0000 (15:35 +0100)
committerthomasv <thomasv@gitorious>
Fri, 23 Nov 2012 14:35:54 +0000 (15:35 +0100)
lib/wallet.py

index 25888aa..29f9bc1 100644 (file)
@@ -414,7 +414,11 @@ class Wallet:
         else:
             # some inputs are mine:
             fee = None
-            v = v_out_mine - v_in
+            if is_send:
+                v = v_out_mine - v_out
+            else:
+                # no input is mine
+                v = v_out_mine
             
         return is_send, v, fee
 
@@ -438,7 +442,7 @@ class Wallet:
             + "Status: %d confirmations\n"%conf
         if is_mine:
             if fee: 
-                tx_details += "Amount sent: %s\n"% format_satoshis(v+fee, False) \
+                tx_details += "Amount sent: %s\n"% format_satoshis(v-fee, False) \
                               + "Transaction fee: %s\n"% format_satoshis(fee, False)
             else:
                 tx_details += "Amount sent: %s\n"% format_satoshis(v, False) \
@@ -655,9 +659,8 @@ class Wallet:
 
     def receive_history_callback(self, addr, hist):
 
-        if hist != ['*']:
-            if not self.check_new_history(addr, hist):
-                raise BaseException("error: received history for %s is not consistent with known transactions"%addr)
+        if not self.check_new_history(addr, hist):
+            raise BaseException("error: received history for %s is not consistent with known transactions"%addr)
             
         with self.lock:
             self.history[addr] = hist
@@ -697,12 +700,9 @@ class Wallet:
             tx_hash = tx['tx_hash']
             timestamp = tx.get('timestamp')
             conf = self.verifier.get_confirmations(tx_hash) if self.verifier else None
-            is_mine, v, fee = self.get_tx_value(tx_hash)
-            if v is not None:
-                balance += v
-                value = v + fee if fee is not None else v
-            else:
-                value = None
+            is_mine, value, fee = self.get_tx_value(tx_hash)
+            if value is not None:
+                balance += value
 
             result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
 
@@ -1069,15 +1069,57 @@ class Wallet:
 
 
     def check_new_history(self, addr, hist):
-        # - check that all tx in hist are relevant
-        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):
-                return False
-
-        # todo: check that we are not "orphaning" a transaction
-        # if we are, reject tx if unconfirmed, else reject the server
+        
+        # check that all tx in hist are relevant
+        if hist != ['*']:
+            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):
+                    return False
+
+        # check that we are not "orphaning" a transaction
+        old_hist = self.history.get(addr)
+        if old_hist == ['*']: return True
+
+        for tx_hash, height in old_hist:
+            if tx_hash in map(lambda x:x[0], hist): continue
+            found = False
+            for _addr, _hist in self.history.items():
+                if _addr == addr: continue
+                if _hist == ['*']: continue
+                _tx_hist = map(lambda x:x[0], _hist)
+                if tx_hash in _tx_hist:
+                    found = True
+                    break
+
+            if not found:
+                tx = self.transactions.get(tx_hash)
+                # already verified?
+                if tx.get('height'):
+                    continue
+                # unconfirmed tx
+                print_error("new history is orphaning transaction:", tx_hash)
+                # check that all outputs are not mine, request histories
+                ext_requests = []
+                for o in tx.get('outputs'):
+                    _addr = o.get('address')
+                    assert not self.is_mine(_addr)
+                    ext_requests.append( ('blockchain.address.get_history', [_addr]) )
+
+                ext_h = self.interface.synchronous_get(ext_requests)
+                height = None
+                for h in ext_h:
+                    if h == ['*']: continue
+                    for item in h:
+                        if item.get('tx_hash') == tx_hash:
+                            height = item.get('height')
+                if height:
+                    print_error("found height for", tx_hash, height)
+                    self.verifier.add(tx_hash, height)
+                else:
+                    print_error("removing orphaned tx from history", tx_hash)
+                    self.transactions.pop(tx_hash)
 
         return True