def default_blockchain_info_opts(config_file=None): """ Get our default blockchain.info options from a config file. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read(config_file) blockchain_info_opts = {} api_token = None if parser.has_section("blockchain_info"): if parser.has_option("blockchain_info", "api_token"): api_token = parser.get("blockchain_info", "api_token") blockchain_info_opts = { "utxo_provider": "blockchain_info", "api_token": api_token } # strip Nones for (k, v) in blockchain_info_opts.items(): if v is None: del blockchain_info_opts[k] return blockchain_info_opts
def default_blockchain_info_opts( config_file=None ): """ Get our default blockchain.info options from a config file. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read( config_file ) blockchain_info_opts = {} api_token = None if parser.has_section("blockchain_info"): if parser.has_option("blockchain_info", "api_token"): api_token = parser.get("blockchain_info", "api_token") blockchain_info_opts = { "utxo_provider": "blockchain_info", "api_token": api_token } # strip Nones for (k, v) in blockchain_info_opts.items(): if v is None: del blockchain_info_opts[k] return blockchain_info_opts
def default_chaincom_opts(config_file=None): """ Get our default chain.com options from a config file. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read(config_file) chaincom_opts = {} api_key_id = None api_key_secret = None if parser.has_section("chain_com"): if parser.has_option("chain_com", "api_key_id"): api_key_id = parser.get("chain_com", "api_key_id") if parser.has_option("chain_com", "api_key_secret"): api_key_secret = parser.get("chain_com", "api_key_secret") chaincom_opts = {"utxo_provider": "chain_com", "api_key_id": api_key_id, "api_key_secret": api_key_secret} # strip Nones for (k, v) in chaincom_opts.items(): if v is None: del chaincom_opts[k] return chaincom_opts
def default_blockcypher_opts( config_file=None ): """ Get our default blockcypher.com options from a config file. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read( config_file ) blockcypher_opts = {} api_token = None if parser.has_section('blockcypher'): if parser.has_option('blockcypher', 'api_token'): api_token = parser.get('blockcypher', 'api_token') blockcypher_opts = { 'utxo_provider': "blockcypher", 'api_token': api_token } # strip Nones for (k, v) in blockcypher_opts.items(): if v is None: del blockcypher_opts[k] return blockcypher_opts
def default_chaincom_opts(config_file=None): """ Get our default chain.com options from a config file. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read(config_file) chaincom_opts = {} api_key_id = None api_key_secret = None if parser.has_section('chain_com'): if parser.has_option('chain_com', 'api_key_id'): api_key_id = parser.get('chain_com', 'api_key_id') if parser.has_option('chain_com', 'api_key_secret'): api_key_secret = parser.get('chain_com', 'api_key_secret') chaincom_opts = { 'api_key_id': api_key_id, 'api_key_secret': api_key_secret } # strip Nones for (k, v) in chaincom_opts.items(): if v is None: del chaincom_opts[k] return chaincom_opts
def default_blockcypher_opts(config_file=None): """ Get our default blockcypher.com options from a config file. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read(config_file) blockcypher_opts = {} api_token = None if parser.has_section('blockcypher'): if parser.has_option('blockcypher', 'api_token'): api_token = parser.get('blockcypher', 'api_token') blockcypher_opts = {'utxo_provider': "blockcypher", 'api_token': api_token} # strip Nones for (k, v) in blockcypher_opts.items(): if v is None: del blockcypher_opts[k] return blockcypher_opts
def configure( config_file=None, force=False, interactive=True ): """ Configure blockstore: find and store configuration parameters to the config file. Optionally prompt for missing data interactively (with interactive=True). Or, raise an exception if there are any fields missing. Optionally force a re-prompting for all configuration details (with force=True) Return (bitcoind_opts, chaincom_opts) """ if config_file is None: try: config_file = virtualchain.get_config_filename() except: pass bitcoind_message = "Blockstore does not have enough information to connect\n" bitcoind_message += "to bitcoind. Please supply the following parameters:" bitcoind_opts = {} bitcoind_params = ["server", "port", "user", "passwd", "use_https"] chaincom_message = 'NOTE: Blockstore currently requires API access to chain.com\n' chaincom_message += 'for getting unspent outputs. We will add support for using\n' chaincom_message += 'bitcoind and/or other API providers in the next release.\n' chaincom_message += "\n" chaincom_message += "If you have not done so already, please go to https://chain.com\n" chaincom_message += "and register for an API key and secret. Once you have them," chaincom_message += "please enter them here." chaincom_opts = {} chaincom_params = ["api_key_id", "api_key_secret"] if not force: # get current set of bitcoind opts tmp_bitcoind_opts = default_bitcoind_opts( config_file=config_file ) bitcoind_opts = opt_strip( "bitcoind_", tmp_bitcoind_opts ) # get current set of chaincom opts chaincom_opts = default_chaincom_opts( config_file=config_file ) # get any missing fields bitcoind_opts, missing_bitcoin_opts = find_missing( bitcoind_message, bitcoind_params, bitcoind_opts, prompt_missing=interactive ) chaincom_opts, missing_chaincom_opts = find_missing( chaincom_message, chaincom_params, chaincom_opts, prompt_missing=interactive ) if not interactive and (len(missing_bitcoin_opts) > 0 or len(missing_chaincom_opts) > 0): # cannot continue raise Exception("Missing configuration fields: %s" % (",".join( missing_bitcoin_opts + missing_chaincom_opts )) ) return (bitcoind_opts, chaincom_opts)
def get_magic_bytes(): """ (required by virtualchain state engine) Get the magic byte sequence for our OP_RETURNs """ blockstore_opts = default_blockstore_opts( virtualchain.get_config_filename() ) if blockstore_opts['testset']: return MAGIC_BYTES_TESTSET else: return MAGIC_BYTES_MAINSET
def get_magic_bytes(): """ (required by virtualchain state engine) Get the magic byte sequence for our OP_RETURNs """ blockstore_opts = default_blockstore_opts( virtualchain.get_config_filename()) if blockstore_opts['testset']: return MAGIC_BYTES_TESTSET else: return MAGIC_BYTES_MAINSET
def default_blockstore_opts( config_file=None, testset=False ): """ Get our default blockstore opts from a config file or from sane defaults. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read( config_file ) blockstore_opts = {} tx_broadcaster = None utxo_provider = None testset_first_block = None max_subsidy = 0 if parser.has_section('blockstore'): if parser.has_option('blockstore', 'tx_broadcaster'): tx_broadcaster = parser.get('blockstore', 'tx_broadcaster') if parser.has_option('blockstore', 'utxo_provider'): utxo_provider = parser.get('blockstore', 'utxo_provider') if parser.has_option('blockstore', 'testset'): testset = bool(parser.get('blockstore', 'testset')) if parser.has_option('blockstore', 'testset_first_block'): testset_first_block = int( parser.get('blockstore', 'testset_first_block') ) if parser.has_option('blockstore', 'max_subsidy'): max_subsidy = int( parser.get('blockstore', 'max_subsidy')) blockstore_opts = { 'tx_broadcaster': tx_broadcaster, 'utxo_provider': utxo_provider, 'testset': testset, 'testset_first_block': testset_first_block, 'max_subsidy': max_subsidy } # strip Nones for (k, v) in blockstore_opts.items(): if v is None: del blockstore_opts[k] return blockstore_opts
def setup( return_parser=False ): """ Do one-time initialization. Call this to set up global state and set signal handlers. If return_parser is True, return a partially- setup argument parser to be populated with subparsers (i.e. as part of main()) Otherwise return None. """ global blockstore_opts global blockchain_client global blockchain_broadcaster global bitcoin_opts global utxo_opts global blockstore_opts global dht_opts # set up our implementation virtualchain.setup_virtualchain( blockstore_state_engine ) # acquire configuration, and store it globally blockstore_opts, bitcoin_opts, utxo_opts, dht_opts = configure( interactive=True ) # merge in command-line bitcoind options config_file = virtualchain.get_config_filename() arg_bitcoin_opts = None argparser = None if return_parser: arg_bitcoin_opts, argparser = virtualchain.parse_bitcoind_args( return_parser=return_parser ) else: arg_bitcoin_opts = virtualchain.parse_bitcoind_args( return_parser=return_parser ) # command-line overrides config file for (k, v) in arg_bitcoin_opts.items(): bitcoin_opts[k] = v # store options set_bitcoin_opts( bitcoin_opts ) set_utxo_opts( utxo_opts ) if return_parser: return argparser else: return None
def default_blockstore_opts(config_file=None, testset=False): """ Get our default blockstore opts from a config file or from sane defaults. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read(config_file) blockstore_opts = {} tx_broadcaster = None utxo_provider = None testset_first_block = None max_subsidy = 0 if parser.has_section('blockstore'): if parser.has_option('blockstore', 'tx_broadcaster'): tx_broadcaster = parser.get('blockstore', 'tx_broadcaster') if parser.has_option('blockstore', 'utxo_provider'): utxo_provider = parser.get('blockstore', 'utxo_provider') if parser.has_option('blockstore', 'testset'): testset = bool(parser.get('blockstore', 'testset')) if parser.has_option('blockstore', 'testset_first_block'): testset_first_block = int( parser.get('blockstore', 'testset_first_block')) if parser.has_option('blockstore', 'max_subsidy'): max_subsidy = int(parser.get('blockstore', 'max_subsidy')) blockstore_opts = { 'tx_broadcaster': tx_broadcaster, 'utxo_provider': utxo_provider, 'testset': testset, 'testset_first_block': testset_first_block, 'max_subsidy': max_subsidy } # strip Nones for (k, v) in blockstore_opts.items(): if v is None: del blockstore_opts[k] return blockstore_opts
def setup( return_parser=False ): """ Do one-time initialization. Call this to set up global state and set signal handlers. If return_parser is True, return a partially- setup argument parser to be populated with subparsers (i.e. as part of main()) Otherwise return None. """ global blockchain_client global bitcoin_opts global chaincom_opts # set up our implementation virtualchain.setup_virtualchain( blockstore_state_engine ) # acquire configuration, and store it globally bitcoin_opts, chaincom_opts = configure( interactive=True ) # merge in command-line bitcoind options config_file = virtualchain.get_config_filename() arg_bitcoin_opts = None argparser = None if return_parser: arg_bitcoin_opts, argparser = virtualchain.parse_bitcoind_args( return_parser=return_parser ) else: arg_bitcoin_opts = virtualchain.parse_bitcoind_args( return_parser=return_parser ) # command-line overrides config file for (k, v) in arg_bitcoin_opts.items(): bitcoin_opts[k] = v # store options set_bitcoin_opts( bitcoin_opts ) set_chaincom_opts( chaincom_opts ) if return_parser: return argparser else: return None
def default_utxo_provider( config_file=None ): """ Get our defualt UTXO provider options from a config file. """ global SUPPORTED_UTXO_PROVIDERS if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read( config_file ) for provider_name in SUPPORTED_UTXO_PROVIDERS: if parser.has_section( provider_name ): return provider_name return None
def default_utxo_provider(config_file=None): """ Get our defualt UTXO provider options from a config file. """ global SUPPORTED_UTXO_PROVIDERS if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read(config_file) for provider_name in SUPPORTED_UTXO_PROVIDERS: if parser.has_section(provider_name): return provider_name return None
def get_first_block_id(): """ (required by virtualchain state engine) Get the id of the first block to start indexing. """ blockstore_opts = default_blockstore_opts( virtualchain.get_config_filename() ) start_block = None if TESTNET: if blockstore_opts['testset']: start_block = FIRST_BLOCK_TESTNET_TESTSET else: start_block = FIRST_BLOCK_TESTNET else: if blockstore_opts['testset']: start_block = FIRST_BLOCK_MAINNET_TESTSET else: start_block = FIRST_BLOCK_MAINNET return start_block
def get_first_block_id(): """ (required by virtualchain state engine) Get the id of the first block to start indexing. """ blockstore_opts = default_blockstore_opts( virtualchain.get_config_filename()) start_block = None if TESTNET: if blockstore_opts['testset']: start_block = FIRST_BLOCK_TESTNET_TESTSET else: start_block = FIRST_BLOCK_TESTNET else: if blockstore_opts['testset']: start_block = FIRST_BLOCK_MAINNET_TESTSET else: start_block = FIRST_BLOCK_MAINNET return start_block
def write_config_file(bitcoind_opts=None, chaincom_opts=None, config_file=None): """ Update a configuration file, given the bitcoind options and chain.com options. Return True on success Return False on failure """ if config_file is None: try: config_file = virtualchain.get_config_filename() except: return False if config_file is None: return False parser = SafeConfigParser() parser.read(config_file) if bitcoind_opts is not None: bitcoind_opts = opt_strip("bitcoind_", bitcoind_opts) for opt_name, opt_value in bitcoind_opts.items(): parser.set('bitcoind', opt_name, opt_value) if chaincom_opts is not None: for opt_name, opt_value in chaincom_opts.items(): parser.set('chain_com', opt_name, opt_value) with open(config_file, "w") as fout: parser.write(fout) return True
def write_config_file( bitcoind_opts=None, chaincom_opts=None, config_file=None ): """ Update a configuration file, given the bitcoind options and chain.com options. Return True on success Return False on failure """ if config_file is None: try: config_file = virtualchain.get_config_filename() except: return False if config_file is None: return False parser = SafeConfigParser() parser.read(config_file) if bitcoind_opts is not None: bitcoind_opts = opt_strip( "bitcoind_", bitcoind_opts ) for opt_name, opt_value in bitcoind_opts.items(): parser.set( 'bitcoind', opt_name, opt_value ) if chaincom_opts is not None: for opt_name, opt_value in chaincom_opts.items(): parser.set( 'chain_com', opt_name, opt_value ) with open(config_file, "w") as fout: parser.write( fout ) return True
def default_blockstore_opts( config_file=None ): """ Get our default blockstore opts from a config file or from sane defaults. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read( config_file ) blockstore_opts = {} tx_broadcaster = None utxo_provider = None if parser.has_section('blockstore'): if parser.has_option('blockstore', 'tx_broadcaster'): tx_broadcaster = parser.get('blockstore', 'tx_broadcaster') if parser.has_option('blockstore', 'utxo_provider'): utxo_provider = parser.get('blockstore', 'utxo_provider') blockstore_opts = { 'tx_broadcaster': tx_broadcaster, 'utxo_provider': utxo_provider } # strip Nones for (k, v) in blockstore_opts.items(): if v is None: del blockstore_opts[k] return blockstore_opts
def default_chaincom_opts( config_file=None ): """ Get our default chain.com options from a config file. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read( config_file ) chaincom_opts = {} api_key_id = None api_key_secret = None if parser.has_section('chain_com'): if parser.has_option('chain_com', 'api_key_id'): api_key_id = parser.get('chain_com', 'api_key_id') if parser.has_option('chain_com', 'api_key_secret'): api_key_secret = parser.get('chain_com', 'api_key_secret') chaincom_opts = { 'api_key_id': api_key_id, 'api_key_secret': api_key_secret } # strip Nones for (k, v) in chaincom_opts.items(): if v is None: del chaincom_opts[k] return chaincom_opts
def default_blockstack_opts( config_file=None, virtualchain_impl=None ): """ Get our default blockstack opts from a config file or from sane defaults. """ if config_file is None: config_file = virtualchain.get_config_filename() announce_path = get_announce_filename( virtualchain.get_working_dir(impl=virtualchain_impl) ) parser = SafeConfigParser() parser.read( config_file ) blockstack_opts = {} contact_email = None announcers = "judecn.id,muneeb.id,shea256.id" announcements = None backup_frequency = 144 # once a day; 10 minute block time backup_max_age = 1008 # one week rpc_port = RPC_SERVER_PORT serve_zonefiles = True serve_profiles = False serve_data = False zonefile_dir = os.path.join( os.path.dirname(config_file), "zonefiles") analytics_key = None zonefile_storage_drivers = "disk,dht" zonefile_storage_drivers_write = "disk" profile_storage_drivers = "disk" profile_storage_drivers_write = "disk" data_storage_drivers = "disk" data_storage_drivers_write = "disk" redirect_data = False data_servers = None server_version = None atlas_enabled = True atlas_seed_peers = "node.blockstack.org:%s" % RPC_SERVER_PORT atlasdb_path = os.path.join( os.path.dirname(config_file), "atlas.db" ) atlas_blacklist = "" atlas_hostname = socket.gethostname() if parser.has_section('blockstack'): if parser.has_option('blockstack', 'backup_frequency'): backup_frequency = int( parser.get('blockstack', 'backup_frequency')) if parser.has_option('blockstack', 'backup_max_age'): backup_max_age = int( parser.get('blockstack', 'backup_max_age') ) if parser.has_option('blockstack', 'email'): contact_email = parser.get('blockstack', 'email') if parser.has_option('blockstack', 'rpc_port'): rpc_port = int(parser.get('blockstack', 'rpc_port')) if parser.has_option('blockstack', 'serve_zonefiles'): serve_zonefiles = parser.get('blockstack', 'serve_zonefiles') if serve_zonefiles.lower() in ['1', 'yes', 'true', 'on']: serve_zonefiles = True else: serve_zonefiles = False if parser.has_option('blockstack', 'serve_profiles'): serve_profiles = parser.get('blockstack', 'serve_profiles') if serve_profiles.lower() in ['1', 'yes', 'true', 'on']: serve_profiles = True else: serve_profiles = False if parser.has_option('blockstack', 'serve_data'): serve_data = parser.get('blockstack', 'serve_data') if serve_data.lower() in ['1', 'yes', 'true', 'on']: serve_data = True else: serve_data = False if parser.has_option("blockstack", "zonefile_storage_drivers"): zonefile_storage_drivers = parser.get("blockstack", "zonefile_storage_drivers") if parser.has_option("blockstack", "zonefile_storage_drivers_write"): zonefile_storage_drivers_write = parser.get("blockstack", "zonefile_storage_drivers_write") if parser.has_option("blockstack", "profile_storage_drivers"): profile_storage_drivers = parser.get("blockstack", "profile_storage_drivers") if parser.has_option("blockstack", "profile_storage_drivers_write"): profile_storage_drivers_write = parser.get("blockstack", "profile_storage_drivers_write") if parser.has_option("blockstack", "data_storage_drivers"): data_storage_drivers = parser.get("blockstack", "data_storage_drivers") if parser.has_option("blockstack", "data_storage_drivers_write"): data_storage_drivers_write = parser.get("blockstack", "data_storage_drivers_write") if parser.has_option("blockstack", "zonefiles"): zonefile_dir = parser.get("blockstack", "zonefiles") if parser.has_option('blockstack', 'redirect_data'): redirect_data = parser.get('blockstack', 'redirect_data') if redirect_data.lower() in ['1', 'yes', 'true', 'on']: redirect_data = True else: redirect_data = False if parser.has_option('blockstack', 'data_servers'): data_servers = parser.get('blockstack', 'data_servers') # must be a CSV of host:port hostports = filter( lambda x: len(x) > 0, data_servers.split(",") ) for hp in hostports: host, port = url_to_host_port( hp ) assert host is not None and port is not None if parser.has_option('blockstack', 'announcers'): # must be a CSV of blockchain IDs announcer_list_str = parser.get('blockstack', 'announcers') announcer_list = filter( lambda x: len(x) > 0, announcer_list_str.split(",") ) import scripts # validate each one valid = True for bid in announcer_list: if not scripts.is_name_valid( bid ): log.error("Invalid blockchain ID '%s'" % bid) valid = False if valid: announcers = ",".join(announcer_list) if parser.has_option('blockstack', 'analytics_key'): analytics_key = parser.get('blockstack', 'analytics_key') if parser.has_option('blockstack', 'server_version'): server_version = parser.get('blockstack', 'server_version') if parser.has_option('blockstack', 'atlas'): atlas_enabled = parser.get('blockstack', 'atlas') if atlas_enabled.lower() in ['true', '1', 'enabled', 'enabled', 'on']: atlas_enabled = True else: atlas_enabled = False if parser.has_option('blockstack', 'atlas_seeds'): atlas_seed_peers = parser.get('blockstack', 'atlas_seeds') # must be a CSV of host:port hostports = filter( lambda x: len(x) > 0, atlas_seed_peers.split(",") ) for hp in hostports: host, port = url_to_host_port( hp ) assert host is not None and port is not None if parser.has_option('blockstack', 'atlasdb_path'): atlasdb_path = parser.get('blockstack', 'atlasdb_path') if parser.has_option('blockstack', 'atlas_blacklist'): atlas_blacklist = parser.get('blockstack', 'atlas_blacklist') # must be a CSV of host:port hostports = filter( lambda x: len(x) > 0, atlas_blacklist.split(",") ) for hp in hostports: host, port = url_to_host_port( hp ) assert host is not None and port is not None if parser.has_option('blockstack', 'atlas_hostname'): atlas_hostname = parser.get('blockstack', 'atlas_hostname') if os.path.exists( announce_path ): # load announcement list with open( announce_path, "r" ) as f: announce_text = f.readlines() all_announcements = [ a.strip() for a in announce_text ] unseen_announcements = [] # find announcements we haven't seen yet for a in all_announcements: if a not in ANNOUNCEMENTS: unseen_announcements.append( a ) announcements = ",".join( unseen_announcements ) if zonefile_dir is not None and not os.path.exists( zonefile_dir ): try: os.makedirs( zonefile_dir, 0700 ) except: pass blockstack_opts = { 'rpc_port': rpc_port, 'email': contact_email, 'announcers': announcers, 'announcements': announcements, 'backup_frequency': backup_frequency, 'backup_max_age': backup_max_age, 'serve_zonefiles': serve_zonefiles, 'zonefile_storage_drivers': zonefile_storage_drivers, "zonefile_storage_drivers_write": zonefile_storage_drivers_write, 'serve_profiles': serve_profiles, 'profile_storage_drivers': profile_storage_drivers, "profile_storage_drivers_write": profile_storage_drivers_write, 'serve_data': serve_data, 'data_storage_drivers': data_storage_drivers, "data_storage_drivers_write": data_storage_drivers_write, 'redirect_data': redirect_data, 'data_servers': data_servers, 'analytics_key': analytics_key, 'server_version': server_version, 'atlas': atlas_enabled, 'atlas_seeds': atlas_seed_peers, 'atlasdb_path': atlasdb_path, 'atlas_blacklist': atlas_blacklist, 'atlas_hostname': atlas_hostname, 'zonefiles': zonefile_dir, } # strip Nones for (k, v) in blockstack_opts.items(): if v is None: del blockstack_opts[k] return blockstack_opts
def configure(config_file=None, force=False, interactive=True): """ Configure blockstore: find and store configuration parameters to the config file. Optionally prompt for missing data interactively (with interactive=True). Or, raise an exception if there are any fields missing. Optionally force a re-prompting for all configuration details (with force=True) Return (bitcoind_opts, chaincom_opts) """ if config_file is None: try: config_file = virtualchain.get_config_filename() except: pass bitcoind_message = "Blockstore does not have enough information to connect\n" bitcoind_message += "to bitcoind. Please supply the following parameters:" bitcoind_opts = {} bitcoind_params = ["server", "port", "user", "passwd", "use_https"] chaincom_message = 'NOTE: Blockstore currently requires API access to chain.com\n' chaincom_message += 'for getting unspent outputs. We will add support for using\n' chaincom_message += 'bitcoind and/or other API providers in the next release.\n' chaincom_message += "\n" chaincom_message += "If you have not done so already, please go to https://chain.com\n" chaincom_message += "and register for an API key and secret. Once you have them," chaincom_message += "please enter them here." chaincom_opts = {} chaincom_params = ["api_key_id", "api_key_secret"] if not force: # get current set of bitcoind opts tmp_bitcoind_opts = default_bitcoind_opts(config_file=config_file) bitcoind_opts = opt_strip("bitcoind_", tmp_bitcoind_opts) # get current set of chaincom opts chaincom_opts = default_chaincom_opts(config_file=config_file) # get any missing fields bitcoind_opts, missing_bitcoin_opts = find_missing( bitcoind_message, bitcoind_params, bitcoind_opts, prompt_missing=interactive) chaincom_opts, missing_chaincom_opts = find_missing( chaincom_message, chaincom_params, chaincom_opts, prompt_missing=interactive) if not interactive and (len(missing_bitcoin_opts) > 0 or len(missing_chaincom_opts) > 0): # cannot continue raise Exception( "Missing configuration fields: %s" % (",".join(missing_bitcoin_opts + missing_chaincom_opts))) return (bitcoind_opts, chaincom_opts)
def default_dht_opts(config_file=None): """ Get our default DHT options from the config file. """ global DHT_SERVER_PORT, DEFAULT_DHT_SERVERS if config_file is None: config_file = virtualchain.get_config_filename() defaults = { 'disable': str(False), 'port': str(DHT_SERVER_PORT), 'servers': ",".join( ["%s:%s" % (host, port) for (host, port) in DEFAULT_DHT_SERVERS]) } parser = SafeConfigParser(defaults) parser.read(config_file) if parser.has_section('dht'): disable = parser.get('dht', 'disable') port = parser.get('dht', 'port') servers = parser.get( 'dht', 'servers') # expect comma-separated list of host:port if disable is None: disable = False if port is None: port = DHT_SERVER_PORT if servers is None: servers = DEFAULT_DHT_SERVERS try: disable = bool(disable) except: raise Exception( "Invalid field value for dht.disable: expected bool") try: port = int(port) except: raise Exception("Invalid field value for dht.port: expected int") parsed_servers = [] try: server_list = servers.split(",") for server in server_list: server_host, server_port = server.split(":") server_port = int(server_port) parsed_servers.append((server_host, server_port)) except: raise Exception( "Invalid field value for dht.servers: expected 'HOST:PORT[,HOST:PORT...]'" ) dht_opts = { 'disable': disable, 'port': port, 'servers': parsed_servers } return dht_opts else: # use defaults dht_opts = { 'disable': False, 'port': DHT_SERVER_PORT, 'servers': DEFAULT_DHT_SERVERS } return dht_opts
def default_blockstack_opts( config_file=None ): """ Get our default blockstack opts from a config file or from sane defaults. """ if config_file is None: config_file = virtualchain.get_config_filename() announce_path = get_announce_filename( virtualchain.get_working_dir() ) parser = SafeConfigParser() parser.read( config_file ) blockstack_opts = {} contact_email = None announcers = "judecn.id,muneeb.id,shea256.id" announcements = None backup_frequency = 1008 # once a week; 10 minute block time backup_max_age = 12096 # 12 weeks rpc_port = RPC_SERVER_PORT blockchain_proxy = False serve_zonefiles = True serve_profiles = False zonefile_dir = None analytics_key = None zonefile_storage_drivers = "disk" profile_storage_drivers = "" server_version = None if parser.has_section('blockstack'): if parser.has_option('blockstack', 'backup_frequency'): backup_frequency = int( parser.get('blockstack', 'backup_frequency')) if parser.has_option('blockstack', 'backup_max_age'): backup_max_age = int( parser.get('blockstack', 'backup_max_age') ) if parser.has_option('blockstack', 'email'): contact_email = parser.get('blockstack', 'email') if parser.has_option('blockstack', 'rpc_port'): rpc_port = int(parser.get('blockstack', 'rpc_port')) if parser.has_option('blockstack', 'blockchain_proxy'): blockchain_proxy = parser.get('blockstack', 'blockchain_proxy') if blockchain_proxy.lower() in ['1', 'yes', 'true', 'on']: blockchain_proxy = True else: blockchain_proxy = False if parser.has_option('blockstack', 'serve_zonefiles'): serve_zonefiles = parser.get('blockstack', 'serve_zonefiles') if serve_zonefiles.lower() in ['1', 'yes', 'true', 'on']: serve_zonefiles = True else: serve_zonefiles = False if parser.has_option('blockstack', 'serve_profiles'): serve_profiles = parser.get('blockstack', 'serve_profiles') if serve_profiles.lower() in ['1', 'yes', 'true', 'on']: serve_profiles = True else: serve_profiles = False if parser.has_option("blockstack", "zonefile_storage_drivers"): zonefile_storage_drivers = parser.get("blockstack", "zonefile_storage_drivers") if parser.has_option("blockstack", "profile_storage_drivers"): profile_storage_drivers = parser.get("blockstack", "profile_storage_drivers") if parser.has_option("blockstack", "zonefiles"): zonefile_dir = parser.get("blockstack", "zonefiles") if parser.has_option('blockstack', 'announcers'): # must be a CSV of blockchain IDs announcer_list_str = parser.get('blockstack', 'announcers') announcer_list = announcer_list_str.split(",") import scripts # validate each one valid = True for bid in announcer_list: if not scripts.is_name_valid( bid ): log.error("Invalid blockchain ID '%s'" % bid) valid = False if valid: announcers = ",".join(announcer_list) if parser.has_option('blockstack', 'analytics_key'): analytics_key = parser.get('blockstack', 'analytics_key') if parser.has_option('blockstack', 'server_version'): server_version = parser.get('blockstack', 'server_version') if os.path.exists( announce_path ): # load announcement list with open( announce_path, "r" ) as f: announce_text = f.readlines() all_announcements = [ a.strip() for a in announce_text ] unseen_announcements = [] # find announcements we haven't seen yet for a in all_announcements: if a not in ANNOUNCEMENTS: unseen_announcements.append( a ) announcements = ",".join( unseen_announcements ) if zonefile_dir is not None and not os.path.exists( zonefile_dir ): try: os.makedirs( zonefile_dir, 0700 ) except: pass blockstack_opts = { 'rpc_port': rpc_port, 'email': contact_email, 'announcers': announcers, 'announcements': announcements, 'backup_frequency': backup_frequency, 'backup_max_age': backup_max_age, 'blockchain_proxy': blockchain_proxy, 'serve_zonefiles': serve_zonefiles, 'serve_profiles': serve_profiles, 'zonefile_storage_drivers': zonefile_storage_drivers, 'profile_storage_drivers': profile_storage_drivers, 'zonefiles': zonefile_dir, 'analytics_key': analytics_key, 'server_version': server_version } # strip Nones for (k, v) in blockstack_opts.items(): if v is None: del blockstack_opts[k] return blockstack_opts
def write_config_file(blockstore_opts=None, bitcoind_opts=None, utxo_opts=None, dht_opts=None, config_file=None): """ Update a configuration file, given the bitcoind options and chain.com options. Return True on success Return False on failure """ if config_file is None: try: config_file = virtualchain.get_config_filename() except: return False if config_file is None: return False parser = SafeConfigParser() parser.read(config_file) if bitcoind_opts is not None and len(bitcoind_opts) > 0: tmp_bitcoind_opts = opt_strip("bitcoind_", bitcoind_opts) if parser.has_section('bitcoind'): parser.remove_section('bitcoind') parser.add_section('bitcoind') for opt_name, opt_value in tmp_bitcoind_opts.items(): if opt_value is None: raise Exception("%s is not defined" % opt_name) parser.set('bitcoind', opt_name, "%s" % opt_value) if utxo_opts is not None and len(utxo_opts) > 0: if parser.has_section(utxo_opts['utxo_provider']): parser.remove_section(utxo_opts['utxo_provider']) parser.add_section(utxo_opts['utxo_provider']) for opt_name, opt_value in utxo_opts.items(): # don't log this meta-field if opt_name == 'utxo_provider': continue if opt_value is None: raise Exception("%s is not defined" % opt_name) parser.set(utxo_opts['utxo_provider'], opt_name, "%s" % opt_value) if dht_opts is not None and len(dht_opts) > 0: if parser.has_section("dht"): parser.remove_section("dht") parser.add_section("dht") for opt_name, opt_value in dht_opts.items(): if opt_value is None: raise Exception("%s is not defined" % opt_name) parser.set("dht", opt_name, "%s" % opt_value) if blockstore_opts is not None and len(blockstore_opts) > 0: if parser.has_section("blockstore"): parser.remove_section("blockstore") parser.add_section("blockstore") for opt_name, opt_value in blockstore_opts.items(): if opt_value is None: raise Exception("%s is not defined" % opt_name) parser.set("blockstore", opt_name, "%s" % opt_value) with open(config_file, "w") as fout: os.fchmod(fout.fileno(), 0600) parser.write(fout) return True
def configure( config_file=None, force=False, interactive=True ): """ Configure blockstack: find and store configuration parameters to the config file. Optionally prompt for missing data interactively (with interactive=True). Or, raise an exception if there are any fields missing. Optionally force a re-prompting for all configuration details (with force=True) Return {'blockstack': {...}, 'bitcoind': {...}} """ if config_file is None: try: # get input for everything config_file = virtualchain.get_config_filename() except: raise if not os.path.exists( config_file ): # definitely ask for everything force = True log.debug("Load config from '%s'" % config_file) # get blockstack opts blockstack_opts = {} blockstack_opts_defaults = default_blockstack_opts( config_file=config_file ) blockstack_params = blockstack_opts_defaults.keys() if not force: # default blockstack options blockstack_opts = default_blockstack_opts( config_file=config_file ) blockstack_msg = "ADVANCED USERS ONLY.\nPlease enter blockstack configuration hints." # NOTE: disabled blockstack_opts, missing_blockstack_opts, num_blockstack_opts_prompted = blockstack_client.config.find_missing( blockstack_msg, \ blockstack_params, \ blockstack_opts, \ blockstack_opts_defaults, \ prompt_missing=False ) bitcoind_message = "Blockstack does not have enough information to connect\n" bitcoind_message += "to bitcoind. Please supply the following parameters, or\n" bitcoind_message += "press [ENTER] to select the default value." bitcoind_opts = {} bitcoind_opts_defaults = blockstack_client.config.default_bitcoind_opts( config_file=config_file ) bitcoind_params = bitcoind_opts_defaults.keys() if not force: # get default set of bitcoind opts bitcoind_opts = blockstack_client.config.default_bitcoind_opts( config_file=config_file ) # get any missing bitcoind fields bitcoind_opts, missing_bitcoin_opts, num_bitcoind_prompted = blockstack_client.config.find_missing( bitcoind_message, \ bitcoind_params, \ bitcoind_opts, \ bitcoind_opts_defaults, \ prompt_missing=interactive ) if not interactive and (len(missing_bitcoin_opts) > 0 or len(missing_blockstack_opts) > 0): # cannot continue raise Exception("Missing configuration fields: %s" % (",".join( missing_blockstack_opts + missing_bitcoin_opts )) ) # ask for contact info, so we can send out notifications for bugfixes and upgrades if blockstack_opts.get('email', None) is None: email_msg = "Would you like to receive notifications\n" email_msg+= "from the developers when there are critical\n" email_msg+= "updates available to install?\n\n" email_msg+= "If so, please enter your email address here.\n" email_msg+= "If not, leave this field blank.\n\n" email_msg+= "Your email address will be used solely\n" email_msg+= "for this purpose.\n" email_opts, _, email_prompted = blockstack_client.config.find_missing( email_msg, ['email'], {}, {'email': ''}, prompt_missing=interactive ) # merge with blockstack section num_blockstack_opts_prompted += 1 blockstack_opts['email'] = email_opts['email'] ret = { 'blockstack': blockstack_opts, 'bitcoind': bitcoind_opts } # if we prompted, then save if num_bitcoind_prompted > 0 or num_blockstack_opts_prompted > 0: print >> sys.stderr, "Saving configuration to %s" % config_file # always set version when writing config_opts = copy.deepcopy(ret) if not config_opts['blockstack'].has_key('server_version'): config_opts['blockstack']['server_version'] = VERSION # if the config file doesn't exist, then set the version # in ret as well, since it's what's written if not os.path.exists(config_file): ret['blockstack']['server_version'] = VERSION blockstack_client.config.write_config_file( config_opts, config_file ) # prefix our bitcoind options, so they work with virtualchain ret['bitcoind'] = blockstack_client.config.opt_restore("bitcoind_", ret['bitcoind']) return ret
def configure(config_file=None, force=False, interactive=True, testset=False): """ Configure blockstore: find and store configuration parameters to the config file. Optionally prompt for missing data interactively (with interactive=True). Or, raise an exception if there are any fields missing. Optionally force a re-prompting for all configuration details (with force=True) Return (bitcoind_opts, utxo_opts) """ global SUPPORTED_UTXO_PROVIDERS, SUPPORTED_UTXO_PARAMS, SUPPORTED_UTXO_PROMPT_MESSAGES if config_file is None: try: # get input for everything config_file = virtualchain.get_config_filename() except: raise if not os.path.exists(config_file): # definitely ask for everything force = True # get blockstore opts blockstore_opts = {} blockstore_opts_defaults = default_blockstore_opts(config_file=config_file, testset=testset) blockstore_params = blockstore_opts_defaults.keys() if not force: # default blockstore options blockstore_opts = default_blockstore_opts(config_file=config_file, testset=testset) blockstore_msg = "ADVANCED USERS ONLY.\nPlease enter blockstore configuration hints." # NOTE: disabled blockstore_opts, missing_blockstore_opts, num_blockstore_opts_prompted = find_missing( blockstore_msg, blockstore_params, blockstore_opts, blockstore_opts_defaults, prompt_missing=False) utxo_provider = None if 'utxo_provider' in blockstore_opts: utxo_provider = blockstore_opts['utxo_provider'] else: utxo_provider = default_utxo_provider(config_file=config_file) bitcoind_message = "Blockstore does not have enough information to connect\n" bitcoind_message += "to bitcoind. Please supply the following parameters, or\n" bitcoind_message += "press [ENTER] to select the default value." bitcoind_opts = {} bitcoind_opts_defaults = default_bitcoind_opts(config_file=config_file) bitcoind_params = bitcoind_opts_defaults.keys() if not force: # get default set of bitcoind opts bitcoind_opts = default_bitcoind_opts(config_file=config_file) # get any missing bitcoind fields bitcoind_opts, missing_bitcoin_opts, num_bitcoind_prompted = find_missing( bitcoind_message, bitcoind_params, bitcoind_opts, bitcoind_opts_defaults, prompt_missing=interactive, strip_prefix="bitcoind_") # find the current utxo provider while utxo_provider is None or utxo_provider not in SUPPORTED_UTXO_PROVIDERS: # prompt for it? if interactive or force: utxo_message = 'NOTE: Blockstore currently requires an external API\n' utxo_message += 'for querying unspent transaction outputs. The set of\n' utxo_message += 'supported providers are:\n' utxo_message += "\t\n".join(SUPPORTED_UTXO_PROVIDERS) + "\n" utxo_message += "Please get the requisite API tokens and enter them here." utxo_provider_dict = interactive_prompt(utxo_message, ['utxo_provider'], {}) utxo_provider = utxo_provider_dict['utxo_provider'] else: raise Exception("No UTXO provider given") utxo_opts = {} utxo_opts_defaults = default_utxo_provider_opts(utxo_provider, config_file=config_file) utxo_params = SUPPORTED_UTXO_PARAMS[utxo_provider] if not force: # get current set of utxo opts utxo_opts = default_utxo_provider_opts(utxo_provider, config_file=config_file) utxo_opts, missing_utxo_opts, num_utxo_opts_prompted = find_missing( SUPPORTED_UTXO_PROMPT_MESSAGES[utxo_provider], utxo_params, utxo_opts, utxo_opts_defaults, prompt_missing=interactive) utxo_opts['utxo_provider'] = utxo_provider dht_opts = {} dht_opts_defaults = default_dht_opts(config_file=config_file) dht_params = dht_opts_defaults.keys() if not force: # default DHT options dht_opts = default_dht_opts(config_file=config_file) dht_msg = "Please enter your DHT node configuration.\nUnless you plan on leaving Blockstore\nrunning, you should disable the DHT feature." # NOTE: disabled dht_opts, missing_dht_opts, num_dht_opts_prompted = find_missing( dht_msg, dht_params, dht_opts, dht_opts_defaults, prompt_missing=False) if not interactive and (len(missing_bitcoin_opts) > 0 or len(missing_utxo_opts) > 0 or len(missing_dht_opts) > 0 or len(missing_blockstore_opts) > 0): # cannot continue raise Exception("Missing configuration fields: %s" % (",".join(missing_bitcoin_opts + missing_utxo_opts))) # ask for contact info, so we can send out notifications for bugfixes and upgrades if blockstore_opts.get('email', None) is None: email_msg = "Would you like to receive notifications\n" email_msg += "from the developers when there are critical\n" email_msg += "updates available to install?\n\n" email_msg += "If so, please enter your email address here.\n" email_msg += "If not, leave this field blank.\n\n" email_msg += "Your email address will be used solely\n" email_msg += "for this purpose.\n" email_opts, _, email_prompted = find_missing( email_msg, ['email'], {}, {'email': ''}, prompt_missing=interactive) # merge with blockstore section num_blockstore_opts_prompted += 1 blockstore_opts['email'] = email_opts['email'] # if we prompted, then save if num_bitcoind_prompted > 0 or num_utxo_opts_prompted > 0 or num_dht_opts_prompted > 0 or num_blockstore_opts_prompted > 0: print >> sys.stderr, "Saving configuration to %s" % config_file write_config_file(bitcoind_opts=bitcoind_opts, utxo_opts=utxo_opts, dht_opts=dht_opts, blockstore_opts=blockstore_opts, config_file=config_file) return (blockstore_opts, bitcoind_opts, utxo_opts, dht_opts)
def default_blockstack_opts( config_file=None, testset=False ): """ Get our default blockstack opts from a config file or from sane defaults. """ if config_file is None: config_file = virtualchain.get_config_filename() testset_path = get_testset_filename( virtualchain.get_working_dir() ) announce_path = get_announce_filename( virtualchain.get_working_dir() ) parser = SafeConfigParser() parser.read( config_file ) blockstack_opts = {} tx_broadcaster = None utxo_provider = None testset_first_block = None max_subsidy = 0 contact_email = None announcers = "judecn.id,muneeb.id,shea256.id" announcements = None if parser.has_section('blockstack'): if parser.has_option('blockstack', 'tx_broadcaster'): tx_broadcaster = parser.get('blockstack', 'tx_broadcaster') if parser.has_option('blockstack', 'utxo_provider'): utxo_provider = parser.get('blockstack', 'utxo_provider') if parser.has_option('blockstack', 'testset_first_block'): testset_first_block = int( parser.get('blockstack', 'testset_first_block') ) if parser.has_option('blockstack', 'max_subsidy'): max_subsidy = int( parser.get('blockstack', 'max_subsidy')) if parser.has_option('blockstack', 'email'): contact_email = parser.get('blockstack', 'email') if parser.has_option('blockstack', 'announcers'): # must be a CSV of blockchain IDs announcer_list_str = parser.get('blockstack', 'announcers') announcer_list = announcer_list_str.split(",") import scripts # validate each one valid = True for bid in announcer_list: if not scripts.is_name_valid( bid ): log.error("Invalid blockchain ID '%s'" % bid) valid = False if valid: announcers = ",".join(announcer_list) if os.path.exists( testset_path ): # testset file flag set testset = True if os.path.exists( announce_path ): # load announcement list with open( announce_path, "r" ) as f: announce_text = f.readlines() all_announcements = [ a.strip() for a in announce_text ] unseen_announcements = [] # find announcements we haven't seen yet for a in all_announcements: if a not in ANNOUNCEMENTS: unseen_announcements.append( a ) announcements = ",".join( unseen_announcements ) blockstack_opts = { 'tx_broadcaster': tx_broadcaster, 'utxo_provider': utxo_provider, 'testset': testset, 'testset_first_block': testset_first_block, 'max_subsidy': max_subsidy, 'email': contact_email, 'announcers': announcers, 'announcements': announcements } # strip Nones for (k, v) in blockstack_opts.items(): if v is None: del blockstack_opts[k] return blockstack_opts
def default_bitcoind_utxo_opts( config_file=None ): """ Get our default bitcoind UTXO options from a config file. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read( config_file ) bitcoind_utxo_opts = {} server = None port = None rpc_username = None rpc_password = None use_https = None version_byte = None if parser.has_section("bitcoind_utxo"): if parser.has_option("bitcoind_utxo", "server"): server = parser.get("bitcoind_utxo", "server") if parser.has_option("bitcoind_utxo", "port"): port = int( parser.get("bitcoind_utxo", "port") ) if parser.has_option("bitcoind_utxo", "rpc_username"): rpc_username = parser.get("bitcoind_utxo", "rpc_username") if parser.has_option("bitcoind_utxo", "rpc_password"): rpc_password = parser.get("bitcoind_utxo", "rpc_password") if parser.has_option("bitcoind_utxo", "use_https"): use_https = bool( parser.get("bitcoind_utxo", "use_https") ) if parser.has_option("bitcoind_utxo", "version_byte"): version_byte = int(parser.get("bitcoind_utxo", "version_byte")) if use_https is None: use_https = True if version_byte is None: version_byte = 0 if server is None: server = '127.0.0.1' if port is None: port = 8332 bitcoind_utxo_opts = { "utxo_provider": "bitcoind_utxo", "rpc_username": rpc_username, "rpc_password": rpc_password, "server": server, "port": port, "use_https": use_https, "version_byte": version_byte } # strip Nones for (k, v) in bitcoind_utxo_opts.items(): if v is None: del bitcoind_utxo_opts[k] return bitcoind_utxo_opts
def default_blockstore_opts(config_file=None, testset=False): """ Get our default blockstore opts from a config file or from sane defaults. """ if config_file is None: config_file = virtualchain.get_config_filename() testset_path = get_testset_filename(virtualchain.get_working_dir()) announce_path = get_announce_filename(virtualchain.get_working_dir()) parser = SafeConfigParser() parser.read(config_file) blockstore_opts = {} tx_broadcaster = None utxo_provider = None testset_first_block = None max_subsidy = 0 contact_email = None announcers = "judecn.id,muneeb.id,shea256.id" announcements = None if parser.has_section('blockstore'): if parser.has_option('blockstore', 'tx_broadcaster'): tx_broadcaster = parser.get('blockstore', 'tx_broadcaster') if parser.has_option('blockstore', 'utxo_provider'): utxo_provider = parser.get('blockstore', 'utxo_provider') if parser.has_option('blockstore', 'testset_first_block'): testset_first_block = int( parser.get('blockstore', 'testset_first_block')) if parser.has_option('blockstore', 'max_subsidy'): max_subsidy = int(parser.get('blockstore', 'max_subsidy')) if parser.has_option('blockstore', 'email'): contact_email = parser.get('blockstore', 'email') if parser.has_option('blockstore', 'announcers'): # must be a CSV of blockchain IDs announcer_list_str = parser.get('blockstore', 'announcers') announcer_list = announcer_list_str.split(",") import scripts # validate each one valid = True for bid in announcer_list: if not scripts.is_name_valid(bid): log.error("Invalid blockchain ID '%s'" % bid) valid = False if valid: announcers = ",".join(announcer_list) if os.path.exists(testset_path): # testset file flag set testset = True if os.path.exists(announce_path): # load announcement list with open(announce_path, "r") as f: announce_text = f.readlines() all_announcements = [a.strip() for a in announce_text] unseen_announcements = [] # find announcements we haven't seen yet for a in all_announcements: if a not in ANNOUNCEMENTS: unseen_announcements.append(a) announcements = ",".join(unseen_announcements) blockstore_opts = { 'tx_broadcaster': tx_broadcaster, 'utxo_provider': utxo_provider, 'testset': testset, 'testset_first_block': testset_first_block, 'max_subsidy': max_subsidy, 'email': contact_email, 'announcers': announcers, 'announcements': announcements } # strip Nones for (k, v) in blockstore_opts.items(): if v is None: del blockstore_opts[k] return blockstore_opts
def configure( config_file=None, force=False, interactive=True ): """ Configure blockstore: find and store configuration parameters to the config file. Optionally prompt for missing data interactively (with interactive=True). Or, raise an exception if there are any fields missing. Optionally force a re-prompting for all configuration details (with force=True) Return (bitcoind_opts, utxo_opts) """ global SUPPORTED_UTXO_PROVIDERS, SUPPORTED_UTXO_PARAMS, SUPPORTED_UTXO_PROMPT_MESSAGES if config_file is None: try: # get input for everything config_file = virtualchain.get_config_filename() except: pass if not os.path.exists( config_file ): # definitely ask for everything force = True # get blockstore opts blockstore_opts = {} blockstore_opts_defaults = default_blockstore_opts( config_file=config_file ) blockstore_params = blockstore_opts_defaults.keys() if not force: # default blockstore options blockstore_opts = default_blockstore_opts( config_file=config_file ) blockstore_msg = "ADVANCED USERS ONLY.\nPlease enter blockstore configuration hints." blockstore_opts, missing_blockstore_opts, num_blockstore_opts_prompted = find_missing( blockstore_msg, blockstore_params, blockstore_opts, blockstore_opts_defaults, prompt_missing=False ) utxo_provider = None if 'utxo_provider' in blockstore_opts: utxo_provider = blockstore_opts['utxo_provider'] else: utxo_provider = default_utxo_provider( config_file=config_file ) bitcoind_message = "Blockstore does not have enough information to connect\n" bitcoind_message += "to bitcoind. Please supply the following parameters, or" bitcoind_message += "press [ENTER] to select the default value." bitcoind_opts = {} bitcoind_opts_defaults = default_bitcoind_opts( config_file=config_file ) bitcoind_params = bitcoind_opts_defaults.keys() if not force: # get default set of bitcoind opts bitcoind_opts = default_bitcoind_opts( config_file=config_file ) # get any missing bitcoind fields bitcoind_opts, missing_bitcoin_opts, num_bitcoind_prompted = find_missing( bitcoind_message, bitcoind_params, bitcoind_opts, bitcoind_opts_defaults, prompt_missing=interactive, strip_prefix="bitcoind_" ) # find the current utxo provider while utxo_provider is None or utxo_provider not in SUPPORTED_UTXO_PROVIDERS: # prompt for it? if interactive or force: utxo_message = 'NOTE: Blockstore currently requires an external API\n' utxo_message += 'for querying unspent transaction outputs. The set of\n' utxo_message += 'supported providers are:\n' utxo_message += "\t\n".join( SUPPORTED_UTXO_PROVIDERS ) + "\n" utxo_message += "Please get the requisite API tokens and enter them here." utxo_provider_dict = interactive_prompt( utxo_message, ['utxo_provider'], {} ) utxo_provider = utxo_provider_dict['utxo_provider'] else: raise Exception("No UTXO provider given") utxo_opts = {} utxo_opts_defaults = default_utxo_provider_opts( utxo_provider, config_file=config_file ) utxo_params = SUPPORTED_UTXO_PARAMS[ utxo_provider ] if not force: # get current set of utxo opts utxo_opts = default_utxo_provider_opts( utxo_provider, config_file=config_file ) utxo_opts, missing_utxo_opts, num_utxo_opts_prompted = find_missing( SUPPORTED_UTXO_PROMPT_MESSAGES[utxo_provider], utxo_params, utxo_opts, utxo_opts_defaults, prompt_missing=interactive ) utxo_opts['utxo_provider'] = utxo_provider dht_opts = {} dht_opts_defaults = default_dht_opts( config_file=config_file ) dht_params = dht_opts_defaults.keys() if not force: # default DHT options dht_opts = default_dht_opts( config_file=config_file ) dht_msg = "Please enter your DHT node configuration.\nUnless you plan on leaving Blockstore\nrunning, you should disable the DHT feature." dht_opts, missing_dht_opts, num_dht_opts_prompted = find_missing( dht_msg, dht_params, dht_opts, dht_opts_defaults, prompt_missing=False ) if not interactive and (len(missing_bitcoin_opts) > 0 or len(missing_utxo_opts) > 0 or len(missing_dht_opts) > 0 or len(missing_blockstore_opts) > 0): # cannot continue raise Exception("Missing configuration fields: %s" % (",".join( missing_bitcoin_opts + missing_utxo_opts )) ) # if we prompted, then save if num_bitcoind_prompted > 0 or num_utxo_opts_prompted > 0 or num_dht_opts_prompted > 0 or num_blockstore_opts_prompted > 0: print >> sys.stderr, "Saving configuration to %s" % config_file write_config_file( bitcoind_opts=bitcoind_opts, utxo_opts=utxo_opts, dht_opts=dht_opts, blockstore_opts=blockstore_opts, config_file=config_file ) return (blockstore_opts, bitcoind_opts, utxo_opts, dht_opts)
def configure( config_file=None, force=False, interactive=True, testset=False ): """ Configure blockstack: find and store configuration parameters to the config file. Optionally prompt for missing data interactively (with interactive=True). Or, raise an exception if there are any fields missing. Optionally force a re-prompting for all configuration details (with force=True) Return (bitcoind_opts, utxo_opts) """ global SUPPORTED_UTXO_PROVIDERS, SUPPORTED_UTXO_PARAMS, SUPPORTED_UTXO_PROMPT_MESSAGES if config_file is None: try: # get input for everything config_file = virtualchain.get_config_filename() except: raise if not os.path.exists( config_file ): # definitely ask for everything force = True # get blockstack opts blockstack_opts = {} blockstack_opts_defaults = default_blockstack_opts( config_file=config_file, testset=testset ) blockstack_params = blockstack_opts_defaults.keys() if not force: # default blockstack options blockstack_opts = default_blockstack_opts( config_file=config_file, testset=testset ) blockstack_msg = "ADVANCED USERS ONLY.\nPlease enter blockstack configuration hints." # NOTE: disabled blockstack_opts, missing_blockstack_opts, num_blockstack_opts_prompted = find_missing( blockstack_msg, blockstack_params, blockstack_opts, blockstack_opts_defaults, prompt_missing=False ) utxo_provider = None if 'utxo_provider' in blockstack_opts: utxo_provider = blockstack_opts['utxo_provider'] else: utxo_provider = default_utxo_provider( config_file=config_file ) bitcoind_message = "Blockstack does not have enough information to connect\n" bitcoind_message += "to bitcoind. Please supply the following parameters, or\n" bitcoind_message += "press [ENTER] to select the default value." bitcoind_opts = {} bitcoind_opts_defaults = default_bitcoind_opts( config_file=config_file ) bitcoind_params = bitcoind_opts_defaults.keys() if not force: # get default set of bitcoind opts bitcoind_opts = default_bitcoind_opts( config_file=config_file ) # get any missing bitcoind fields bitcoind_opts, missing_bitcoin_opts, num_bitcoind_prompted = find_missing( bitcoind_message, bitcoind_params, bitcoind_opts, bitcoind_opts_defaults, prompt_missing=interactive, strip_prefix="bitcoind_" ) # find the current utxo provider while utxo_provider is None or utxo_provider not in SUPPORTED_UTXO_PROVIDERS: # prompt for it? if interactive or force: utxo_message = 'NOTE: Blockstack currently requires an external API\n' utxo_message += 'for querying unspent transaction outputs. The set of\n' utxo_message += 'supported providers are:\n' utxo_message += "\t\n".join( SUPPORTED_UTXO_PROVIDERS ) + "\n" utxo_message += "Please get the requisite API tokens and enter them here." utxo_provider_dict = interactive_prompt( utxo_message, ['utxo_provider'], {} ) utxo_provider = utxo_provider_dict['utxo_provider'] else: raise Exception("No UTXO provider given") utxo_opts = {} utxo_opts_defaults = default_utxo_provider_opts( utxo_provider, config_file=config_file ) utxo_params = SUPPORTED_UTXO_PARAMS[ utxo_provider ] if not force: # get current set of utxo opts utxo_opts = default_utxo_provider_opts( utxo_provider, config_file=config_file ) utxo_opts, missing_utxo_opts, num_utxo_opts_prompted = find_missing( SUPPORTED_UTXO_PROMPT_MESSAGES[utxo_provider], utxo_params, utxo_opts, utxo_opts_defaults, prompt_missing=interactive ) utxo_opts['utxo_provider'] = utxo_provider dht_opts = {} dht_opts_defaults = default_dht_opts( config_file=config_file ) dht_params = dht_opts_defaults.keys() if not force: # default DHT options dht_opts = default_dht_opts( config_file=config_file ) dht_msg = "Please enter your DHT node configuration.\nUnless you plan on leaving Blockstack\nrunning, you should disable the DHT feature." # NOTE: disabled dht_opts, missing_dht_opts, num_dht_opts_prompted = find_missing( dht_msg, dht_params, dht_opts, dht_opts_defaults, prompt_missing=False ) if not interactive and (len(missing_bitcoin_opts) > 0 or len(missing_utxo_opts) > 0 or len(missing_dht_opts) > 0 or len(missing_blockstack_opts) > 0): # cannot continue raise Exception("Missing configuration fields: %s" % (",".join( missing_bitcoin_opts + missing_utxo_opts )) ) # ask for contact info, so we can send out notifications for bugfixes and upgrades if blockstack_opts.get('email', None) is None: email_msg = "Would you like to receive notifications\n" email_msg+= "from the developers when there are critical\n" email_msg+= "updates available to install?\n\n" email_msg+= "If so, please enter your email address here.\n" email_msg+= "If not, leave this field blank.\n\n" email_msg+= "Your email address will be used solely\n" email_msg+= "for this purpose.\n" email_opts, _, email_prompted = find_missing( email_msg, ['email'], {}, {'email': ''}, prompt_missing=interactive ) # merge with blockstack section num_blockstack_opts_prompted += 1 blockstack_opts['email'] = email_opts['email'] # if we prompted, then save if num_bitcoind_prompted > 0 or num_utxo_opts_prompted > 0 or num_dht_opts_prompted > 0 or num_blockstack_opts_prompted > 0: print >> sys.stderr, "Saving configuration to %s" % config_file write_config_file( bitcoind_opts=bitcoind_opts, utxo_opts=utxo_opts, dht_opts=dht_opts, blockstack_opts=blockstack_opts, config_file=config_file ) return (blockstack_opts, bitcoind_opts, utxo_opts, dht_opts)
def default_dht_opts( config_file=None ): """ Get our default DHT options from the config file. """ global DHT_SERVER_PORT, DEFAULT_DHT_SERVERS if config_file is None: config_file = virtualchain.get_config_filename() defaults = { 'disable': str(True), 'port': str(DHT_SERVER_PORT), 'servers': ",".join( ["%s:%s" % (host, port) for (host, port) in DEFAULT_DHT_SERVERS] ) } parser = SafeConfigParser( defaults ) parser.read( config_file ) if parser.has_section('dht'): disable = parser.get('dht', 'disable') port = parser.get('dht', 'port') servers = parser.get('dht', 'servers') # expect comma-separated list of host:port if disable is None: disable = True if port is None: port = DHT_SERVER_PORT if servers is None: servers = DEFAULT_DHT_SERVERS try: disable = bool(disable) except: raise Exception("Invalid field value for dht.disable: expected bool") try: port = int(port) except: raise Exception("Invalid field value for dht.port: expected int") parsed_servers = [] try: server_list = servers.split(",") for server in server_list: server_host, server_port = server.split(":") server_port = int(server_port) parsed_servers.append( (server_host, server_port) ) except: raise Exception("Invalid field value for dht.servers: expected 'HOST:PORT[,HOST:PORT...]'") dht_opts = { 'disable': disable, 'port': port, 'servers': ",".join( ["%s:%s" % (host, port) for (host, port) in DEFAULT_DHT_SERVERS] ) } return dht_opts else: # use defaults dht_opts = { 'disable': True, 'port': DHT_SERVER_PORT, 'servers': ",".join( ["%s:%s" % (host, port) for (host, port) in DEFAULT_DHT_SERVERS] ) } return dht_opts
def default_bitcoind_utxo_opts(config_file=None): """ Get our default bitcoind UTXO options from a config file. """ if config_file is None: config_file = virtualchain.get_config_filename() parser = SafeConfigParser() parser.read(config_file) bitcoind_utxo_opts = {} server = None port = None rpc_username = None rpc_password = None use_https = None version_byte = None if parser.has_section("bitcoind_utxo"): if parser.has_option("bitcoind_utxo", "server"): server = parser.get("bitcoind_utxo", "server") if parser.has_option("bitcoind_utxo", "port"): port = int(parser.get("bitcoind_utxo", "port")) if parser.has_option("bitcoind_utxo", "rpc_username"): rpc_username = parser.get("bitcoind_utxo", "rpc_username") if parser.has_option("bitcoind_utxo", "rpc_password"): rpc_password = parser.get("bitcoind_utxo", "rpc_password") if parser.has_option("bitcoind_utxo", "use_https"): if parser.get("bitcoind_utxo", "use_https").lower() in ["y", "yes", "true"]: use_https = True else: use_https = False if parser.has_option("bitcoind_utxo", "version_byte"): version_byte = int(parser.get("bitcoind_utxo", "version_byte")) if use_https is None: use_https = True if version_byte is None: version_byte = 0 if server is None: server = '127.0.0.1' if port is None: port = 8332 bitcoind_utxo_opts = { "utxo_provider": "bitcoind_utxo", "rpc_username": rpc_username, "rpc_password": rpc_password, "server": server, "port": port, "use_https": use_https, "version_byte": version_byte } # strip Nones for (k, v) in bitcoind_utxo_opts.items(): if v is None: del bitcoind_utxo_opts[k] return bitcoind_utxo_opts
def write_config_file( blockstore_opts=None, bitcoind_opts=None, utxo_opts=None, dht_opts=None, config_file=None ): """ Update a configuration file, given the bitcoind options and chain.com options. Return True on success Return False on failure """ print dht_opts if config_file is None: try: config_file = virtualchain.get_config_filename() except: return False if config_file is None: return False parser = SafeConfigParser() parser.read(config_file) if bitcoind_opts is not None and len(bitcoind_opts) > 0: tmp_bitcoind_opts = opt_strip( "bitcoind_", bitcoind_opts ) if parser.has_section('bitcoind'): parser.remove_section('bitcoind') parser.add_section( 'bitcoind' ) for opt_name, opt_value in tmp_bitcoind_opts.items(): if opt_value is None: raise Exception("%s is not defined" % opt_name) parser.set( 'bitcoind', opt_name, "%s" % opt_value ) if utxo_opts is not None and len(utxo_opts) > 0: if parser.has_section( utxo_opts['utxo_provider'] ): parser.remove_section( utxo_opts['utxo_provider'] ) parser.add_section( utxo_opts['utxo_provider'] ) for opt_name, opt_value in utxo_opts.items(): # don't log this meta-field if opt_name == 'utxo_provider': continue if opt_value is None: raise Exception("%s is not defined" % opt_name) parser.set( utxo_opts['utxo_provider'], opt_name, "%s" % opt_value ) if dht_opts is not None and len(dht_opts) > 0: if parser.has_section("dht"): parser.remove_section("dht") parser.add_section( "dht" ) for opt_name, opt_value in dht_opts.items(): if opt_value is None: raise Exception("%s is not defined" % opt_name ) parser.set( "dht", opt_name, "%s" % opt_value ) if blockstore_opts is not None and len(blockstore_opts) > 0: if parser.has_section("blockstore"): parser.remove_section("blockstore") parser.add_section( "blockstore" ) for opt_name, opt_value in blockstore_opts.items(): if opt_value is None: raise Exception("%s is not defined" % opt_name ) parser.set( "blockstore", opt_name, "%s" % opt_value ) with open(config_file, "w") as fout: os.fchmod( fout.fileno(), 0600 ) parser.write( fout ) return True
def default_dht_opts(config_file=None): """ Get our default DHT options from the config file. """ global DHT_SERVER_PORT, DEFAULT_DHT_SERVERS if config_file is None: config_file = virtualchain.get_config_filename() defaults = { "disable": str(True), "port": str(DHT_SERVER_PORT), "servers": ",".join(["%s:%s" % (host, port) for (host, port) in DEFAULT_DHT_SERVERS]), } parser = SafeConfigParser(defaults) parser.read(config_file) if parser.has_section("dht"): disable = parser.get("dht", "disable") port = parser.get("dht", "port") servers = parser.get("dht", "servers") # expect comma-separated list of host:port if disable is None: disable = True if port is None: port = DHT_SERVER_PORT if servers is None: servers = DEFAULT_DHT_SERVERS if disable.lower() in ["no", "n", "0", "false"]: disable = False else: disable = True try: port = int(port) except: raise Exception("Invalid field value for dht.port: expected int") parsed_servers = [] try: server_list = servers.split(",") for server in server_list: server_host, server_port = server.split(":") server_port = int(server_port) parsed_servers.append((server_host, server_port)) except: raise Exception("Invalid field value for dht.servers: expected 'HOST:PORT[,HOST:PORT...]'") dht_opts = { "disable": disable, "port": port, "servers": ",".join(["%s:%s" % (host, port) for (host, port) in DEFAULT_DHT_SERVERS]), } return dht_opts else: # use defaults dht_opts = { "disable": True, "port": DHT_SERVER_PORT, "servers": ",".join(["%s:%s" % (host, port) for (host, port) in DEFAULT_DHT_SERVERS]), } return dht_opts