Get the sequence of virtualchain transactions from the blockchain.
    Each transaction returned will be a `nulldata` transaction (i.e. the first output script starts with OP_RETURN).
    * output values will be in satoshis
    * `fee` will be defined, and will be the total amount sent (in satoshis)
    * `txindex` will be defined, and will be the offset in the block where the tx occurs
    * `senders` will be defined as a list, and will contain the following information
        * `script_pubkey`: an output scriptPubKey hex script
        * `units`: a value in satoshis
        * `addresses`: a list of zero or more addresses
      This list corresponds to the list of outputs that funded the given transaction's inputs.
      That is, senders[i] corresponds to the output that funded vin[i], found in transaction vin[i]['txid']
    * `nulldata` will be define as the hex string that encodes the OP_RETURN payload

    @blockchain_opts must be a dict with the following keys:
    * `bitcoind_server`: hostname of the bitcoind peer
    * `bitcoind_port`: RPC port of the bitcoind peer
    * `bitcoind_p2p_port`: p2p port of the bitcoind peer
    * `bitcoind_user`: username to authenticate
    * `bitcoind_passwd`: password for authentication
    * `bitcoind_spv_path`: path on disk to where SPV headers should be stored

    Returns a list of [(block number), [txs]] on success
    Returns None on error

def get_bitcoin_virtual_transactions(blockchain_opts, first_block_height, last_block_height, tx_filter=None, spv_last_block=None, first_block_hash=None, **hints):
    """
    Get the sequence of virtualchain transactions from the blockchain.
    Each transaction returned will be a `nulldata` transaction (i.e. the first output script starts with OP_RETURN).
    * output values will be in satoshis
    * `fee` will be defined, and will be the total amount sent (in satoshis)
    * `txindex` will be defined, and will be the offset in the block where the tx occurs
    * `senders` will be defined as a list, and will contain the following information
        * `script_pubkey`: an output scriptPubKey hex script
        * `units`: a value in satoshis
        * `addresses`: a list of zero or more addresses
      This list corresponds to the list of outputs that funded the given transaction's inputs.
      That is, senders[i] corresponds to the output that funded vin[i], found in transaction vin[i]['txid']
    * `nulldata` will be define as the hex string that encodes the OP_RETURN payload

    @blockchain_opts must be a dict with the following keys:
    * `bitcoind_server`: hostname of the bitcoind peer
    * `bitcoind_port`: RPC port of the bitcoind peer
    * `bitcoind_p2p_port`: p2p port of the bitcoind peer
    * `bitcoind_user`: username to authenticate
    * `bitcoind_passwd`: password for authentication
    * `bitcoind_spv_path`: path on disk to where SPV headers should be stored

    Returns a list of [(block number), [txs]] on success
    Returns None on error
    """

    headers_path = blockchain_opts['bitcoind_spv_path']
    bitcoind_server = "%s:%s" % (blockchain_opts['bitcoind_server'], blockchain_opts['bitcoind_p2p_port'])
    spv_last_block = spv_last_block if spv_last_block is not None else last_block_height - 1

    if headers_path is None:
        log.error("FATAL: bitcoind_spv_path not defined in blockchain options")
        os.abort()

    if not os.path.exists(headers_path):
        log.debug("Will download SPV headers to %s" % headers_path)

    # synchronize SPV headers
    SPVClient.init( headers_path )

    rc = None
    for i in xrange(0, 65536, 1):
        # basically try forever
        try:
            rc = SPVClient.sync_header_chain( headers_path, bitcoind_server, spv_last_block )
            if not rc:
                delay = min( 600, 2**i + ((2**i) * random.random()) )
                log.error("Failed to synchronize SPV headers (%s) up to %s.  Try again in %s seconds" % (headers_path, last_block_height, delay))
                time.sleep( delay )
                continue

            else:
                break

        except SystemExit, s:
            log.error("Aborting on SPV header sync")
            os.abort()

        except Exception, e:
            log.exception(e)
            delay = min( 600, 2**i + ((2**i) * random.random()) )
            log.debug("Try again in %s seconds" % delay)
            time.sleep( delay )
            continue

    downloader = None
    for i in xrange(0, 65536, 1):
        # basically try forever
        try:
            
            # fetch all blocks
            downloader = BlockchainDownloader( blockchain_opts, blockchain_opts['bitcoind_spv_path'], first_block_height, last_block_height - 1, \
                                       p2p_port=blockchain_opts['bitcoind_p2p_port'], tx_filter=tx_filter )

            if first_block_height > last_block_height - 1:
                downloader.loop_exit()
                break

            rc = downloader.run()
            if not rc:
                delay = min( 600, 2**i + ((2**i) * random.random()) )
                log.error("Failed to fetch %s-%s; trying again in %s seconds" % (first_block_height, last_block_height, delay))
                time.sleep( delay )
                continue
            else:
                break

        except SystemExit, s:
            log.error("Aborting on blockchain sync")
            os.abort()

        except Exception, e:
            log.exception(e)
            delay = min( 600, 2**i + ((2**i) * random.random()) )
            log.debug("Try again in %s seconds" % delay)
            time.sleep( delay )
            continue            

    if not rc or downloader is None:
        log.error("Failed to fetch blocks %s-%s" % (first_block_height, last_block_height))
        return None

    # extract
    block_info = downloader.get_block_info()
    return block_info