URI: 
       tsubscribe to / verify headers - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit f91dde55dd8bf710feadcef7d0c806218efa43c3
   DIR parent b3b31fdc07517ea5af36b1eb37d595d5e0d2237f
  HTML Author: thomasv <thomasv@gitorious>
       Date:   Thu, 25 Oct 2012 15:40:30 +0200
       
       subscribe to / verify headers
       
       Diffstat:
         M lib/interface.py                    |       4 ++++
         M lib/verifier.py                     |     111 +++++++++++++++++++++++--------
       
       2 files changed, 89 insertions(+), 26 deletions(-)
       ---
   DIR diff --git a/lib/interface.py b/lib/interface.py
       t@@ -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]
   DIR diff --git a/lib/verifier.py b/lib/verifier.py
       t@@ -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()
        
                
       t@@ -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
       t@@ -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:
       t@@ -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):
       t@@ -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)
       t@@ -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)
       t@@ -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):
       t@@ -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):
       t@@ -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):