From 81b6d65764a718be99b4f8e309a644ac1dc78ed3 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Sun, 4 Mar 2018 22:10:59 +0100 Subject: [PATCH] refactor network constants --- electrum | 3 +- gui/kivy/main_window.py | 4 +- gui/qt/exception_window.py | 4 +- gui/qt/main_window.py | 5 +- gui/qt/network_dialog.py | 6 +- lib/bitcoin.py | 131 +++++++------------------ lib/blockchain.py | 11 ++- lib/constants.py | 107 ++++++++++++++++++++ lib/keystore.py | 4 +- lib/network.py | 11 ++- lib/tests/test_bitcoin.py | 7 +- lib/util.py | 4 +- plugins/digitalbitbox/digitalbitbox.py | 7 +- plugins/keepkey/plugin.py | 9 +- plugins/trezor/trezor.py | 5 +- plugins/trustedcoin/trustedcoin.py | 5 +- 16 files changed, 191 insertions(+), 132 deletions(-) create mode 100644 lib/constants.py diff --git a/electrum b/electrum index 4a0470b23..78924631e 100755 --- a/electrum +++ b/electrum @@ -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 @@ -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']: diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py index bd23d2ae9..9261e8b5e 100644 --- a/gui/kivy/main_window.py +++ b/gui/kivy/main_window.py @@ -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 diff --git a/gui/qt/exception_window.py b/gui/qt/exception_window.py index 743a98351..fa2912834 100644 --- a/gui/qt/exception_window.py +++ b/gui/qt/exception_window.py @@ -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 = """

Traceback

@@ -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
diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
index ababe1f16..f46d4ba71 100644
--- a/gui/qt/main_window.py
+++ b/gui/qt/main_window.py
@@ -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,
@@ -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', '?')]
diff --git a/gui/qt/network_dialog.py b/gui/qt/network_dialog.py
index f1c578e36..e578bb43a 100644
--- a/gui/qt/network_dialog.py
+++ b/gui/qt/network_dialog.py
@@ -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
 
@@ -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]
@@ -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:
diff --git a/lib/bitcoin.py b/lib/bitcoin.py
index 2926cb732..18d2afd14 100644
--- a/lib/bitcoin.py
+++ b/lib/bitcoin.py
@@ -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
 
@@ -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))
@@ -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
@@ -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
@@ -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)
@@ -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
@@ -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
@@ -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)
 
@@ -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')
@@ -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)]
@@ -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]
diff --git a/lib/blockchain.py b/lib/blockchain.py
index d592e584b..f6fe31dcf 100644
--- a/lib/blockchain.py
+++ b/lib/blockchain.py
@@ -25,6 +25,7 @@ import threading
 
 from . import util
 from . import bitcoin
+from . import constants
 from .bitcoin import *
 
 MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
@@ -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:
@@ -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'):
@@ -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
@@ -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
@@ -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:
diff --git a/lib/constants.py b/lib/constants.py
new file mode 100644
index 000000000..ec35cbe3e
--- /dev/null
+++ b/lib/constants.py
@@ -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
diff --git a/lib/keystore.py b/lib/keystore.py
index 011602ec4..676db247d 100644
--- a/lib/keystore.py
+++ b/lib/keystore.py
@@ -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
@@ -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):
diff --git a/lib/network.py b/lib/network.py
index 7d00d9c64..b803a7820 100644
--- a/lib/network.py
+++ b/lib/network.py
@@ -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
@@ -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:]
@@ -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
 
@@ -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:
@@ -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:
@@ -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)
diff --git a/lib/tests/test_bitcoin.py b/lib/tests/test_bitcoin.py
index 03f1d00f0..a5f734357 100644
--- a/lib/tests/test_bitcoin.py
+++ b/lib/tests/test_bitcoin.py
@@ -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
@@ -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
diff --git a/lib/util.py b/lib/util.py
index 68d127f85..1714c78d4 100644
--- a/lib/util.py
+++ b/lib/util.py
@@ -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')
diff --git a/plugins/digitalbitbox/digitalbitbox.py b/plugins/digitalbitbox/digitalbitbox.py
index 2f63fda4d..623290543 100644
--- a/plugins/digitalbitbox/digitalbitbox.py
+++ b/plugins/digitalbitbox/digitalbitbox.py
@@ -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
@@ -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:
diff --git a/plugins/keepkey/plugin.py b/plugins/keepkey/plugin.py
index acbad49c0..a81a328bd 100644
--- a/plugins/keepkey/plugin.py
+++ b/plugins/keepkey/plugin.py
@@ -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
@@ -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
@@ -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))
diff --git a/plugins/trezor/trezor.py b/plugins/trezor/trezor.py
index 512168c18..322c2da31 100644
--- a/plugins/trezor/trezor.py
+++ b/plugins/trezor/trezor.py
@@ -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
@@ -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
diff --git a/plugins/trustedcoin/trustedcoin.py b/plugins/trustedcoin/trustedcoin.py
index a6a008c45..b8e832e66 100644
--- a/plugins/trustedcoin/trustedcoin.py
+++ b/plugins/trustedcoin/trustedcoin.py
@@ -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
@@ -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"