document public methods of verifier
[electrum-nvc.git] / lib / verifier.py
index 5332a86..5bbcf00 100644 (file)
@@ -25,30 +25,41 @@ from bitcoin import *
 
 
 class WalletVerifier(threading.Thread):
+    """ Simple Verification Protocol """
 
-    def __init__(self, interface, config, get_transactions):
+    def __init__(self, interface, config):
         threading.Thread.__init__(self)
         self.daemon = True
         self.config = config
         self.interface = interface
-        self.get_transactions = get_transactions
+        self.transactions    = []                                 # monitored transactions
         self.interface.register_channel('verifier')
-        self.verified_tx     = config.get('verified_tx',[])
+        self.verified_tx     = config.get('verified_tx',{})
         self.merkle_roots    = config.get('merkle_roots',{})      # hashed by me
         self.targets         = config.get('targets',{})           # compute targets
         self.lock = threading.Lock()
         self.pending_headers = [] # headers that have not been verified
         self.height = 0
         self.local_height = 0
-        self.set_local_numblocks()
+        self.set_local_height()
 
+    def get_confirmations(self, tx):
+        """ return the number of confirmations of a monitored transaction. """
+        with self.lock:
+            assert tx in self.transactions
+            return (self.local_height - self.verified_tx[tx] + 1) if tx in self.verified_tx else 0
 
-        
+    def add(self, tx):
+        """ add a transaction to the list of monitored transactions. """
+        with self.lock:
+            if tx not in self.transactions:
+                self.transactions.append(tx)
 
     def run(self):
         requested_merkle = []
         requested_chunks = []
         requested_headers = []
+        pending_headers_changed = False
         
         # subscribe to block headers
         self.interface.send([ ('blockchain.headers.subscribe',[])], 'verifier')
@@ -74,8 +85,7 @@ class WalletVerifier(threading.Thread):
                         requested_headers.append(i)
             
             # request missing tx merkle
-            txlist = self.get_transactions()
-            for tx in txlist:
+            for tx in self.transactions:
                 if tx not in self.verified_tx:
                     if tx not in requested_merkle:
                         requested_merkle.append(tx)
@@ -106,18 +116,26 @@ class WalletVerifier(threading.Thread):
             elif method == 'blockchain.headers.subscribe':
                 self.height = result.get('block_height')
                 self.pending_headers.append(result)
+                pending_headers_changed = True
 
             elif method == 'blockchain.block.get_header':
                 height = result.get('block_height')
                 requested_headers.remove(height)
                 self.pending_headers.append(result)
+                pending_headers_changed = True
 
             # process pending headers
-            # todo: sort them first
-            for header in self.pending_headers:
-                self.verify_header(header)
-            self.pending_headers = []
+            if pending_headers_changed:
+                self.pending_headers.sort(key=lambda x: x.get('block_height'))
+                print "pending headers", map(lambda x: x.get('block_height'), self.pending_headers)
+                for header in self.pending_headers:
+                    if self.verify_header(header):
+                        self.pending_headers.remove(header)
+                    else:
+                        break
+                pending_headers_changed = False
 
+            self.interface.trigger_callback('updated')
 
 
     def request_merkle(self, tx_hash):
@@ -130,7 +148,7 @@ class WalletVerifier(threading.Thread):
         header = self.read_header(tx_height)
         if header:
             assert header.get('merkle_root') == self.merkle_roots[tx_hash]
-            self.verified_tx.append(tx_hash)
+            self.verified_tx[tx_hash] = tx_height
             print "verified", tx_hash
             self.config.set_key('verified_tx', self.verified_tx, True)
 
@@ -173,7 +191,7 @@ class WalletVerifier(threading.Thread):
 
         prev_header = self.read_header(height -1)
         if not prev_header:
-            raise "no previous header", height
+            print "no previous header", height
             return
 
         #prev_hash = prev_header.get('block_height')
@@ -195,6 +213,7 @@ class WalletVerifier(threading.Thread):
         if ok:
             self.save_header(header)
             print "verify header: ok", height
+            return True
         
 
             
@@ -248,7 +267,7 @@ class WalletVerifier(threading.Thread):
         f.seek(index*2016*80)
         h = f.write(chunk)
         f.close()
-        self.set_local_numblocks()
+        self.set_local_height()
 
     def save_header(self, header):
         data = self.header_to_string(header).decode('hex')
@@ -259,13 +278,15 @@ class WalletVerifier(threading.Thread):
         f.seek(height*80)
         h = f.write(data)
         f.close()
-        self.set_local_numblocks()
+        self.set_local_height()
+
 
-    def set_local_numblocks(self):
+    def set_local_height(self):
         name = self.path()
         if os.path.exists(name):
-            self.local_height = os.path.getsize(name)/80 - 1
-            # print "local height", self.local_height, os.path.getsize(name)/80.
+            h = os.path.getsize(name)/80 - 1
+            if self.local_height != h:
+                self.local_height = h
 
 
     def read_header(self, block_height):