Top-level block processing method.
        Feed the block and its data transactions 
        through the implementation, to build up the 
        implementation's state.  Cache the 
        resulting data to disk.
       
        Return the (consensus hash, ops hash) for this block on success.
        Exit on failure.

def process_block(self, block_id, ops, backup=False, expected_snapshots=None):
        """
        Top-level block processing method.
        Feed the block and its data transactions 
        through the implementation, to build up the 
        implementation's state.  Cache the 
        resulting data to disk.
       
        Return the (consensus hash, ops hash) for this block on success.
        Exit on failure.
        """
        
        log.debug("Process block {} ({} virtual transactions)".format(block_id, len(ops)))

        if expected_snapshots is None:
            expected_snapshots = self.expected_snapshots
        if expected_snapshots is None:
            expected_snapshots = {}
        
        new_ops = self.process_ops(block_id, ops)
        consensus_hash, ops_hash = self.snapshot(block_id, new_ops['virtualchain_ordered'])

        # sanity check against a known sequence of consensus hashes
        if block_id in expected_snapshots:
            log.debug("Expecting CONSENSUS({}) == {}".format(block_id, expected_snapshots[block_id]))
            if expected_snapshots[block_id] != consensus_hash:
                log.error("FATAL: consensus hash mismatch at height {}: {} != {}".format(block_id, expected_snapshots[block_id], consensus_hash))
                traceback.print_stack()
                os.abort()
        
        # remove virtualchain-reserved keys
        sanitized_ops = []
        virtualchain_ops_hints = []
        for opdata in new_ops['virtualchain_ordered']:
            op_sanitized, op_reserved = self.remove_reserved_keys(opdata)
            sanitized_ops.append(op_sanitized)
            virtualchain_ops_hints.append(op_reserved)

        # save state for this block
        rc = self.save(block_id, consensus_hash, ops_hash, sanitized_ops, virtualchain_ops_hints, backup=backup)
        if not rc:
            # implementation requests early termination 
            log.debug("Early indexing termination at {}".format(block_id))
            return None

        # store statistics if we're in test mode 
        if os.environ.get("BLOCKSTACK_TEST"):
            global STATISTICS
            STATISTICS[block_id] = {
                'consensus_hash': consensus_hash,
                'num_parsed_ops': len(ops),
                'num_processed_ops': len(new_ops['virtualchain_ordered']),
                'ops_hash': ops_hash,
                'backup': backup,
            }

        return consensus_hash