Source code for bitcoinlib.networks

# -*- coding: utf-8 -*-
#
#    BitcoinLib - Python Cryptocurrency Library
#    NETWORK class reads network definitions and with helper methods
#    © 2017 - 2020 February - 1200 Web Development <http://1200wd.com/>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import json
import binascii
import math
from bitcoinlib.main import *
from bitcoinlib.encoding import to_hexstring, change_base, to_bytes


_logger = logging.getLogger(__name__)


[docs]class NetworkError(Exception): """ Network Exception class """ def __init__(self, msg=''): self.msg = msg _logger.error(msg) def __str__(self): return self.msg
def _read_network_definitions(): """ Returns network definitions from json file in settings dir :return dict: Network definitions """ fn = Path(BCL_DATA_DIR, 'networks.json') f = fn.open() try: network_definitions = json.loads(f.read()) except json.decoder.JSONDecodeError as e: raise NetworkError("Error reading provider definitions from %s: %s" % (fn, e)) f.close() return network_definitions NETWORK_DEFINITIONS = _read_network_definitions() def _format_value(field, value): if field[:6] == 'prefix': return binascii.unhexlify(value) elif field == 'denominator': return float(value) else: return value
[docs]def network_values_for(field): """ Return all prefixes for field, i.e.: prefix_wif, prefix_address_p2sh, etc >>> network_values_for('prefix_wif') [b'\\x99', b'\\x80', b'\\xef', b'\\xb0', b'\\xb0', b'\\xef', b'\\xcc', b'\\xef', b'\\x9e', b'\\xf1'] >>> network_values_for('prefix_address_p2sh') [b'\\x95', b'\\x05', b'\\xc4', b'2', b'\\x05', b':', b'\\x10', b'\\x13', b'\\x16', b'\\xc4'] :param field: Prefix name from networks definitions (networks.json) :type field: str :return str: """ return [_format_value(field, nv[field]) for nv in NETWORK_DEFINITIONS.values()]
[docs]def network_by_value(field, value): """ Return all networks for field and (prefix) value. Example, get available networks for WIF or address prefix >>> network_by_value('prefix_wif', 'B0') ['litecoin', 'litecoin_legacy'] >>> network_by_value('prefix_address', '6f') ['testnet', 'litecoin_testnet'] This method does not work for HD prefixes, use 'wif_prefix_search' instead >>> network_by_value('prefix_address', '043587CF') [] :param field: Prefix name from networks definitions (networks.json) :type field: str :param value: Value of network prefix :type value: str, bytes :return list: Of network name strings """ nws = [(nv, NETWORK_DEFINITIONS[nv]['priority']) for nv in NETWORK_DEFINITIONS if NETWORK_DEFINITIONS[nv][field] == value] if not nws: try: value = to_hexstring(value).upper() except TypeError: pass nws = [(nv, NETWORK_DEFINITIONS[nv]['priority']) for nv in NETWORK_DEFINITIONS if NETWORK_DEFINITIONS[nv][field] == value] return [nw[0] for nw in sorted(nws, key=lambda x: x[1], reverse=True)]
[docs]def network_defined(network): """ Is network defined? Networks of this library are defined in networks.json in the operating systems user path. >>> network_defined('bitcoin') True >>> network_defined('ethereum') False :param network: Network name :type network: str :return bool: """ if network not in list(NETWORK_DEFINITIONS.keys()): return False return True
[docs]class Network(object): """ Network class with all network definitions. Prefixes for WIF, P2SH keys, HD public and private keys, addresses. A currency symbol and type, the denominator (such as satoshi) and a BIP0044 cointype. """ def __init__(self, network_name=DEFAULT_NETWORK): if network_name not in NETWORK_DEFINITIONS: raise NetworkError("Network %s not found in network definitions" % network_name) self.name = network_name self.currency_name = NETWORK_DEFINITIONS[network_name]['currency_name'] self.currency_name_plural = NETWORK_DEFINITIONS[network_name]['currency_name_plural'] self.currency_code = NETWORK_DEFINITIONS[network_name]['currency_code'] self.currency_symbol = NETWORK_DEFINITIONS[network_name]['currency_symbol'] self.description = NETWORK_DEFINITIONS[network_name]['description'] self.prefix_address_p2sh = binascii.unhexlify(NETWORK_DEFINITIONS[network_name]['prefix_address_p2sh']) self.prefix_address = binascii.unhexlify(NETWORK_DEFINITIONS[network_name]['prefix_address']) self.prefix_bech32 = NETWORK_DEFINITIONS[network_name]['prefix_bech32'] self.prefix_wif = binascii.unhexlify(NETWORK_DEFINITIONS[network_name]['prefix_wif']) self.denominator = NETWORK_DEFINITIONS[network_name]['denominator'] self.bip44_cointype = NETWORK_DEFINITIONS[network_name]['bip44_cointype'] self.dust_amount = NETWORK_DEFINITIONS[network_name]['dust_amount'] self.fee_default = NETWORK_DEFINITIONS[network_name]['fee_default'] self.fee_min = NETWORK_DEFINITIONS[network_name]['fee_min'] self.fee_max = NETWORK_DEFINITIONS[network_name]['fee_max'] self.priority = NETWORK_DEFINITIONS[network_name]['priority'] self.prefixes_wif = NETWORK_DEFINITIONS[network_name]['prefixes_wif'] # This could be more shorter and more flexible with this code, but this gives 'Unresolved attributes' warnings # for f in list(NETWORK_DEFINITIONS[network_name].keys()): # exec("self.%s = NETWORK_DEFINITIONS[network_name]['%s']" % (f, f)) def __repr__(self): return "<Network: %s>" % self.name def __eq__(self, other): return self.name == other.name def __hash__(self): return hash(self.name)
[docs] def print_value(self, value): """ Return the value as string with currency symbol Print value for 100000 satoshi as string in human readable format >>> Network('bitcoin').print_value(100000) '0.00100000 BTC' :param value: Value in smallest denominitor such as Satoshi :type value: int, float :return str: """ symb = self.currency_code denominator = self.denominator denominator_size = -int(math.log10(denominator)) balance = round(value * denominator, denominator_size) format_str = "%%.%df %%s" % denominator_size return format_str % (balance, symb)
[docs] def wif_prefix(self, is_private=False, witness_type='legacy', multisig=False): """ Get WIF prefix for this network and specifications in arguments >>> Network('bitcoin').wif_prefix() # xpub b'\\x04\\x88\\xb2\\x1e' >>> Network('bitcoin').wif_prefix(is_private=True, witness_type='segwit', multisig=True) # Zprv b'\\x02\\xaaz\\x99' :param is_private: Private or public key, default is True :type is_private: bool :param witness_type: Legacy, segwit or p2sh-segwit :type witness_type: str :param multisig: Multisignature or single signature wallet. Default is False: no multisig :type multisig: bool :return bytes: """ script_type = script_type_default(witness_type, multisig, locking_script=True) if script_type == 'p2sh' and witness_type in ['p2sh-segwit', 'segwit']: script_type = 'p2sh_p2wsh' if multisig else 'p2sh_p2wpkh' if is_private: ip = 'private' else: ip = 'public' found_prefixes = [to_bytes(pf[0]) for pf in self.prefixes_wif if pf[2] == ip and script_type == pf[5]] if found_prefixes: return found_prefixes[0] else: raise NetworkError("WIF Prefix for script type %s not found" % script_type)