subscribe to / verify headers
authorthomasv <thomasv@gitorious>
Thu, 25 Oct 2012 13:40:30 +0000 (15:40 +0200)
committerthomasv <thomasv@gitorious>
Thu, 25 Oct 2012 13:40:30 +0000 (15:40 +0200)
lib/interface.py
lib/verifier.py

index 1a6885b..c12eea1 100644 (file)
@@ -97,6 +97,10 @@ class Interface(threading.Thread):
                 result = params[0]
                 params = []
 
+            elif method == 'blockchain.headers.subscribe':
+                result = params[0]
+                params = []
+
             elif method == 'blockchain.address.subscribe':
                 addr = params[0]
                 result = params[1]
index c95a607..6aa4719 100644 (file)
@@ -38,8 +38,14 @@ class WalletVerifier(threading.Thread):
         self.targets         = config.get('targets',{})           # compute targets
         self.lock = threading.Lock()
 
-        #self.config.set_key('verified_tx', [], True)
-        #for i in range(70): self.get_target(i)
+        self.pending_headers = [] # headers that have not been verified
+
+        self.height = 0
+        self.local_height = 0
+        self.set_local_numblocks()
+
+        #prev_header = self.read_header(0)
+        #print prev_header
         #sys.exit()
 
         
@@ -47,10 +53,14 @@ class WalletVerifier(threading.Thread):
     def run(self):
         requested_merkle = []
         requested_chunks = []
+        requested_headers = []
+        
+        # subscribe to block headers
+        self.interface.send([ ('blockchain.headers.subscribe',[])], 'verifier')
 
         while True:
             # request missing chunks
-            max_index = self.wallet.blocks/2016
+            max_index = (self.height+1)/2016
             if not requested_chunks:
                 for i in range(0, max_index + 1):
                     # test if we can read the first header of the chunk
@@ -60,8 +70,14 @@ class WalletVerifier(threading.Thread):
                     requested_chunks.append(i)
                     break
 
-            # todo: request missing blocks too
-
+            # request missing headers
+            if not requested_chunks and self.local_height:
+                for i in range(self.local_height + 1, self.height + 1):
+                    if i not in requested_headers:
+                        print "requesting header", i
+                        self.interface.send([ ('blockchain.block.get_header',[i])], 'verifier')
+                        requested_headers.append(i)
+            
             # request missing tx merkle
             txlist = self.wallet.get_tx_hashes()
             for tx in txlist:
@@ -92,8 +108,21 @@ class WalletVerifier(threading.Thread):
                 self.verify_chunk(index, result)
                 requested_chunks.remove(index)
 
+            elif method == 'blockchain.headers.subscribe':
+                self.height = result.get('block_height')
+                self.pending_headers.append(result)
+
             elif method == 'blockchain.block.get_header':
-                self.verify_header(result)
+                height = result.get('block_height')
+                requested_headers.remove(height)
+                self.pending_headers.append(result)
+
+            # process pending headers
+            # todo: sort them first
+            for header in self.pending_headers:
+                self.verify_header(header)
+            self.pending_headers = []
+
 
 
     def request_merkle(self, tx_hash):
@@ -114,8 +143,8 @@ class WalletVerifier(threading.Thread):
     def verify_chunk(self, index, hexdata):
         data = hexdata.decode('hex')
         height = index*2016
-        numblocks = len(data)/80
-        print "validate_chunk", index, numblocks
+        num = len(data)/80
+        print "validate_chunk", index, num
 
         if index == 0:  
             previous_hash = ("0"*64)
@@ -126,7 +155,7 @@ class WalletVerifier(threading.Thread):
 
         bits, target = self.get_target(index)
 
-        for i in range(numblocks):
+        for i in range(num):
             height = index*2016 + i
             raw_header = data[i*80:(i+1)*80]
             header = self.header_from_string(raw_header)
@@ -141,24 +170,38 @@ class WalletVerifier(threading.Thread):
         self.save_chunk(index, data)
 
 
-    def validate_header(self, header):
-        """ if there is a previous or a next block in the list, check the hash"""
+    def verify_header(self, header):
+        # add header to the blockchain file
+        # if there is a reorg, push it in a stack
+
         height = header.get('block_height')
-        with self.lock:
-            self.headers[height] = header # detect conflicts
-            prev_header = next_header = None
-            if height-1 in self.headers:
-                prev_header = self.headers[height-1]
-            if height+1 in self.headers:
-                next_header = self.headers[height+1]
-
-        if prev_header:
-            prev_hash = self.hash_header(prev_header)
+
+        prev_header = self.read_header(height -1)
+        if not prev_header:
+            raise "no previous header", height
+            return
+
+        #prev_hash = prev_header.get('block_height')
+        prev_hash = self.hash_header(prev_header)
+        bits, target = self.get_target(height/2016)
+        _hash = self.hash_header(header)
+        try:
             assert prev_hash == header.get('prev_block_hash')
+            assert bits == header.get('bits')
+            assert eval('0x'+_hash) < target
+            ok = True
+        except:
+            print "verify header failed", header
+            raise
+            # this could be caused by a reorg. request the previous header
+            ok = False
+            #request previous one
+
+        if ok:
             self.save_header(header)
-        if next_header:
-            _hash = self.hash_header(header)
-            assert _hash == next_header.get('prev_block_hash')
+            print "verify header: ok", height
+        
+
             
 
     def header_to_string(self, res):
@@ -194,14 +237,12 @@ class WalletVerifier(threading.Thread):
             h = Hash( h + hash_decode(item[1:]) ) if is_left else Hash( hash_decode(item[1:]) + h )
         return hash_encode(h)
 
-
     def path(self):
         wdir = user_dir()
         if not os.path.exists( wdir ):
             wdir = os.path.dirname(self.config.path)
         return os.path.join( wdir, 'blockchain_headers')
 
-
     def save_chunk(self, index, chunk):
         filename = self.path()
         if os.path.exists(filename):
@@ -212,6 +253,24 @@ class WalletVerifier(threading.Thread):
         f.seek(index*2016*80)
         h = f.write(chunk)
         f.close()
+        self.set_local_numblocks()
+
+    def save_header(self, header):
+        data = self.header_to_string(header).decode('hex')
+        assert len(data) == 80
+        height = header.get('block_height')
+        filename = self.path()
+        f = open(filename,'rw+')
+        f.seek(height*80)
+        h = f.write(data)
+        f.close()
+        self.set_local_numblocks()
+
+    def set_local_numblocks(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.
 
 
     def read_header(self, block_height):