tMerge pull request #4008 from SomberNight/network_constants_refactor - electrum - Electrum Bitcoin wallet
 (HTM) git clone https://git.parazyd.org/electrum
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
       ---
 (DIR) commit c3ff6040d25eb58a874aac2ff4355292843daec3
 (DIR) parent 1c0e23c8b90a99f7a2fe1b8e2dbbd128fa1c7d5f
 (HTM) Author: ThomasV <thomasv@electrum.org>
       Date:   Sun,  4 Mar 2018 23:13:50 +0100
       
       Merge pull request #4008 from SomberNight/network_constants_refactor
       
       refactor network constants
       Diffstat:
         M electrum                            |       3 ++-
         M gui/kivy/main_window.py             |       4 ++--
         M gui/qt/exception_window.py          |       4 ++--
         M gui/qt/main_window.py               |       5 +++--
         M gui/qt/network_dialog.py            |       6 +++---
         M lib/bitcoin.py                      |     131 +++++++++----------------------
         M lib/blockchain.py                   |      11 ++++++-----
         A lib/constants.py                    |     107 +++++++++++++++++++++++++++++++
         M lib/keystore.py                     |       4 ++--
         M lib/network.py                      |      11 ++++++-----
         M lib/tests/test_bitcoin.py           |       7 ++++---
         M lib/util.py                         |       4 ++--
         M plugins/digitalbitbox/digitalbitbo… |       7 ++++---
         M plugins/keepkey/plugin.py           |       9 +++++----
         M plugins/trezor/trezor.py            |       5 +++--
         M plugins/trustedcoin/trustedcoin.py  |       5 +++--
       
       16 files changed, 191 insertions(+), 132 deletions(-)
       ---
 (DIR) diff --git a/electrum b/electrum
       t@@ -89,6 +89,7 @@ if is_local or is_android:
        
        
        from electrum import bitcoin, util
       +from electrum import constants
        from electrum import SimpleConfig, Network
        from electrum.wallet import Wallet, Imported_Wallet
        from electrum.storage import WalletStorage, get_derivation_used_for_hw_device_encryption
       t@@ -411,7 +412,7 @@ if __name__ == '__main__':
            cmdname = config.get('cmd')
        
            if config.get('testnet'):
       -        bitcoin.NetworkConstants.set_testnet()
       +        constants.set_testnet()
        
            # run non-RPC commands separately
            if cmdname in ['create', 'restore']:
 (DIR) diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py
       t@@ -99,8 +99,8 @@ class ElectrumWindow(App):
                from .uix.dialogs.choice_dialog import ChoiceDialog
                protocol = 's'
                def cb2(host):
       -            from electrum.bitcoin import NetworkConstants
       -            pp = servers.get(host, NetworkConstants.DEFAULT_PORTS)
       +            from electrum import constants
       +            pp = servers.get(host, constants.net.DEFAULT_PORTS)
                    port = pp.get(protocol, '')
                    popup.ids.host.text = host
                    popup.ids.port.text = port
 (DIR) diff --git a/gui/qt/exception_window.py b/gui/qt/exception_window.py
       t@@ -36,7 +36,7 @@ from PyQt5.QtGui import QIcon
        from PyQt5.QtWidgets import *
        
        from electrum.i18n import _
       -from electrum import ELECTRUM_VERSION, bitcoin
       +from electrum import ELECTRUM_VERSION, bitcoin, constants
        
        issue_template = """<h2>Traceback</h2>
        <pre>
       t@@ -107,7 +107,7 @@ class Exception_Window(QWidget):
                self.show()
        
            def send_report(self):
       -        if bitcoin.NetworkConstants.GENESIS[-4:] not in ["4943", "e26f"] and ".electrum.org" in report_server:
       +        if constants.net.GENESIS[-4:] not in ["4943", "e26f"] and ".electrum.org" in report_server:
                    # Gah! Some kind of altcoin wants to send us crash reports.
                    self.main_window.show_critical(_("Please report this issue manually."))
                    return
 (DIR) diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -40,7 +40,8 @@ from .exception_window import Exception_Hook
        from PyQt5.QtWidgets import *
        
        from electrum import keystore, simple_config
       -from electrum.bitcoin import COIN, is_address, TYPE_ADDRESS, NetworkConstants
       +from electrum.bitcoin import COIN, is_address, TYPE_ADDRESS
       +from electrum import constants
        from electrum.plugins import run_hook
        from electrum.i18n import _
        from electrum.util import (format_time, format_satoshis, PrintError,
       t@@ -371,7 +372,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                    self.setGeometry(100, 100, 840, 400)
        
            def watching_only_changed(self):
       -        name = "Electrum Testnet" if NetworkConstants.TESTNET else "Electrum"
       +        name = "Electrum Testnet" if constants.net.TESTNET else "Electrum"
                title = '%s %s  -  %s' % (name, self.wallet.electrum_version,
                                                self.wallet.basename())
                extra = [self.wallet.storage.get('wallet_type', '?')]
 (DIR) diff --git a/gui/qt/network_dialog.py b/gui/qt/network_dialog.py
       t@@ -31,7 +31,7 @@ from PyQt5.QtWidgets import *
        import PyQt5.QtCore as QtCore
        
        from electrum.i18n import _
       -from electrum.bitcoin import NetworkConstants
       +from electrum import constants
        from electrum.util import print_error
        from electrum.network import serialize_server, deserialize_server
        
       t@@ -393,7 +393,7 @@ class NetworkChoiceLayout(object):
            def change_protocol(self, use_ssl):
                p = 's' if use_ssl else 't'
                host = self.server_host.text()
       -        pp = self.servers.get(host, NetworkConstants.DEFAULT_PORTS)
       +        pp = self.servers.get(host, constants.net.DEFAULT_PORTS)
                if p not in pp.keys():
                    p = list(pp.keys())[0]
                port = pp[p]
       t@@ -418,7 +418,7 @@ class NetworkChoiceLayout(object):
                    self.change_server(str(x.text(0)), self.protocol)
        
            def change_server(self, host, protocol):
       -        pp = self.servers.get(host, NetworkConstants.DEFAULT_PORTS)
       +        pp = self.servers.get(host, constants.net.DEFAULT_PORTS)
                if protocol and protocol not in protocol_letters:
                    protocol = None
                if protocol:
 (DIR) diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -36,75 +36,8 @@ from .util import bfh, bh2u, to_string
        from . import version
        from .util import print_error, InvalidPassword, assert_bytes, to_bytes, inv_dict
        from . import segwit_addr
       +from . import constants
        
       -def read_json(filename, default):
       -    path = os.path.join(os.path.dirname(__file__), filename)
       -    try:
       -        with open(path, 'r') as f:
       -            r = json.loads(f.read())
       -    except:
       -        r = default
       -    return r
       -
       -
       -class NetworkConstants:
       -
       -    @classmethod
       -    def set_mainnet(cls):
       -        cls.TESTNET = False
       -        cls.WIF_PREFIX = 0x80
       -        cls.ADDRTYPE_P2PKH = 0
       -        cls.ADDRTYPE_P2SH = 5
       -        cls.SEGWIT_HRP = "bc"
       -        cls.GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
       -        cls.DEFAULT_PORTS = {'t': '50001', 's': '50002'}
       -        cls.DEFAULT_SERVERS = read_json('servers.json', {})
       -        cls.CHECKPOINTS = read_json('checkpoints.json', [])
       -
       -        cls.XPRV_HEADERS = {
       -            'standard':    0x0488ade4,  # xprv
       -            'p2wpkh-p2sh': 0x049d7878,  # yprv
       -            'p2wsh-p2sh':  0x0295b005,  # Yprv
       -            'p2wpkh':      0x04b2430c,  # zprv
       -            'p2wsh':       0x02aa7a99,  # Zprv
       -        }
       -        cls.XPUB_HEADERS = {
       -            'standard':    0x0488b21e,  # xpub
       -            'p2wpkh-p2sh': 0x049d7cb2,  # ypub
       -            'p2wsh-p2sh':  0x0295b43f,  # Ypub
       -            'p2wpkh':      0x04b24746,  # zpub
       -            'p2wsh':       0x02aa7ed3,  # Zpub
       -        }
       -
       -    @classmethod
       -    def set_testnet(cls):
       -        cls.TESTNET = True
       -        cls.WIF_PREFIX = 0xef
       -        cls.ADDRTYPE_P2PKH = 111
       -        cls.ADDRTYPE_P2SH = 196
       -        cls.SEGWIT_HRP = "tb"
       -        cls.GENESIS = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
       -        cls.DEFAULT_PORTS = {'t':'51001', 's':'51002'}
       -        cls.DEFAULT_SERVERS = read_json('servers_testnet.json', {})
       -        cls.CHECKPOINTS = read_json('checkpoints_testnet.json', [])
       -
       -        cls.XPRV_HEADERS = {
       -            'standard':    0x04358394,  # tprv
       -            'p2wpkh-p2sh': 0x044a4e28,  # uprv
       -            'p2wsh-p2sh':  0x024285b5,  # Uprv
       -            'p2wpkh':      0x045f18bc,  # vprv
       -            'p2wsh':       0x02575048,  # Vprv
       -        }
       -        cls.XPUB_HEADERS = {
       -            'standard':    0x043587cf,  # tpub
       -            'p2wpkh-p2sh': 0x044a5262,  # upub
       -            'p2wsh-p2sh':  0x024285ef,  # Upub
       -            'p2wpkh':      0x045f1cf6,  # vpub
       -            'p2wsh':       0x02575483,  # Vpub
       -        }
       -
       -
       -NetworkConstants.set_mainnet()
        
        ################################## transactions
        
       t@@ -341,16 +274,16 @@ def b58_address_to_hash160(addr):
        
        
        def hash160_to_p2pkh(h160):
       -    return hash160_to_b58_address(h160, NetworkConstants.ADDRTYPE_P2PKH)
       +    return hash160_to_b58_address(h160, constants.net.ADDRTYPE_P2PKH)
        
        def hash160_to_p2sh(h160):
       -    return hash160_to_b58_address(h160, NetworkConstants.ADDRTYPE_P2SH)
       +    return hash160_to_b58_address(h160, constants.net.ADDRTYPE_P2SH)
        
        def public_key_to_p2pkh(public_key):
            return hash160_to_p2pkh(hash_160(public_key))
        
        def hash_to_segwit_addr(h):
       -    return segwit_addr.encode(NetworkConstants.SEGWIT_HRP, 0, h)
       +    return segwit_addr.encode(constants.net.SEGWIT_HRP, 0, h)
        
        def public_key_to_p2wpkh(public_key):
            return hash_to_segwit_addr(hash_160(public_key))
       t@@ -396,7 +329,7 @@ def script_to_address(script):
            return addr
        
        def address_to_script(addr):
       -    witver, witprog = segwit_addr.decode(NetworkConstants.SEGWIT_HRP, addr)
       +    witver, witprog = segwit_addr.decode(constants.net.SEGWIT_HRP, addr)
            if witprog is not None:
                assert (0 <= witver <= 16)
                OP_n = witver + 0x50 if witver > 0 else 0
       t@@ -404,11 +337,11 @@ def address_to_script(addr):
                script += push_script(bh2u(bytes(witprog)))
                return script
            addrtype, hash_160 = b58_address_to_hash160(addr)
       -    if addrtype == NetworkConstants.ADDRTYPE_P2PKH:
       +    if addrtype == constants.net.ADDRTYPE_P2PKH:
                script = '76a9'                                      # op_dup, op_hash_160
                script += push_script(bh2u(hash_160))
                script += '88ac'                                     # op_equalverify, op_checksig
       -    elif addrtype == NetworkConstants.ADDRTYPE_P2SH:
       +    elif addrtype == constants.net.ADDRTYPE_P2SH:
                script = 'a9'                                        # op_hash_160
                script += push_script(bh2u(hash_160))
                script += '87'                                       # op_equal
       t@@ -526,9 +459,9 @@ SCRIPT_TYPES = {
        
        def serialize_privkey(secret, compressed, txin_type, internal_use=False):
            if internal_use:
       -        prefix = bytes([(SCRIPT_TYPES[txin_type] + NetworkConstants.WIF_PREFIX) & 255])
       +        prefix = bytes([(SCRIPT_TYPES[txin_type] + constants.net.WIF_PREFIX) & 255])
            else:
       -        prefix = bytes([NetworkConstants.WIF_PREFIX])
       +        prefix = bytes([constants.net.WIF_PREFIX])
            suffix = b'\01' if compressed else b''
            vchIn = prefix + secret + suffix
            base58_wif = EncodeBase58Check(vchIn)
       t@@ -552,9 +485,9 @@ def deserialize_privkey(key):
        
            if txin_type is None:
                # keys exported in version 3.0.x encoded script type in first byte
       -        txin_type = inv_dict(SCRIPT_TYPES)[vch[0] - NetworkConstants.WIF_PREFIX]
       +        txin_type = inv_dict(SCRIPT_TYPES)[vch[0] - constants.net.WIF_PREFIX]
            else:
       -        assert vch[0] == NetworkConstants.WIF_PREFIX
       +        assert vch[0] == constants.net.WIF_PREFIX
        
            assert len(vch) in [33, 34]
            compressed = len(vch) == 34
       t@@ -590,7 +523,7 @@ def address_from_private_key(sec):
        
        def is_segwit_address(addr):
            try:
       -        witver, witprog = segwit_addr.decode(NetworkConstants.SEGWIT_HRP, addr)
       +        witver, witprog = segwit_addr.decode(constants.net.SEGWIT_HRP, addr)
            except Exception as e:
                return False
            return witprog is not None
       t@@ -600,7 +533,7 @@ def is_b58_address(addr):
                addrtype, h = b58_address_to_hash160(addr)
            except Exception as e:
                return False
       -    if addrtype not in [NetworkConstants.ADDRTYPE_P2PKH, NetworkConstants.ADDRTYPE_P2SH]:
       +    if addrtype not in [constants.net.ADDRTYPE_P2PKH, constants.net.ADDRTYPE_P2SH]:
                return False
            return addr == hash160_to_b58_address(h, addrtype)
        
       t@@ -912,25 +845,35 @@ def _CKD_pub(cK, c, s):
            return cK_n, c_n
        
        
       -def xprv_header(xtype):
       -    return bfh("%08x" % NetworkConstants.XPRV_HEADERS[xtype])
       +def xprv_header(xtype, *, net=None):
       +    if net is None:
       +        net = constants.net
       +    return bfh("%08x" % net.XPRV_HEADERS[xtype])
        
        
       -def xpub_header(xtype):
       -    return bfh("%08x" % NetworkConstants.XPUB_HEADERS[xtype])
       +def xpub_header(xtype, *, net=None):
       +    if net is None:
       +        net = constants.net
       +    return bfh("%08x" % net.XPUB_HEADERS[xtype])
        
        
       -def serialize_xprv(xtype, c, k, depth=0, fingerprint=b'\x00'*4, child_number=b'\x00'*4):
       -    xprv = xprv_header(xtype) + bytes([depth]) + fingerprint + child_number + c + bytes([0]) + k
       +def serialize_xprv(xtype, c, k, depth=0, fingerprint=b'\x00'*4,
       +                   child_number=b'\x00'*4, *, net=None):
       +    xprv = xprv_header(xtype, net=net) \
       +           + bytes([depth]) + fingerprint + child_number + c + bytes([0]) + k
            return EncodeBase58Check(xprv)
        
        
       -def serialize_xpub(xtype, c, cK, depth=0, fingerprint=b'\x00'*4, child_number=b'\x00'*4):
       -    xpub = xpub_header(xtype) + bytes([depth]) + fingerprint + child_number + c + cK
       +def serialize_xpub(xtype, c, cK, depth=0, fingerprint=b'\x00'*4,
       +                   child_number=b'\x00'*4, *, net=None):
       +    xpub = xpub_header(xtype, net=net) \
       +           + bytes([depth]) + fingerprint + child_number + c + cK
            return EncodeBase58Check(xpub)
        
        
       -def deserialize_xkey(xkey, prv):
       +def deserialize_xkey(xkey, prv, *, net=None):
       +    if net is None:
       +        net = constants.net
            xkey = DecodeBase58Check(xkey)
            if len(xkey) != 78:
                raise BaseException('Invalid length')
       t@@ -939,7 +882,7 @@ def deserialize_xkey(xkey, prv):
            child_number = xkey[9:13]
            c = xkey[13:13+32]
            header = int('0x' + bh2u(xkey[0:4]), 16)
       -    headers = NetworkConstants.XPRV_HEADERS if prv else NetworkConstants.XPUB_HEADERS
       +    headers = net.XPRV_HEADERS if prv else net.XPUB_HEADERS
            if header not in headers.values():
                raise BaseException('Invalid xpub format', hex(header))
            xtype = list(headers.keys())[list(headers.values()).index(header)]
       t@@ -948,11 +891,11 @@ def deserialize_xkey(xkey, prv):
            return xtype, depth, fingerprint, child_number, c, K_or_k
        
        
       -def deserialize_xpub(xkey):
       -    return deserialize_xkey(xkey, False)
       +def deserialize_xpub(xkey, *, net=None):
       +    return deserialize_xkey(xkey, False, net=net)
        
       -def deserialize_xprv(xkey):
       -    return deserialize_xkey(xkey, True)
       +def deserialize_xprv(xkey, *, net=None):
       +    return deserialize_xkey(xkey, True, net=net)
        
        def xpub_type(x):
            return deserialize_xpub(x)[0]
 (DIR) diff --git a/lib/blockchain.py b/lib/blockchain.py
       t@@ -25,6 +25,7 @@ import threading
        
        from . import util
        from . import bitcoin
       +from . import constants
        from .bitcoin import *
        
        MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
       t@@ -102,7 +103,7 @@ class Blockchain(util.PrintError):
                self.config = config
                self.catch_up = None # interface catching up
                self.checkpoint = checkpoint
       -        self.checkpoints = bitcoin.NetworkConstants.CHECKPOINTS
       +        self.checkpoints = constants.net.CHECKPOINTS
                self.parent_id = parent_id
                self.lock = threading.Lock()
                with self.lock:
       t@@ -152,7 +153,7 @@ class Blockchain(util.PrintError):
                _hash = hash_header(header)
                if prev_hash != header.get('prev_block_hash'):
                    raise BaseException("prev hash mismatch: %s vs %s" % (prev_hash, header.get('prev_block_hash')))
       -        if bitcoin.NetworkConstants.TESTNET:
       +        if constants.net.TESTNET:
                    return
                bits = self.target_to_bits(target)
                if bits != header.get('bits'):
       t@@ -262,7 +263,7 @@ class Blockchain(util.PrintError):
                if height == -1:
                    return '0000000000000000000000000000000000000000000000000000000000000000'
                elif height == 0:
       -            return bitcoin.NetworkConstants.GENESIS
       +            return constants.net.GENESIS
                elif height < len(self.checkpoints) * 2016:
                    assert (height+1) % 2016 == 0, height
                    index = height // 2016
       t@@ -273,7 +274,7 @@ class Blockchain(util.PrintError):
        
            def get_target(self, index):
                # compute target from chunk x, used in chunk x+1
       -        if bitcoin.NetworkConstants.TESTNET:
       +        if constants.net.TESTNET:
                    return 0
                if index == -1:
                    return MAX_TARGET
       t@@ -317,7 +318,7 @@ class Blockchain(util.PrintError):
                    #self.print_error("cannot connect at height", height)
                    return False
                if height == 0:
       -            return hash_header(header) == bitcoin.NetworkConstants.GENESIS
       +            return hash_header(header) == constants.net.GENESIS
                try:
                    prev_hash = self.get_hash(height - 1)
                except:
 (DIR) diff --git a/lib/constants.py b/lib/constants.py
       t@@ -0,0 +1,107 @@
       +# -*- coding: utf-8 -*-
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2018 The Electrum developers
       +#
       +# Permission is hereby granted, free of charge, to any person
       +# obtaining a copy of this software and associated documentation files
       +# (the "Software"), to deal in the Software without restriction,
       +# including without limitation the rights to use, copy, modify, merge,
       +# publish, distribute, sublicense, and/or sell copies of the Software,
       +# and to permit persons to whom the Software is furnished to do so,
       +# subject to the following conditions:
       +#
       +# The above copyright notice and this permission notice shall be
       +# included in all copies or substantial portions of the Software.
       +#
       +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
       +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
       +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +# SOFTWARE.
       +
       +import os
       +import json
       +
       +
       +def read_json(filename, default):
       +    path = os.path.join(os.path.dirname(__file__), filename)
       +    try:
       +        with open(path, 'r') as f:
       +            r = json.loads(f.read())
       +    except:
       +        r = default
       +    return r
       +
       +
       +class BitcoinMainnet:
       +
       +    TESTNET = False
       +    WIF_PREFIX = 0x80
       +    ADDRTYPE_P2PKH = 0
       +    ADDRTYPE_P2SH = 5
       +    SEGWIT_HRP = "bc"
       +    GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
       +    DEFAULT_PORTS = {'t': '50001', 's': '50002'}
       +    DEFAULT_SERVERS = read_json('servers.json', {})
       +    CHECKPOINTS = read_json('checkpoints.json', [])
       +
       +    XPRV_HEADERS = {
       +        'standard':    0x0488ade4,  # xprv
       +        'p2wpkh-p2sh': 0x049d7878,  # yprv
       +        'p2wsh-p2sh':  0x0295b005,  # Yprv
       +        'p2wpkh':      0x04b2430c,  # zprv
       +        'p2wsh':       0x02aa7a99,  # Zprv
       +    }
       +    XPUB_HEADERS = {
       +        'standard':    0x0488b21e,  # xpub
       +        'p2wpkh-p2sh': 0x049d7cb2,  # ypub
       +        'p2wsh-p2sh':  0x0295b43f,  # Ypub
       +        'p2wpkh':      0x04b24746,  # zpub
       +        'p2wsh':       0x02aa7ed3,  # Zpub
       +    }
       +
       +
       +class BitcoinTestnet:
       +
       +    TESTNET = True
       +    WIF_PREFIX = 0xef
       +    ADDRTYPE_P2PKH = 111
       +    ADDRTYPE_P2SH = 196
       +    SEGWIT_HRP = "tb"
       +    GENESIS = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
       +    DEFAULT_PORTS = {'t': '51001', 's': '51002'}
       +    DEFAULT_SERVERS = read_json('servers_testnet.json', {})
       +    CHECKPOINTS = read_json('checkpoints_testnet.json', [])
       +
       +    XPRV_HEADERS = {
       +        'standard':    0x04358394,  # tprv
       +        'p2wpkh-p2sh': 0x044a4e28,  # uprv
       +        'p2wsh-p2sh':  0x024285b5,  # Uprv
       +        'p2wpkh':      0x045f18bc,  # vprv
       +        'p2wsh':       0x02575048,  # Vprv
       +    }
       +    XPUB_HEADERS = {
       +        'standard':    0x043587cf,  # tpub
       +        'p2wpkh-p2sh': 0x044a5262,  # upub
       +        'p2wsh-p2sh':  0x024285ef,  # Upub
       +        'p2wpkh':      0x045f1cf6,  # vpub
       +        'p2wsh':       0x02575483,  # Vpub
       +    }
       +
       +
       +# don't import net directly, import the module instead (so that net is singleton)
       +net = BitcoinMainnet
       +
       +
       +def set_mainnet():
       +    global net
       +    net = BitcoinMainnet
       +
       +
       +def set_testnet():
       +    global net
       +    net = BitcoinTestnet
 (DIR) diff --git a/lib/keystore.py b/lib/keystore.py
       t@@ -28,7 +28,7 @@ from unicodedata import normalize
        
        from . import bitcoin
        from .bitcoin import *
       -
       +from . import constants
        from .util import PrintError, InvalidPassword, hfu
        from .mnemonic import Mnemonic, load_wordlist
        from .plugins import run_hook
       t@@ -688,7 +688,7 @@ is_bip32_key = lambda x: is_xprv(x) or is_xpub(x)
        
        
        def bip44_derivation(account_id, bip43_purpose=44):
       -    coin = 1 if bitcoin.NetworkConstants.TESTNET else 0
       +    coin = 1 if constants.net.TESTNET else 0
            return "m/%d'/%d'/%d'" % (bip43_purpose, coin, int(account_id))
        
        def from_seed(seed, passphrase, is_p2sh):
 (DIR) diff --git a/lib/network.py b/lib/network.py
       t@@ -37,6 +37,7 @@ import socks
        from . import util
        from . import bitcoin
        from .bitcoin import *
       +from . import constants
        from .interface import Connection, Interface
        from . import blockchain
        from .version import ELECTRUM_VERSION, PROTOCOL_VERSION
       t@@ -60,7 +61,7 @@ def parse_servers(result):
                    for v in item[2]:
                        if re.match("[st]\d*", v):
                            protocol, port = v[0], v[1:]
       -                    if port == '': port = bitcoin.NetworkConstants.DEFAULT_PORTS[protocol]
       +                    if port == '': port = constants.net.DEFAULT_PORTS[protocol]
                            out[protocol] = port
                        elif re.match("v(.?)+", v):
                            version = v[1:]
       t@@ -94,7 +95,7 @@ def filter_protocol(hostmap, protocol = 's'):
        
        def pick_random_server(hostmap = None, protocol = 's', exclude_set = set()):
            if hostmap is None:
       -        hostmap = bitcoin.NetworkConstants.DEFAULT_SERVERS
       +        hostmap = constants.net.DEFAULT_SERVERS
            eligible = list(set(filter_protocol(hostmap, protocol)) - exclude_set)
            return random.choice(eligible) if eligible else None
        
       t@@ -364,7 +365,7 @@ class Network(util.DaemonThread):
                return list(self.interfaces.keys())
        
            def get_servers(self):
       -        out = bitcoin.NetworkConstants.DEFAULT_SERVERS
       +        out = constants.net.DEFAULT_SERVERS
                if self.irc_servers:
                    out.update(filter_version(self.irc_servers.copy()))
                else:
       t@@ -967,7 +968,7 @@ class Network(util.DaemonThread):
            def init_headers_file(self):
                b = self.blockchains[0]
                filename = b.path()
       -        length = 80 * len(bitcoin.NetworkConstants.CHECKPOINTS) * 2016
       +        length = 80 * len(constants.net.CHECKPOINTS) * 2016
                if not os.path.exists(filename) or os.path.getsize(filename) < length:
                    with open(filename, 'wb') as f:
                        if length>0:
       t@@ -1092,4 +1093,4 @@ class Network(util.DaemonThread):
                    f.write(json.dumps(cp, indent=4))
        
            def max_checkpoint(self):
       -        return max(0, len(bitcoin.NetworkConstants.CHECKPOINTS) * 2016 - 1)
       +        return max(0, len(constants.net.CHECKPOINTS) * 2016 - 1)
 (DIR) diff --git a/lib/tests/test_bitcoin.py b/lib/tests/test_bitcoin.py
       t@@ -11,8 +11,9 @@ from lib.bitcoin import (
            var_int, op_push, address_to_script, regenerate_key,
            verify_message, deserialize_privkey, serialize_privkey, is_segwit_address,
            is_b58_address, address_to_scripthash, is_minikey, is_compressed, is_xpub,
       -    xpub_type, is_xprv, is_bip32_derivation, seed_type, NetworkConstants)
       +    xpub_type, is_xprv, is_bip32_derivation, seed_type)
        from lib.util import bfh
       +from lib import constants
        
        try:
            import ecdsa
       t@@ -168,12 +169,12 @@ class Test_bitcoin_testnet(unittest.TestCase):
            @classmethod
            def setUpClass(cls):
                super().setUpClass()
       -        NetworkConstants.set_testnet()
       +        constants.set_testnet()
        
            @classmethod
            def tearDownClass(cls):
                super().tearDownClass()
       -        NetworkConstants.set_mainnet()
       +        constants.set_mainnet()
        
            def test_address_to_script(self):
                # bech32 native segwit
 (DIR) diff --git a/lib/util.py b/lib/util.py
       t@@ -530,8 +530,8 @@ testnet_block_explorers = {
        }
        
        def block_explorer_info():
       -    from . import bitcoin
       -    return testnet_block_explorers if bitcoin.NetworkConstants.TESTNET else mainnet_block_explorers
       +    from . import constants
       +    return testnet_block_explorers if constants.net.TESTNET else mainnet_block_explorers
        
        def block_explorer(config):
            return config.get('block_explorer', 'Blocktrail.com')
 (DIR) diff --git a/plugins/digitalbitbox/digitalbitbox.py b/plugins/digitalbitbox/digitalbitbox.py
       t@@ -7,6 +7,7 @@ try:
            import electrum
            from electrum.bitcoin import TYPE_ADDRESS, push_script, var_int, msg_magic, Hash, verify_message, pubkey_from_signature, point_to_ser, public_key_to_p2pkh, EncodeAES, DecodeAES, MyVerifyingKey
            from electrum.bitcoin import serialize_xpub, deserialize_xpub
       +    from electrum import constants
            from electrum.transaction import Transaction
            from electrum.i18n import _
            from electrum.keystore import Hardware_KeyStore
       t@@ -92,10 +93,10 @@ class DigitalBitbox_Client():
                if reply:
                    xpub = reply['xpub']
                    # Change type of xpub to the requested type. The firmware
       -            # only ever returns the standard type, but it is agnostic
       +            # only ever returns the mainnet standard type, but it is agnostic
                    # to the type when signing.
       -            if xtype != 'standard':
       -                _, depth, fingerprint, child_number, c, cK = deserialize_xpub(xpub)
       +            if xtype != 'standard' or constants.net.TESTNET:
       +                _, depth, fingerprint, child_number, c, cK = deserialize_xpub(xpub, net=constants.BitcoinMainnet)
                        xpub = serialize_xpub(xtype, c, cK, depth, fingerprint, child_number)
                    return xpub
                else:
 (DIR) diff --git a/plugins/keepkey/plugin.py b/plugins/keepkey/plugin.py
       t@@ -4,8 +4,9 @@ from binascii import hexlify, unhexlify
        
        from electrum.util import bfh, bh2u
        from electrum.bitcoin import (b58_address_to_hash160, xpub_from_pubkey,
       -                              TYPE_ADDRESS, TYPE_SCRIPT, NetworkConstants,
       +                              TYPE_ADDRESS, TYPE_SCRIPT,
                                      is_segwit_address)
       +from electrum import constants
        from electrum.i18n import _
        from electrum.plugins import BasePlugin
        from electrum.transaction import deserialize
       t@@ -139,7 +140,7 @@ class KeepKeyCompatiblePlugin(HW_PluginBase):
                return client
        
            def get_coin_name(self):
       -        return "Testnet" if NetworkConstants.TESTNET else "Bitcoin"
       +        return "Testnet" if constants.net.TESTNET else "Bitcoin"
        
            def initialize_device(self, device_id, wizard, handler):
                # Initialization method
       t@@ -344,9 +345,9 @@ class KeepKeyCompatiblePlugin(HW_PluginBase):
                                txoutputtype.script_type = self.types.PAYTOWITNESS
                            else:
                                addrtype, hash_160 = b58_address_to_hash160(address)
       -                        if addrtype == NetworkConstants.ADDRTYPE_P2PKH:
       +                        if addrtype == constants.net.ADDRTYPE_P2PKH:
                                    txoutputtype.script_type = self.types.PAYTOADDRESS
       -                        elif addrtype == NetworkConstants.ADDRTYPE_P2SH:
       +                        elif addrtype == constants.net.ADDRTYPE_P2SH:
                                    txoutputtype.script_type = self.types.PAYTOSCRIPTHASH
                                else:
                                    raise BaseException('addrtype: ' + str(addrtype))
 (DIR) diff --git a/plugins/trezor/trezor.py b/plugins/trezor/trezor.py
       t@@ -4,7 +4,8 @@ from binascii import hexlify, unhexlify
        
        from electrum.util import bfh, bh2u, versiontuple
        from electrum.bitcoin import (b58_address_to_hash160, xpub_from_pubkey,
       -                              TYPE_ADDRESS, TYPE_SCRIPT, NetworkConstants)
       +                              TYPE_ADDRESS, TYPE_SCRIPT)
       +from electrum import constants
        from electrum.i18n import _
        from electrum.plugins import BasePlugin, Device
        from electrum.transaction import deserialize
       t@@ -173,7 +174,7 @@ class TrezorPlugin(HW_PluginBase):
                return client
        
            def get_coin_name(self):
       -        return "Testnet" if NetworkConstants.TESTNET else "Bitcoin"
       +        return "Testnet" if constants.net.TESTNET else "Bitcoin"
        
            def initialize_device(self, device_id, wizard, handler):
                # Initialization method
 (DIR) diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py
       t@@ -32,6 +32,7 @@ from urllib.parse import quote
        
        import electrum
        from electrum import bitcoin
       +from electrum import constants
        from electrum import keystore
        from electrum.bitcoin import *
        from electrum.mnemonic import Mnemonic
       t@@ -44,13 +45,13 @@ from electrum.storage import STO_EV_USER_PW
        
        # signing_xpub is hardcoded so that the wallet can be restored from seed, without TrustedCoin's server
        def get_signing_xpub():
       -    if NetworkConstants.TESTNET:
       +    if constants.net.TESTNET:
                return "tpubD6NzVbkrYhZ4XdmyJQcCPjQfg6RXVUzGFhPjZ7uvRC8JLcS7Hw1i7UTpyhp9grHpak4TyK2hzBJrujDVLXQ6qB5tNpVx9rC6ixijUXadnmY"
            else:
                return "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL"
        
        def get_billing_xpub():
       -    if NetworkConstants.TESTNET:
       +    if constants.net.TESTNET:
                return "tpubD6NzVbkrYhZ4X11EJFTJujsYbUmVASAYY7gXsEt4sL97AMBdypiH1E9ZVTpdXXEy3Kj9Eqd1UkxdGtvDt5z23DKsh6211CfNJo8bLLyem5r"
            else:
                return "xpub6DTBdtBB8qUmH5c77v8qVGVoYk7WjJNpGvutqjLasNG1mbux6KsojaLrYf2sRhXAVU4NaFuHhbD9SvVPRt1MB1MaMooRuhHcAZH1yhQ1qDU"