Exemple #1
0
def session(conf=None, config_path=CONFIG_PATH, server_host=None, server_port=None,
            storage_drivers=None, metadata_dir=None, spv_headers_path=None, set_global=False):

    """
    Create a blockstack session:
    * validate the configuration
    * load all storage drivers
    * initialize all storage drivers
    * load an API proxy to blockstack

    conf's fields override specific keyword arguments.

    Returns the API proxy object.
    """

    if conf is None and config_path is not None:
        conf = get_config(config_path)

    if conf is not None:
        if server_host is None:
            server_host = conf['server']
        if server_port is None:
            server_port = conf['port']
        if storage_drivers is None:
            storage_drivers = conf['storage_drivers']
        if metadata_dir is None:
            metadata_dir = conf['metadata']
        if spv_headers_path is None:
            spv_headers_path = conf['blockchain_headers']

    if storage_drivers is None:
        log.error("No storage driver(s) defined in the config file.  Please set 'storage=' to a comma-separated list of drivers")
        sys.exit(1)

    # create proxy
    log.debug('Connect to {}:{}'.format(server_host, server_port))
    proxy = BlockstackRPCClient(server_host, server_port)

    # load all storage drivers
    for storage_driver in storage_drivers.split(","):
        storage_impl = load_storage(storage_driver)
        if storage_impl is None:
            log.error("Failed to load storage driver '%s'" % (storage_driver))
            sys.exit(1)

        rc = register_storage(storage_impl, conf)
        if not rc:
            log.error("Failed to initialize storage driver '%s' (%s)" % (storage_driver, rc))
            sys.exit(1)

    # initialize SPV
    SPVClient.init(spv_headers_path)
    proxy.spv_headers_path = spv_headers_path
    proxy.conf = conf

    if set_global:
        set_default_proxy( proxy )

    return proxy
Exemple #2
0
def session(conf=None, config_path=CONFIG_PATH, server_host=None, server_port=None, wallet_password=None,
            storage_drivers=None, metadata_dir=None, spv_headers_path=None, set_global=False):
    """
    Create a blockstack session:
    * validate the configuration
    * load all storage drivers
    * initialize all storage drivers
    * load an API proxy to blockstack

    conf's fields override specific keyword arguments.

    Returns the API proxy object.
    """

    if set_global:
        if server_host is not None:
            os.environ['BLOCKSTACK_CLI_SERVER_HOST'] = server_host

        if server_port is not None:
            os.environ['BLOCKSTACK_CLI_SERVER_PORT'] = str(server_port)

    if conf is None:
        conf = get_config(config_path)
        if conf is None:
            log.error("Failed to read configuration file {}".format(config_path))
            return None 

        conf_version = conf.get('client_version', '')
        if not semver_match(conf_version, VERSION):
            log.error("Failed to use legacy configuration file {}".format(config_path))
            return None

    if conf is not None:
        if server_host is None:
            server_host = conf['server']
        if server_port is None:
            server_port = conf['port']
        if storage_drivers is None:
            storage_drivers = conf['storage_drivers']
        if metadata_dir is None:
            metadata_dir = conf['metadata']
        if spv_headers_path is None:
            spv_headers_path = conf['bitcoind_spv_path']

    if storage_drivers is None:
        msg = ('No storage driver(s) defined in the config file. '
               'Please set "storage=" to a comma-separated list of drivers')
        log.error(msg)
        sys.exit(1)

    # create proxy
    log.debug('Connect to {}:{}'.format(server_host, server_port))
    proxy = BlockstackRPCClient(server_host, server_port)

    # load all storage drivers
    loaded = []
    for storage_driver in storage_drivers.split(','):
        storage_impl = load_storage(storage_driver)
        if storage_impl is None:
            log.error('Failed to load storage driver "{}"'.format(storage_driver))
            sys.exit(1)
        loaded.append(storage_driver)
        rc = register_storage(storage_impl, conf)
        if not rc:
            log.error('Failed to initialize storage driver "{}" ({})'.format(storage_driver, rc))
            sys.exit(1)
    log.debug('Loaded storage drivers {}'.format(loaded))
    # initialize SPV
    SPVClient.init(spv_headers_path)
    proxy.spv_headers_path = spv_headers_path
    proxy.conf = conf

    if set_global:
        set_default_proxy(proxy)

    return proxy
