def __init__(self, network='dash', base_url='', denominator=100000000, *args): """ Open connection to dashcore node :param network: Dash mainnet or testnet. Default is dash mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for Dash :type: str """ if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError( "Dashd connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password 'password' in dashd provider settings. " "Please set password and url in providers.json file") _logger.info("Connect to dashd") self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args)
def __init__(self, network='bitcoin', base_url='', denominator=100000000, *args): """ Open connection to bitcoin node :param network: Bitcoin mainnet or testnet. Default is bitcoin mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for bitcoin :type: str """ if isinstance(network, Network): network = network.name if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError( "Bitcoind connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file or check bitcoin config " "file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password in bitcoind provider settings. " "Please replace default password and set url in providers.json or bitcoin.conf file" ) _logger.info("Connect to bitcoind on %s" % base_url) self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args)
def __init__(self, network='bitcoin', base_url='', denominator=100000000, api_key=''): if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError("Bitcoind connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file" % base_url) if 'password' in base_url: raise ConfigError("Invalid password 'password' in bitcoind provider settings. " "Please set password and url in providers.json file") _logger.info("Connect to bitcoind on %s" % base_url) self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, api_key)
def __init__(self, name, m, n, signers, psbt, address_index): # Name of the wallet self.name = name # Signers required for bitcoin tx self.m = m # Total signers self.n = n # Dictionary self.signers = signers # Hex string self.psbt = psbt # Depth in HD derivation self.address_index = address_index # RPC connection to corresponding watch-only wallet in Bitcoin Core wallet_uri = self.wallet_template.format(**settings["rpc"], name=self.watchonly_name()) self.wallet_rpc = AuthServiceProxy(wallet_uri)
class DashdClient(BaseClient): """ Class to interact with dashd, the Dash deamon """ @staticmethod def from_config(configfile=None, network='dash'): """ Read settings from dashd config file :param configfile: Path to config file. Leave empty to look in default places :type: str :param network: Dash mainnet or testnet. Default is dash mainnet :type: str :return DashdClient: """ config = configparser.ConfigParser(strict=False) if not configfile: cfn = os.path.join(os.path.expanduser("~"), '.bitcoinlib/dash.conf') if not os.path.isfile(cfn): cfn = os.path.join(os.path.expanduser("~"), '.dashcore/dash.conf') if not os.path.isfile(cfn): raise ConfigError( "Please install dash client and specify a path to config file if path is not " "default. Or place a config file in .bitcoinlib/dash.conf to reference to " "an external server.") else: cfn = os.path.join(BCL_DATA_DIR, 'config', configfile) if not os.path.isfile(cfn): raise ConfigError("Config file %s not found" % cfn) with open(cfn, 'r') as f: config_string = '[rpc]\n' + f.read() config.read_string(config_string) try: if int(config.get('rpc', 'testnet')): network = 'testnet' except configparser.NoOptionError: pass if config.get('rpc', 'rpcpassword') == 'specify_rpc_password': raise ConfigError("Please update config settings in %s" % cfn) try: port = config.get('rpc', 'port') except configparser.NoOptionError: if network == 'testnet': port = 19998 else: port = 9998 server = '127.0.0.1' if 'bind' in config['rpc']: server = config.get('rpc', 'bind') elif 'externalip' in config['rpc']: server = config.get('rpc', 'externalip') url = "http://%s:%s@%s:%s" % (config.get( 'rpc', 'rpcuser'), config.get('rpc', 'rpcpassword'), server, port) return DashdClient(network, url) def __init__(self, network='dash', base_url='', denominator=100000000, *args): """ Open connection to dashcore node :param network: Dash mainnet or testnet. Default is dash mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for Dash :type: str """ if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError( "Dashd connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password 'password' in dashd provider settings. " "Please set password and url in providers.json file") _logger.info("Connect to dashd") self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args) def _parse_transaction(self, tx, block_height=None, get_input_values=True): t = Transaction.parse_hex(tx['hex'], strict=False, network=self.network) t.confirmations = None if 'confirmations' not in tx else tx[ 'confirmations'] if t.confirmations or block_height: t.status = 'confirmed' t.verified = True for i in t.inputs: if i.prev_txid == b'\x00' * 32: i.script_type = 'coinbase' continue if get_input_values: txi = self.proxy.getrawtransaction(i.prev_txid.hex(), 1) i.value = int( round( float(txi['vout'][i.output_n_int]['value']) / self.network.denominator)) for o in t.outputs: o.spent = None t.block_height = block_height t.version = tx['version'].to_bytes(4, 'big') t.date = datetime.utcfromtimestamp(tx['blocktime']) t.update_totals() return t def gettransaction(self, txid): tx = self.proxy.getrawtransaction(txid, 1) return self._parse_transaction(tx) def getrawtransaction(self, txid): res = self.proxy.getrawtransaction(txid) return res def sendrawtransaction(self, rawtx): res = self.proxy.sendrawtransaction(rawtx) return {'txid': res, 'response_dict': res} def estimatefee(self, blocks): try: res = self.proxy.estimatesmartfee(blocks)['feerate'] except KeyError: res = self.proxy.estimatefee(blocks) return int(res * self.units) def blockcount(self): return self.proxy.getblockcount() def getutxos(self, address, after_txid='', limit=MAX_TRANSACTIONS): txs = [] txs_list = self.proxy.listunspent(0, 99999999, [address]) for t in sorted(txs_list, key=lambda x: x['confirmations'], reverse=True): txs.append({ 'address': t['address'], 'txid': t['txid'], 'confirmations': t['confirmations'], 'output_n': t['vout'], 'input_n': -1, 'block_height': None, 'fee': None, 'size': 0, 'value': int(t['amount'] * self.units), 'script': t['scriptPubKey'], 'date': None, }) if t['txid'] == after_txid: txs = [] return txs def getblock(self, blockid, parse_transactions=True, page=1, limit=None): if isinstance(blockid, int): blockid = self.proxy.getblockhash(blockid) if not limit: limit = 99999 txs = [] if parse_transactions: bd = self.proxy.getblock(blockid, 2) for tx in bd['tx'][(page - 1) * limit:page * limit]: # try: tx['blocktime'] = bd['time'] tx['blockhash'] = bd['hash'] txs.append( self._parse_transaction(tx, block_height=bd['height'], get_input_values=False)) # except Exception as e: # _logger.error("Could not parse tx %s with error %s" % (tx['txid'], e)) # txs += [tx['hash'] for tx in bd['tx'][len(txs):]] else: bd = self.proxy.getblock(blockid, 1) txs = bd['tx'] block = { 'bits': int(bd['bits'], 16), 'depth': bd['confirmations'], 'hash': bd['hash'], 'height': bd['height'], 'merkle_root': bd['merkleroot'], 'nonce': bd['nonce'], 'prev_block': bd['previousblockhash'], 'time': bd['time'], 'total_txs': bd['nTx'], 'txs': txs, 'version': bd['version'], 'page': page, 'pages': None, 'limit': limit } return block def getrawblock(self, blockid): if isinstance(blockid, int): blockid = self.proxy.getblockhash(blockid) return self.proxy.getblock(blockid, 0) def isspent(self, txid, index): res = self.proxy.gettxout(txid, index) if not res: return True return False def getinfo(self): info = self.proxy.getmininginfo() return { 'blockcount': info['blocks'], 'chain': info['chain'], 'difficulty': int(info['difficulty']), 'hashrate': int(info['networkhashps']), 'mempool_size': int(info['pooledtx']), }
class DashdClient(BaseClient): """ Class to interact with dashd, the Dash deamon """ @staticmethod def from_config(configfile=None, network='dash'): """ Read settings from dashd config file :param configfile: Path to config file. Leave empty to look in default places :type: str :param network: Dash mainnet or testnet. Default is dash mainnet :type: str :return DashdClient: """ config = configparser.ConfigParser(strict=False) if not configfile: cfn = os.path.join(os.path.expanduser("~"), '.bitcoinlib/config/dash.conf') if not os.path.isfile(cfn): cfn = os.path.join(os.path.expanduser("~"), '.dashcore/dash.conf') if not os.path.isfile(cfn): raise ConfigError("Please install dash client and specify a path to config file if path is not " "default. Or place a config file in .bitcoinlib/config/dash.conf to reference to " "an external server.") else: cfn = os.path.join(BCL_DATA_DIR, 'config', configfile) if not os.path.isfile(cfn): raise ConfigError("Config file %s not found" % cfn) with open(cfn, 'r') as f: config_string = '[rpc]\n' + f.read() config.read_string(config_string) try: if int(config.get('rpc', 'testnet')): network = 'testnet' except configparser.NoOptionError: pass if config.get('rpc', 'rpcpassword') == 'specify_rpc_password': raise ConfigError("Please update config settings in %s" % cfn) try: port = config.get('rpc', 'port') except configparser.NoOptionError: if network == 'testnet': port = 19998 else: port = 9998 server = '127.0.0.1' if 'bind' in config['rpc']: server = config.get('rpc', 'bind') elif 'externalip' in config['rpc']: server = config.get('rpc', 'externalip') url = "http://%s:%s@%s:%s" % (config.get('rpc', 'rpcuser'), config.get('rpc', 'rpcpassword'), server, port) return DashdClient(network, url) def __init__(self, network='dash', base_url='', denominator=100000000, *args): """ Open connection to dashcore node :param network: Dash mainnet or testnet. Default is dash mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for Dash :type: str """ if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError("Dashd connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file" % base_url) if 'password' in base_url: raise ConfigError("Invalid password 'password' in dashd provider settings. " "Please set password and url in providers.json file") _logger.info("Connect to dashd on %s" % base_url) self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args) def gettransaction(self, txid): tx = self.proxy.getrawtransaction(txid, 1) t = Transaction.import_raw(tx['hex'], network=self.network) t.confirmations = tx['confirmations'] if t.confirmations: t.status = 'confirmed' t.verified = True for i in t.inputs: txi = self.proxy.getrawtransaction(to_hexstring(i.prev_hash), 1) value = int(float(txi['vout'][i.output_n_int]['value']) / self.network.denominator) i.value = value t.block_hash = tx['blockhash'] t.version = tx['version'] t.date = datetime.fromtimestamp(tx['blocktime']) t.update_totals() return t def getrawtransaction(self, txid): res = self.proxy.getrawtransaction(txid) return res def sendrawtransaction(self, rawtx): res = self.proxy.sendrawtransaction(rawtx) return { 'txid': res, 'response_dict': res } def estimatefee(self, blocks): try: res = self.proxy.estimatesmartfee(blocks)['feerate'] except KeyError: res = self.proxy.estimatefee(blocks) return int(res * self.units) def blockcount(self): return self.proxy.getblockcount() def getutxos(self, address, after_txid='', max_txs=MAX_TRANSACTIONS): txs = [] for t in self.proxy.listunspent(0, 99999999, [address]): txs.append({ 'address': t['address'], 'tx_hash': t['txid'], 'confirmations': t['confirmations'], 'output_n': t['vout'], 'input_n': -1, 'block_height': None, 'fee': None, 'size': 0, 'value': int(t['amount'] * self.units), 'script': t['scriptPubKey'], 'date': None, }) return txs
class BitcoindClient(BaseClient): """ Class to interact with bitcoind, the Bitcoin deamon """ @staticmethod def from_config(configfile=None, network='bitcoin'): """ Read settings from bitcoind config file :param configfile: Path to config file. Leave empty to look in default places :type: str :param network: Bitcoin mainnet or testnet. Default is bitcoin mainnet :type: str :return BitcoindClient: """ try: config = configparser.ConfigParser(strict=False) except TypeError: config = configparser.ConfigParser() config_fn = 'bitcoin.conf' if isinstance(network, Network): network = network.name if network == 'testnet': config_fn = 'bitcoin-testnet.conf' cfn = None if not configfile: config_locations = [ '~/.bitcoinlib', '~/.bitcoin', '~/Application Data/Bitcoin', '~/Library/Application Support/Bitcoin' ] for location in config_locations: cfn = Path(location, config_fn).expanduser() if cfn.exists(): break else: cfn = Path(BCL_DATA_DIR, 'config', configfile) if not cfn or not cfn.is_file(): raise ConfigError( "Config file %s not found. Please install bitcoin client and specify a path to config " "file if path is not default. Or place a config file in .bitcoinlib/bitcoin.conf to " "reference to an external server." % cfn) try: config.read(cfn) except Exception: with cfn.open() as f: config_string = '[rpc]\n' + f.read() config.read_string(config_string) testnet = _read_from_config(config, 'rpc', 'testnet') if testnet: network = 'testnet' if _read_from_config(config, 'rpc', 'rpcpassword') == 'specify_rpc_password': raise ConfigError("Please update config settings in %s" % cfn) if network == 'testnet': port = 18332 else: port = 8332 port = _read_from_config(config, 'rpc', 'rpcport', port) server = '127.0.0.1' server = _read_from_config(config, 'rpc', 'rpcconnect', server) server = _read_from_config(config, 'rpc', 'bind', server) server = _read_from_config(config, 'rpc', 'externalip', server) url = "http://%s:%s@%s:%s" % (config.get( 'rpc', 'rpcuser'), config.get('rpc', 'rpcpassword'), server, port) return BitcoindClient(network, url) def __init__(self, network='bitcoin', base_url='', denominator=100000000, *args): """ Open connection to bitcoin node :param network: Bitcoin mainnet or testnet. Default is bitcoin mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for bitcoin :type: str """ if isinstance(network, Network): network = network.name if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError( "Bitcoind connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file or check bitcoin config " "file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password in bitcoind provider settings. " "Please replace default password and set url in providers.json or bitcoin.conf file" ) _logger.info("Connect to bitcoind on %s" % base_url) self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args) # def getbalance def getutxos(self, address, after_txid='', limit=MAX_TRANSACTIONS): txs = [] res = self.proxy.getaddressinfo(address) if not (res['ismine'] or res['iswatchonly']): raise ClientError( "Address %s not found in bitcoind wallet, use 'importpubkey' or 'importaddress' to add " "address to wallet." % address) txs_list = self.proxy.listunspent(0, 99999999, [address]) for t in sorted(txs_list, key=lambda x: x['confirmations'], reverse=True): txs.append({ 'address': t['address'], 'tx_hash': t['txid'], 'confirmations': t['confirmations'], 'output_n': t['vout'], 'input_n': -1, 'block_height': None, 'fee': None, 'size': 0, 'value': int(t['amount'] * self.units), 'script': t['scriptPubKey'], 'date': None, }) if t['txid'] == after_txid: txs = [] return txs def _parse_transaction(self, tx, block_height=None, get_input_values=True): t = Transaction.import_raw(tx['hex'], network=self.network) t.confirmations = None if 'confirmations' not in tx else tx[ 'confirmations'] if t.confirmations or block_height: t.status = 'confirmed' t.verified = True for i in t.inputs: if i.prev_hash == b'\x00' * 32: i.script_type = 'coinbase' continue if get_input_values: txi = self.proxy.getrawtransaction(to_hexstring(i.prev_hash), 1) i.value = int( round( float(txi['vout'][i.output_n_int]['value']) / self.network.denominator)) for o in t.outputs: o.spent = None t.block_hash = tx.get('block_hash', tx['txid']) # FIXME, use only one t.block_height = block_height t.version = struct.pack('>L', tx['version']) t.date = datetime.utcfromtimestamp(tx['time']) t.update_totals() return t def gettransaction(self, txid): tx = self.proxy.getrawtransaction(txid, 1) return self._parse_transaction(tx) # def gettransactions def getrawtransaction(self, txid): res = self.proxy.getrawtransaction(txid) return res def sendrawtransaction(self, rawtx): res = self.proxy.sendrawtransaction(rawtx) return {'txid': res, 'response_dict': res} def estimatefee(self, blocks): pres = '' try: pres = self.proxy.estimatesmartfee(blocks) res = pres['feerate'] except KeyError as e: _logger.info("bitcoind error: %s, %s" % (e, pres)) res = self.proxy.estimatefee(blocks) return int(res * self.units) def blockcount(self): return self.proxy.getblockcount() def mempool(self, txid=''): txids = self.proxy.getrawmempool() if not txid: return txids elif txid in txids: return [txid] return [] def getblock(self, blockid, parse_transactions=True, page=None, limit=None): if isinstance(blockid, int): blockid = self.proxy.getblockhash(blockid) if not limit: limit = 99999 txs = [] if parse_transactions: bd = self.proxy.getblock(blockid, 2) for tx in bd['tx'][(page - 1) * limit:page * limit]: # try: tx['time'] = bd['time'] tx['txid'] = bd['hash'] txs.append( self._parse_transaction(tx, block_height=bd['height'], get_input_values=False)) # except Exception as e: # _logger.error("Could not parse tx %s with error %s" % (tx['txid'], e)) # txs += [tx['hash'] for tx in bd['tx'][len(txs):]] else: bd = self.proxy.getblock(blockid, 1) txs = bd['tx'] block = { 'bits': bd['bits'], 'depth': bd['confirmations'], 'block_hash': bd['hash'], 'height': bd['height'], 'merkle_root': bd['merkleroot'], 'nonce': bd['nonce'], 'prev_block': None if 'previousblockhash' not in bd else bd['previousblockhash'], 'time': bd['time'], 'tx_count': bd['nTx'], 'txs': txs, 'version': bd['version'], 'page': page, 'pages': None, 'limit': limit } return block def getrawblock(self, blockid): if isinstance(blockid, int): blockid = self.proxy.getblockhash(blockid) return self.proxy.getblock(blockid, 0) def isspent(self, txid, index): res = self.proxy.gettxout(txid, index) if not res: return True return False
class LitecoindClient(BaseClient): """ Class to interact with litecoind, the Litecoin deamon """ @staticmethod def from_config(configfile=None, network='litecoin'): """ Read settings from litecoind config file :param configfile: Path to config file. Leave empty to look in default places :type: str :param network: Litecoin mainnet or testnet. Default is litecoin mainnet :type: str :return LitecoindClient: """ if PY3: config = configparser.ConfigParser(strict=False) else: config = configparser.ConfigParser() config_fn = 'litecoin.conf' if isinstance(network, Network): network = network.name if network == 'testnet': config_fn = 'litecoin-testnet.conf' if not configfile: cfn = os.path.join(os.path.expanduser("~"), '.bitcoinlib/config/%s' % config_fn) if not os.path.isfile(cfn): # Linux cfn = os.path.join(os.path.expanduser("~"), '.litecoin/%s' % config_fn) if not os.path.isfile(cfn): # Try Windows path cfn = os.path.join(os.path.expanduser("~"), 'Application Data/Litecoin/%s' % config_fn) if not os.path.isfile(cfn): # Try Mac path cfn = os.path.join( os.path.expanduser("~"), 'Library/Application Support/Litecoin/%s' % config_fn) if not os.path.isfile(cfn): raise ConfigError( "Please install litecoin client and specify a path to config file if path is not " "default. Or place a config file in .bitcoinlib/config/litecoin.conf to reference to " "an external server.") else: cfn = os.path.join(BCL_CONFIG_DIR, configfile) if not os.path.isfile(cfn): raise ConfigError("Config file %s not found" % cfn) try: config.read(cfn) except Exception: with open(cfn, 'r') as f: config_string = '[rpc]\n' + f.read() config.read_string(config_string) testnet = _read_from_config(config, 'rpc', 'testnet') if testnet: network = 'testnet' if _read_from_config(config, 'rpc', 'rpcpassword') == 'specify_rpc_password': raise ConfigError("Please update config settings in %s" % cfn) if network == 'testnet': port = 19332 else: port = 9332 port = _read_from_config(config, 'rpc', 'rpcport', port) server = '127.0.0.1' server = _read_from_config(config, 'rpc', 'rpcconnect', server) server = _read_from_config(config, 'rpc', 'bind', server) server = _read_from_config(config, 'rpc', 'externalip', server) url = "http://%s:%s@%s:%s" % (config.get( 'rpc', 'rpcuser'), config.get('rpc', 'rpcpassword'), server, port) return LitecoindClient(network, url) def __init__(self, network='litecoin', base_url='', denominator=100000000, *args): """ Open connection to litecoin node :param network: Litecoin mainnet or testnet. Default is litecoin mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for litecoin :type: str """ if isinstance(network, Network): network = network.name if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError( "Litecoind connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file or check litecoin config " "file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password in litecoind provider settings. " "Please replace default password and set url in providers.json or litecoin.conf file" ) _logger.info("Connect to litecoind on %s" % base_url) self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args) # def getbalance def getutxos(self, address, after_txid='', max_txs=MAX_TRANSACTIONS): txs = [] res = self.proxy.getaddressinfo(address) if not (res['ismine'] or res['iswatchonly']): raise ClientError( "Address %s not found in litecoind wallet, use 'importaddress' to add address to " "wallet." % address) for t in self.proxy.listunspent(0, 99999999, [address]): txs.append({ 'address': t['address'], 'tx_hash': t['txid'], 'confirmations': t['confirmations'], 'output_n': t['vout'], 'input_n': -1, 'block_height': None, 'fee': None, 'size': 0, 'value': int(t['amount'] * self.units), 'script': t['scriptPubKey'], 'date': None, }) return txs def gettransaction(self, txid): tx = self.proxy.getrawtransaction(txid, 1) t = Transaction.import_raw(tx['hex'], network=self.network) t.confirmations = tx['confirmations'] if t.confirmations: t.status = 'confirmed' t.verified = True for i in t.inputs: if i.prev_hash == b'\x00' * 32: i.value = t.output_total i.script_type = 'coinbase' continue txi = self.proxy.getrawtransaction(to_hexstring(i.prev_hash), 1) i.value = int( round( float(txi['vout'][i.output_n_int]['value']) / self.network.denominator)) for o in t.outputs: o.spent = None t.block_hash = tx['blockhash'] t.version = struct.pack('>L', tx['version']) t.date = datetime.fromtimestamp(tx['blocktime']) t.update_totals() t.hash = txid return t # def gettransactions def getrawtransaction(self, txid): res = self.proxy.getrawtransaction(txid) return res def sendrawtransaction(self, rawtx): res = self.proxy.sendrawtransaction(rawtx) return {'txid': res, 'response_dict': res} def estimatefee(self, blocks): pres = '' try: pres = self.proxy.estimatesmartfee(blocks) res = pres['feerate'] except KeyError as e: _logger.warning("litecoind error: %s, %s" % (e, pres)) res = self.proxy.estimatefee(blocks) return int(res * self.units) def blockcount(self): return self.proxy.getblockcount() def mempool(self, txid=''): txids = self.proxy.getrawmempool() if not txid: return txids elif txid in txids: return [txid] return []
class DogecoindClient(BaseClient): """ Class to interact with dogecoind, the Dogecoin daemon """ @staticmethod def from_config(configfile=None, network='dogecoin'): """ Read settings from dogecoind config file :param configfile: Path to config file. Leave empty to look in default places :type: str :param network: Dogecoin mainnet or testnet. Default is dogecoin mainnet :type: str :return DogecoindClient: """ try: config = configparser.ConfigParser(strict=False) except TypeError: config = configparser.ConfigParser() config_fn = 'dogecoin.conf' if isinstance(network, Network): network = network.name if network == 'testnet': config_fn = 'dogecoin-testnet.conf' cfn = None if not configfile: config_locations = [ '~/.bitcoinlib', '~/.dogecoin', '~/Application Data/Dogecoin', '~/Library/Application Support/Dogecoin' ] for location in config_locations: cfn = Path(location, config_fn).expanduser() if cfn.exists(): break else: cfn = Path(BCL_DATA_DIR, 'config', configfile) if not cfn or not cfn.is_file(): raise ConfigError( "Config file %s not found. Please install dogecoin client and specify a path to config " "file if path is not default. Or place a config file in .bitcoinlib/dogecoin.conf to " "reference to an external server." % cfn) try: config.read(cfn) except Exception: with cfn.open() as f: config_string = '[rpc]\n' + f.read() config.read_string(config_string) testnet = _read_from_config(config, 'rpc', 'testnet') if testnet: network = 'testnet' if _read_from_config(config, 'rpc', 'rpcpassword') == 'specify_rpc_password': raise ConfigError("Please update config settings in %s" % cfn) if network == 'testnet': port = 44555 else: port = 22555 port = _read_from_config(config, 'rpc', 'rpcport', port) server = '127.0.0.1' server = _read_from_config(config, 'rpc', 'rpcconnect', server) server = _read_from_config(config, 'rpc', 'bind', server) server = _read_from_config(config, 'rpc', 'externalip', server) url = "http://%s:%s@%s:%s" % (config.get( 'rpc', 'rpcuser'), config.get('rpc', 'rpcpassword'), server, port) return DogecoindClient(network, url) def __init__(self, network='dogecoin', base_url='', denominator=100000000, *args): """ Open connection to dogecoin node :param network: Dogecoin mainnet or testnet. Default is dogecoin mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for dogecoin :type: str """ if isinstance(network, Network): network = network.name if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError( "Dogecoind connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file or check dogecoin config " "file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password in dogecoind provider settings. " "Please replace default password and set url in providers.json or dogecoin.conf file" ) _logger.info("Connect to dogecoind") self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args) def getutxos(self, address, after_txid='', max_txs=MAX_TRANSACTIONS): txs = [] for t in self.proxy.listunspent(0, 99999999, [address]): txs.append({ 'address': t['address'], 'txid': t['txid'], 'confirmations': t['confirmations'], 'output_n': t['vout'], 'input_n': -1, 'block_height': None, 'fee': None, 'size': 0, 'value': int(t['amount'] * self.units), 'script': t['scriptPubKey'], 'date': None, }) return txs def gettransaction(self, txid, block_height=None, get_input_values=True): tx = self.proxy.getrawtransaction(txid, 1) t = Transaction.parse_hex(tx['hex'], strict=False, network=self.network) t.confirmations = tx['confirmations'] if t.confirmations: t.status = 'confirmed' t.verified = True for i in t.inputs: if i.prev_txid == b'\x00' * 32: i.value = t.output_total i.script_type = 'coinbase' continue if get_input_values: txi = self.proxy.getrawtransaction(i.prev_txid.hex(), 1) i.value = int( round( float(txi['vout'][i.output_n_int]['value']) / self.network.denominator)) for o in t.outputs: o.spent = None t.version = tx['version'].to_bytes(4, 'big') t.date = datetime.fromtimestamp(tx['blocktime']) t.block_height = block_height t.update_totals() return t def getrawtransaction(self, txid): return self.proxy.getrawtransaction(txid) def sendrawtransaction(self, rawtx): res = self.proxy.sendrawtransaction(rawtx) return {'txid': res, 'response_dict': res} def estimatefee(self, blocks): pres = '' try: pres = self.proxy.estimatesmartfee(blocks) res = pres['feerate'] except KeyError as e: _logger.info("dogecoind error: %s, %s" % (e, pres)) res = self.proxy.estimatefee(blocks) return int(res * self.units) def blockcount(self): return self.proxy.getblockcount() def mempool(self, txid=''): txids = self.proxy.getrawmempool() if not txid: return txids elif txid in txids: return [txid] return [] def getinfo(self): info = self.proxy.getmininginfo() return { 'blockcount': info['blocks'], 'chain': info['chain'], 'difficulty': int(info['difficulty']), 'hashrate': int(info['networkhashps']), 'mempool_size': int(info['pooledtx']), }
class BitcoindClient(BaseClient): """ Class to interact with bitcoind, the Bitcoin deamon """ @staticmethod def from_config(configfile=None, network='bitcoin'): """ Read settings from bitcoind config file :param configfile: Path to config file. Leave empty to look in default places :type: str :param network: Bitcoin mainnet or testnet. Default is bitcoin mainnet :type: str :return BitcoindClient: """ config = configparser.ConfigParser(strict=False) if not configfile: cfn = os.path.join(os.path.expanduser("~"), '.bitcoinlib/config/bitcoin.conf') if not os.path.isfile(cfn): cfn = os.path.join(os.path.expanduser("~"), '.bitcoin/bitcoin.conf') if not os.path.isfile(cfn): raise ConfigError( "Please install bitcoin client and specify a path to config file if path is not " "default. Or place a config file in .bitcoinlib/config/bitcoin.conf to reference to " "an external server.") else: cfn = os.path.join(DEFAULT_SETTINGSDIR, configfile) if not os.path.isfile(cfn): raise ConfigError("Config file %s not found" % cfn) with open(cfn, 'r') as f: config_string = '[rpc]\n' + f.read() config.read_string(config_string) try: if int(config.get('rpc', 'testnet')): network = 'testnet' except configparser.NoOptionError: pass if config.get('rpc', 'rpcpassword') == 'specify_rpc_password': raise ConfigError("Please update config settings in %s" % cfn) try: port = config.get('rpc', 'port') except configparser.NoOptionError: if network == 'testnet': port = 18332 else: port = 8332 server = '127.0.0.1' if 'bind' in config['rpc']: server = config.get('rpc', 'bind') elif 'externalip' in config['rpc']: server = config.get('rpc', 'externalip') url = "http://%s:%s@%s:%s" % (config.get( 'rpc', 'rpcuser'), config.get('rpc', 'rpcpassword'), server, port) return BitcoindClient(network, url) def __init__(self, network='bitcoin', base_url='', denominator=100000000, *args): """ Open connection to bitcoin node :param network: Bitcoin mainnet or testnet. Default is bitcoin mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for bitcoin :type: str """ if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError( "Bitcoind connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password 'password' in bitcoind provider settings. " "Please set password and url in providers.json file") _logger.info("Connect to bitcoind on %s" % base_url) self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, *args) def getrawtransaction(self, txid): res = self.proxy.getrawtransaction(txid) return res def gettransaction(self, txid): tx = self.proxy.getrawtransaction(txid, 1) t = Transaction.import_raw(tx['hex']) t.confirmations = tx['confirmations'] if t.confirmations: t.status = 'confirmed' t.verified = True for i in t.inputs: if i.prev_hash == b'\x00' * 32: i.value = t.output_total i.script_type = 'coinbase' continue txi = self.proxy.getrawtransaction(to_hexstring(i.prev_hash), 1) i.value = int( round( float(txi['vout'][i.output_n_int]['value']) / self.network.denominator)) for o in t.outputs: o.spent = None t.block_hash = tx['blockhash'] t.version = struct.pack('>L', tx['version']) t.date = datetime.fromtimestamp(tx['blocktime']) t.hash = txid t.update_totals() return t def sendrawtransaction(self, rawtx): res = self.proxy.sendrawtransaction(rawtx) return {'txid': res, 'response_dict': res} def estimatefee(self, blocks): try: res = self.proxy.estimatesmartfee(blocks)['feerate'] except KeyError: res = self.proxy.estimatefee(blocks) return int(res * self.units)
class BitcoindClient(BaseClient): """ Class to interact with bitcoind, the Bitcoin deamon """ @staticmethod def from_config(configfile=None, network='bitcoin'): """ Read settings from bitcoind config file :param configfile: Path to config file. Leave empty to look in default places :type: str :param network: Bitcoin mainnet or testnet. Default is bitcoin mainnet :type: str :return BitcoindClient: """ config = configparser.ConfigParser() if not configfile: cfn = os.path.join(os.path.expanduser("~"), '.bitcoinlib/config/bitcoin.conf') if not os.path.isfile(cfn): cfn = os.path.join(os.path.expanduser("~"), '.bitcoin/bitcoin.conf') if not os.path.isfile(cfn): raise ConfigError( "Please install bitcoin client and specify a path to config file if path is not " "default. Or place a config file in .bitcoinlib/config/bitcoin.conf to reference to " "an external server.") else: cfn = os.path.join(DEFAULT_SETTINGSDIR, configfile) if not os.path.isfile(cfn): raise ConfigError("Config file %s not found" % cfn) with open(cfn, 'r') as f: config_string = '[rpc]\n' + f.read() config.read_string(config_string) try: if config.get('rpc', 'testnet'): network = 'testnet' except configparser.NoOptionError: pass if config.get('rpc', 'rpcpassword') == 'specify_rpc_password': raise ConfigError("Please update config settings in %s" % cfn) try: port = config.get('rpc', 'port') except configparser.NoOptionError: if network == 'testnet': port = 18332 else: port = 8332 try: server = config.get('rpc', 'bind') except configparser.NoOptionError: server = '127.0.0.1' url = "http://%s:%s@%s:%s" % (config.get( 'rpc', 'rpcuser'), config.get('rpc', 'rpcpassword'), server, port) return BitcoindClient(network, url) def __init__(self, network='bitcoin', base_url='', denominator=100000000, api_key=''): """ Open connection to bitcoin node :param network: Bitcoin mainnet or testnet. Default is bitcoin mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for bitcoin :type: str :param api_key: Leave empty for :type: str """ if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError( "Bitcoind connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password 'password' in bitcoind provider settings. " "Please set password and url in providers.json file") _logger.info("Connect to bitcoind on %s" % base_url) self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, api_key) def getrawtransaction(self, txid): res = self.proxy.getrawtransaction(txid) return res def sendrawtransaction(self, rawtx): return self.proxy.sendrawtransaction(rawtx) def estimatefee(self, blocks): try: res = self.proxy.estimatesmartfee(blocks)['feerate'] except KeyError: res = self.proxy.estimatefee(blocks) return int(res * self.units)
class BitcoindClient(BaseClient): @staticmethod def from_config(configfile=None, network='bitcoin'): config = configparser.ConfigParser() if not configfile: cfn = os.path.join(os.path.expanduser("~"), '.bitcoin/bitcoin.conf') if not os.path.isfile(cfn): cfn = os.path.join(os.path.expanduser("~"), '.bitcoinlib/config/bitcoin.conf') if not os.path.isfile(cfn): raise ConfigError( "Please install bitcoin client and specify a path to config file if path is not " "default. Or place a config file in .bitcoinlib/config/bitcoin.conf to reference to " "an external server.") else: cfn = os.path.join(DEFAULT_SETTINGSDIR, configfile) if not os.path.isfile(cfn): raise ConfigError("Config file %s not found" % cfn) with open(cfn, 'r') as f: config_string = '[rpc]\n' + f.read() config.read_string(config_string) if config.get('rpc', 'rpcpassword') == 'specify_rpc_password': raise ConfigError("Please update config settings in %s" % cfn) if network == 'bitcoin': port = 8332 elif network == 'testnet': port = 18332 else: raise ConfigError("Network %s not supported by BitcoindClient" % network) try: server = config.get('rpc', 'server') except: server = '127.0.0.1' url = "http://%s:%s@%s:%s" % (config.get( 'rpc', 'rpcuser'), config.get('rpc', 'rpcpassword'), server, port) return url def __init__(self, network='bitcoin', base_url='', denominator=100000000, api_key=''): if not base_url: base_url = self.from_config('', network) if len(base_url.split(':')) != 4: raise ConfigError( "Bitcoind connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password 'password' in bitcoind provider settings. " "Please set password and url in providers.json file") _logger.info("Connect to bitcoind on %s" % base_url) self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, api_key) def getrawtransaction(self, txid): res = self.proxy.getrawtransaction(txid) return res def sendrawtransaction(self, rawtx): return self.proxy.sendrawtransaction(rawtx) def estimatefee(self, blocks): res = self.proxy.estimatefee(blocks) return int(res * self.units) def address_transactions(self, addresslist): # TODO: write this method if possible pass
class MultiSig: wallet_template = "http://{username}:{password}@{host}:{port}/wallet/{name}" def __init__(self, name, m, n, signers, psbt, address_index): # Name of the wallet self.name = name # Signers required for bitcoin tx self.m = m # Total signers self.n = n # Dictionary self.signers = signers # Hex string self.psbt = psbt # Depth in HD derivation self.address_index = address_index # RPC connection to corresponding watch-only wallet in Bitcoin Core wallet_uri = self.wallet_template.format(**settings["rpc"], name=self.watchonly_name()) self.wallet_rpc = AuthServiceProxy(wallet_uri) def ready(self): return len(self.signers) == self.n def filename(self): return f"{self.name}.wallet" @classmethod def create(cls, name, m, n): # sanity check if m > n: raise JunctionError( f"\"m\" ({m}) must be no larger than \"n\" ({n})") # MultiSig instance multisig = cls(name, m, n, [], None, 0) # Never overwrite existing wallet files filename = multisig.filename() if os.path.exists(filename): raise JunctionError(f"{filename} already exists") # create a watch-only Bitcoin Core wallet multisig.create_watchonly() # Save a copy of wallet to disk multisig.save() # Return instance return multisig @classmethod def open(cls, filename): with open(filename) as f: multisig = cls.from_dict(json.load(f)) logger.info(f"Opened wallet from {filename}") return multisig def save(self): filename = self.filename() # if this line broke inside json.dump, the json file would be emptyed :eek: data = self.to_dict() with open(filename, "w") as f: json.dump(data, f, indent=4) logger.info(f"Saved wallet to {filename}") @classmethod def from_dict(cls, d): if d["psbt"]: psbt = hwilib.serializations.PSBT() psbt.deserialize(d["psbt"]) d["psbt"] = psbt return cls(**d) def to_dict(self): return { "name": self.name, "m": self.m, "n": self.n, "signers": self.signers, "psbt": self.psbt.serialize() if self.psbt else "", "address_index": self.address_index, } def add_signer(self, name, fingerprint, xpub, deriv_path): # FIXME: deriv_path not used ... if self.ready(): raise JunctionError( f'Already have {len(self.signers)} of {self.n} required signers' ) # Check if name used before if name in [signer["name"] for signer in self.signers]: raise JunctionError(f'Name "{name}" already taken') # check if fingerprint used before if fingerprint in [signer["fingerprint"] for signer in self.signers]: raise JunctionError(f'Fingerprint "{fingerprint}" already used') self.signers.append({ "name": name, "fingerprint": fingerprint, "xpub": xpub }) logger.info(f"Registered signer \"{name}\"") # Export next chunk watch-only addresses to Bitcoin Core if we're done adding signers if self.ready(): self.export_watchonly() self.save() def remove_signer(signer_name): raise NotImplementedError() def descriptor(self): '''Descriptor for shared multisig addresses''' # TODO: consider using HWI's Descriptor class origin_path = "/44h/1h/0h" path_suffix = "/0/*" xpubs = [ f'[{signer["fingerprint"]}{origin_path}]{signer["xpub"]}{path_suffix}' for signer in self.signers ] xpubs = ",".join(xpubs) descriptor = f"wsh(multi({self.m},{xpubs}))" logger.info(f"Exporting descriptor: {descriptor}") # validates and appends checksum r = self.wallet_rpc.getdescriptorinfo(descriptor) return r['descriptor'] def address(self): # TODO: this check could work nicely as a decorator if not self.ready(): raise JunctionError( f'{self.n} signers required, {len(self.signers)} registered') address = self.wallet_rpc.deriveaddresses( self.descriptor(), [self.address_index, self.address_index + 1])[0] self.address_index += 1 self.save() return address def create_watchonly(self): # Create watch-only Bitcoin Core wallet (FIXME super ugly) # Prefix the wallet name with "junction_" to make it clear this is a junction wallet watch_only_name = self.watchonly_name() bitcoin_wallets = bitcoin_rpc.listwallets() if watch_only_name not in bitcoin_wallets: try: bitcoin_rpc.loadwallet(watch_only_name) logger.info( f"Loaded watch-only Bitcoin Core wallet \"{watch_only_name}\"" ) except JSONRPCException as e: try: bitcoin_rpc.createwallet(watch_only_name, True) logger.info( f"Created watch-only Bitcoin Core wallet \"{watch_only_name}\"" ) except JSONRPCException as e: raise JunctionError( "Couldn't establish watch-only Bitcoin Core wallet") def watchonly_name(self): # maybe add a "junction_" prefix or something? return self.name def export_watchonly(self): logger.info("Starting watch-only export") self.wallet_rpc.importmulti([{ "desc": self.descriptor(), "timestamp": "now", "range": [self.address_index, settings["wallet"]["address_chunk"]], "watchonly": True, "keypool": True, "internal": False, }]) logger.info("Finished watch-only export") def create_psbt(self, recipient, amount): if self.psbt: raise JunctionError('PSBT already present') # FIXME bitcoin core can't generate change addrs change_address = self.address() raw_psbt = self.wallet_rpc.walletcreatefundedpsbt( # let Bitcoin Core choose inputs [], # Outputs [{ recipient: amount }], # Locktime 0, { # Include watch-only outputs "includeWatching": True, # Provide change address b/c Core can't generate it "changeAddress": change_address, }, # Include BIP32 derivation paths in the PSBT True, )['psbt'] # Serialize and save self.psbt = hwilib.serializations.PSBT() self.psbt.deserialize(raw_psbt) self.save() def remove_psbt(self): self.psbt = None def decode_psbt(self): return self.wallet_rpc.decodepsbt(self.psbt.serialize()) def broadcast(self): psbt_hex = self.psbt.serialize() tx_hex = self.wallet_rpc.finalizepsbt(psbt_hex)["hex"] return self.wallet_rpc.sendrawtransaction(tx_hex)
import json import logging import os.path import hwilib import toml from bitcoinlib.services.authproxy import AuthServiceProxy, JSONRPCException from pprint import pprint from hwilib.serializations import PSBT logger = logging.getLogger(__name__) settings = toml.load("settings.toml") bitcoin_uri = "http://{username}:{password}@{host}:{port}" bitcoin_rpc = AuthServiceProxy(bitcoin_uri.format(**settings["rpc"])) class JunctionError(Exception): pass class MultiSig: wallet_template = "http://{username}:{password}@{host}:{port}/wallet/{name}" def __init__(self, name, m, n, signers, psbt, address_index): # Name of the wallet self.name = name # Signers required for bitcoin tx self.m = m # Total signers
class QtumdClient(BaseClient): """ Class to interact with qtumd, the Qtum deamon """ @staticmethod def from_config(configfile=None, network='qtum'): """ Read settings from qtumd config file :param configfile: Path to config file. Leave empty to look in default places :type: str :param network: Qtum mainnet or testnet. Default is qtum mainnet :type: str :return QtumdClient: """ config = configparser.ConfigParser() if not configfile: cfn = os.path.join(os.path.expanduser("~"), '.bitcoinlib/config/qtum.conf') if not os.path.isfile(cfn): cfn = os.path.join(os.path.expanduser("~"), '.qtum/qtum.conf') if not os.path.isfile(cfn): raise ConfigError( "Please install qtum client and specify a path to config file if path is not " "default. Or place a config file in .bitcoinlib/config/qtum.conf to reference to " "an external server.") else: cfn = os.path.join(DEFAULT_SETTINGSDIR, configfile) if not os.path.isfile(cfn): raise ConfigError("Config file %s not found" % cfn) with open(cfn, 'r') as f: config_string = '[rpc]\n' + f.read() config.read_string(config_string) try: if config.get('rpc', 'testnet'): network = 'qtum_testnet' except configparser.NoOptionError: pass if config.get('rpc', 'rpcpassword') == 'specify_rpc_password': raise ConfigError("Please update config settings in %s" % cfn) try: port = config.get('rpc', 'port') except configparser.NoOptionError: if network == 'qtum_testnet': port = 8333 else: port = 8333 try: server = config.get('rpc', 'bind') except configparser.NoOptionError: server = '127.0.0.1' url = "http://%s:%s@%s:%s" % (config.get( 'rpc', 'rpcuser'), config.get('rpc', 'rpcpassword'), server, port) return QtumdClient(network, url) def __init__(self, network='qtum', base_url='', denominator=100000000, api_key=''): """ Open connection to qtum node :param network: Qtum mainnet or testnet. Default is qtum mainnet :type: str :param base_url: Connection URL in format http(s)://user:password@host:port. :type: str :param denominator: Denominator for this currency. Should be always 100000000 (satoshis) for qtum :type: str :param api_key: Leave empty for :type: str """ if not base_url: bdc = self.from_config('', network) base_url = bdc.base_url network = bdc.network if len(base_url.split(':')) != 4: raise ConfigError( "Qtumd connection URL must be of format 'http(s)://user:password@host:port," "current format is %s. Please set url in providers.json file" % base_url) if 'password' in base_url: raise ConfigError( "Invalid password 'password' in qtumd provider settings. " "Please set password and url in providers.json file") _logger.info("Connect to qtumd on %s" % base_url) self.proxy = AuthServiceProxy(base_url) super(self.__class__, self).__init__(network, PROVIDERNAME, base_url, denominator, api_key) def getutxos(self, addresslist): res = self.proxy.listunspent(3, 9999999, addresslist) txs = [] for tx in res: txs.append({ 'address': tx['address'], 'tx_hash': tx['txid'], 'confirmations': tx['confirmations'], 'output_n': tx['vout'], 'index': 0, 'value': int(round(tx['amount'] * self.units, 0)), 'script': tx['scriptPubKey'], 'date': 0 }) return txs def getbalance(self, addresslist): res = self.proxy.listunspent(3, 9999999, addresslist) balance = 0 for tx in res: balance += tx['values'] return balance def getrawtransaction(self, txid): res = self.proxy.getrawtransaction(txid) return res def sendrawtransaction(self, rawtx): return self.proxy.sendrawtransaction(rawtx) def estimatefee(self, blocks): try: res = self.proxy.estimatesmartfee(blocks)['feerate'] except KeyError: res = self.proxy.estimatefee(blocks) return int(res * self.units)