docs: def build( cls, blockchain_opts, end_block_id, state_engine, expected_snapshots={}, tx_filter=None ):
          """
          Top-level call to process all blocks in the blockchain.
          Goes and fetches all data-bearing transactions in order,
          and feeds them into the state engine implementation.
          
          Note that this method can take some time (hours, days) to complete 
          when called from the first block.
          
          Return True on success 
          Return False on error
          Raise an exception on recoverable error--the caller should simply try again.
          Exit on irrecoverable error--do not try to make forward progress
          """
         
          first_block_id = state_engine.lastblock + 1
          if first_block_id >= end_block_id:
              # built 
              log.debug("Up-to-date ({} >= {})".format(first_block_id, end_block_id))
              return True 
  
          rc = True
          batch_size = config.BLOCK_BATCH_SIZE
          log.debug("Sync virtualchain state from {} to {}".format(first_block_id, end_block_id))
          
          for block_id in range( first_block_id, end_block_id+1, batch_size ):
              
              if not rc:
                  break 
             
              last_block_id = min(block_id + batch_size, end_block_id)
  
              # get the blocks and transactions from the underlying blockchain
              block_ids_and_txs = transactions.get_virtual_transactions(state_engine.impl.get_blockchain(), blockchain_opts, block_id, last_block_id, tx_filter=tx_filter, spv_last_block=end_block_id - 1)
              if block_ids_and_txs is None:
                  raise Exception("Failed to get virtual transactions {} to {}".format(block_id, last_block_id))
  
              # process in order by block ID
              block_ids_and_txs.sort()
              for processed_block_id, txs in block_ids_and_txs:
  
                  if state_engine.get_consensus_at(processed_block_id) is not None:
                      raise Exception("Already processed block %s (%s)" % (processed_block_id, state_engine.get_consensus_at( processed_block_id )) )
                  
                  cls.db_set_indexing(True, state_engine.impl, state_engine.working_dir)
  
                  ops = state_engine.parse_block(processed_block_id, txs)
                  consensus_hash = state_engine.process_block(processed_block_id, ops, expected_snapshots=expected_snapshots)
  
                  cls.db_set_indexing(False, state_engine.impl, state_engine.working_dir)
                  
                  if consensus_hash is None:
                      # request to stop
                      rc = False
                      log.debug("Stopped processing at block %s" % processed_block_id)
                      break
  
                  log.debug("CONSENSUS({}): {}".format(processed_block_id, state_engine.get_consensus_at(processed_block_id)))
  
                  # sanity check, if given 
                  expected_consensus_hash = state_engine.get_expected_consensus_at( processed_block_id )
                  if expected_consensus_hash is not None:
                      if str(consensus_hash) != str(expected_consensus_hash):
                          rc = False
                          log.error("FATAL: DIVERGENCE DETECTED AT {}: {} != {}".format(processed_block_id, consensus_hash, expected_consensus_hash))
                          traceback.print_stack()
                          os.abort()
         
              if not rc:
                  break
          
          log.debug("Last block is %s" % state_engine.lastblock )
          return rc