Exemple #3
0
def serial_number_to_tx(serial_number, bitcoind_proxy, proxy=None):
    """
    Convert a serial number into its transaction in the blockchain.
    Use an untrusted bitcoind connection to get the list of transactions,
    and use trusted SPV headers to ensure that the transaction obtained is on the main chain.
    @bitcoind_proxy must be a BitcoindConnection (from virtualchain.lib.session)

    Return the SPV-verified transaction object (as a dict) on success
    Return None on error
    """

    proxy = get_default_proxy() if proxy is None else proxy

    parts = serial_number.split('-')
    block_id, tx_index = int(parts[0]), int(parts[1])

    timeout = 1.0
    while True:
        try:
            block_hash = bitcoind_proxy.getblockhash(block_id)
            block_data = bitcoind_proxy.getblock(block_hash)
            break
        except Exception as e:
            log.error('Unable to obtain block data; retrying...')
            time.sleep(timeout)
            timeout = timeout * 2 + random.random() * timeout

    rc = SPVClient.sync_header_chain(proxy.spv_headers_path,
                                     bitcoind_proxy.opts['bitcoind_server'],
                                     block_id)

    if not rc:
        msg = 'Failed to synchronize SPV header chain up to {}'
        log.error(msg.format(block_id))
        return None

    # verify block header
    rc = SPVClient.block_header_verify(proxy.spv_headers_path, block_id,
                                       block_hash, block_data)
    if not rc:
        msg = 'Failed to verify block header for {} against SPV headers'
        log.error(msg.format(block_id))
        return None

    # verify block txs
    rc = SPVClient.block_verify(block_data, block_data['tx'])
    if not rc:
        msg = 'Failed to verify block transaction IDs for {} against SPV headers'
        log.error(msg.format(block_id))
        return None

    # sanity check
    if tx_index >= len(block_data['tx']):
        msg = 'Serial number {} references non-existant transaction {} (out of {} txs)'
        log.error(msg.format(serial_number, tx_index, len(block_data['tx'])))
        return None

    # obtain transaction
    txid = block_data['tx'][tx_index]
    tx = bitcoind_proxy.getrawtransaction(txid, 1)

    # verify tx
    rc = SPVClient.tx_verify(block_data['tx'], tx)
    if not rc:
        msg = 'Failed to verify block transaction {} against SPV headers'
        log.error(msg.format(txid))
        return None

    # verify tx index
    if tx_index != SPVClient.tx_index(block_data['tx'], tx):
        msg = ('TX index mismatch: serial number identifies '
               'transaction number {} ({}), but got transaction {}')

        log.error(
            msg.format(
                tx_index, block_data['tx'][tx_index],
                block_data['tx'][SPVClient.tx_index(block_data['tx'], tx)]))
        return None

    # success!
    return tx
Exemple #4
0
def txid_to_block_data(txid, bitcoind_proxy, proxy=None):
    """
    Given a txid, get its block's data.

    Use SPV to verify the information we receive from the (untrusted)
    bitcoind host.

    @bitcoind_proxy must be a BitcoindConnection (from virtualchain.lib.session)

    Return the (block hash, block data, txdata) on success
    Return (None, None, None) on error
    """

    proxy = get_default_proxy() if proxy is None else proxy

    timeout = 1.0
    while True:
        try:
            untrusted_tx_data = bitcoind_proxy.getrawtransaction(txid, 1)
            untrusted_block_hash = untrusted_tx_data['blockhash']
            untrusted_block_data = bitcoind_proxy.getblock(
                untrusted_block_hash)
            break
        except (OSError, IOError) as ie:
            log.exception(ie)
            log.error('Network error; retrying...')
            timeout = timeout * 2 + random.randint(0, timeout)
            continue
        except Exception as e:
            log.exception(e)
            return None, None, None

    # first, can we trust this block? is it in the SPV headers?
    untrusted_block_header_hex = virtualchain.block_header_to_hex(
        untrusted_block_data, untrusted_block_data['previousblockhash'])

    block_id = SPVClient.block_header_index(
        proxy.spv_headers_path,
        ('{}00'.format(untrusted_block_header_hex)).decode('hex'))

    if block_id < 0:
        # bad header
        log.error('Block header "{}" is not in the SPV headers ({})'.format(
            untrusted_block_header_hex, proxy.spv_headers_path))

        return None, None, None

    # block header is trusted.  Is the transaction data consistent with it?
    verified_block_header = virtualchain.block_verify(untrusted_block_data)

    if not verified_block_header:
        msg = ('Block transaction IDs are not consistent '
               'with the Merkle root of the trusted header')

        log.error(msg)

        return None, None, None

    # verify block hash
    verified_block_hash = virtualchain.block_header_verify(
        untrusted_block_data, untrusted_block_data['previousblockhash'],
        untrusted_block_hash)

    if not verified_block_hash:
        log.error('Block hash is not consistent with block header')
        return None, None, None

    # we trust the block hash, block data, and txids
    block_hash = untrusted_block_hash
    block_data = untrusted_block_data
    tx_data = untrusted_tx_data

    return block_hash, block_data, tx_data
Exemple #5
0
            break
        except (OSError, IOError), ie:
            log.exception(ie)
            log.error("Network error; retrying...")
            timeout = timeout * 2 + random.randint(0, timeout)
            continue

        except Exception, e:
            log.exception(e)
            return (None, None, None)

    # first, can we trust this block? is it in the SPV headers?
    untrusted_block_header_hex = virtualchain.block_header_to_hex(
        untrusted_block_data, untrusted_block_data['previousblockhash'])
    block_id = SPVClient.block_header_index(proxy.spv_headers_path,
                                            (untrusted_block_header_hex +
                                             "00").decode('hex'))
    if block_id < 0:
        # bad header
        log.error("Block header '%s' is not in the SPV headers (%s)" %
                  (untrusted_block_header_hex, proxy.spv_headers_path))
        return (None, None, None)

    # block header is trusted.  Is the transaction data consistent with it?
    if not virtualchain.block_verify(untrusted_block_data):
        log.error(
            "Block transaction IDs are not consistent with the trusted header's Merkle root"
        )
        return (None, None, None)

    # verify block hash