Handle a 'headers' message.
        NOTE: we request headers in order, so we will expect to receive them in order here.
        Verify that we do so.

def handle_headers( self, message_header, block_headers_message ):
        """
        Handle a 'headers' message.
        NOTE: we request headers in order, so we will expect to receive them in order here.
        Verify that we do so.
        """
        log.debug("handle headers (%s)" % len(block_headers_message.headers))

        block_headers = block_headers_message.headers

        serializer = BlockHeaderSerializer()

        # verify that the local header chain connects to this sequence
        current_height = SPVClient.height( self.path )
        if current_height is None:
            assert USE_TESTNET
            current_height = -1

        assert (current_height >= 0 and USE_MAINNET) or USE_TESTNET, "Invalid height %s" % current_height
    
        last_header = None

        if current_height >= 0:
            last_header = SPVClient.read_header( self.path, current_height )
            log.debug("Receive %s headers (%s to %s)" % (len(block_headers), current_height, current_height + len(block_headers)))

        else:
            # first testnet header
            log.debug("Receive %s testnet headers (%s to %s)" % (len(block_headers), current_height + 1, current_height + len(block_headers)))
            last_header = {
                "version": block_headers[0].version,
                "prev_block_hash": "%064x" % block_headers[0].prev_block,
                "merkle_root": "%064x" % block_headers[0].merkle_root,
                "timestamp": block_headers[0].timestamp,
                "bits": block_headers[0].bits,
                "nonce": block_headers[0].nonce,
                "hash": block_headers[0].calculate_hash()
            }

        if (USE_MAINNET or (USE_TESTNET and current_height >= 0)) and last_header['hash'] != self.hash_to_string(block_headers[0].prev_block):
            raise Exception("Received discontinuous block header at height %s: hash '%s' (expected '%s')" % \
                    (current_height,
                    self.hash_to_string(block_headers[0].prev_block),
                    last_header['hash'] ))

        header_start = 1
        if USE_TESTNET and current_height < 0:
            # save initial header
            header_start = 0

        # verify that this sequence of headers constitutes a hash chain 
        for i in xrange(header_start, len(block_headers)):
            prev_block_hash = self.hash_to_string(block_headers[i].prev_block)
            if i > 0 and prev_block_hash != block_headers[i-1].calculate_hash():
                raise Exception("Block '%s' is not continuous with block '%s'" % \
                        prev_block_hash,
                        block_headers[i-1].calculate_hash())

        if current_height < 0:
            # save the first header 
            if not os.path.exists(self.path):
                with open(self.path, "wb") as f:
                    block_header_serializer = BlockHeaderSerializer()
                    bin_data = block_header_serializer.serialize( block_headers[0] )
                    f.write( bin_data )

            # got all headers, including the first
            current_height = 0

        # insert into to local headers database
        next_block_id = current_height + 1
        for block_header in block_headers:
            with open(self.path, "rb+") as f:

                # omit tx count 
                block_header.txns_count = 0
                bin_data = serializer.serialize( block_header )

                if len(bin_data) != BLOCK_HEADER_SIZE:
                    raise Exception("Block %s (%s) has %s-byte header" % (next_block_id, block_header.calculate_hash(), len(bin_data)))

                # NOTE: the fact that we use seek + write ensures that we can:
                # * restart synchronizing at any point
                # * allow multiple processes to work on the chain safely (even if they're duplicating effort)
                f.seek( BLOCK_HEADER_SIZE * next_block_id, os.SEEK_SET )
                f.write( bin_data )

                if SPVClient.height( self.path ) >= self.last_block_id - 1:
                    break

                next_block_id += 1
            
        current_block_id = SPVClient.height( self.path )
        if current_block_id >= self.last_block_id - 1:
            # got all headers
            self.loop_exit()
            return

        prev_block_header = SPVClient.read_header( self.path, current_block_id )
        prev_block_hash = prev_block_header['hash']
        self.send_getheaders( prev_block_hash )