electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 63a1db117282eb5526f5ef55b4bc720ae820fdde
parent bd16e20a4d5705b8b957a9af8aa9ec83286b20af
Author: ThomasV <thomasv@electrum.org>
Date:   Mon,  4 Sep 2017 11:04:59 +0200

make xpub/xprv version information user-visible

Diffstat:
Mlib/bitcoin.py | 36+++++++++++++++++++++++++-----------
Mlib/keystore.py | 7+------
Mlib/wallet.py | 25++++++++++++++++++-------
Mplugins/ledger/ledger.py | 11+++--------
Mplugins/trezor/clientbase.py | 3++-
5 files changed, 49 insertions(+), 33 deletions(-)

diff --git a/lib/bitcoin.py b/lib/bitcoin.py @@ -47,14 +47,30 @@ def read_json_dict(filename): return r +# Version numbers for BIP32 extended keys +# standard: xprv, xpub +# segwit in p2sh: yprv, ypub +# native segwit: zprv, zpub +XPRV_HEADERS = { + 'standard': 0x0488ade4, + 'segwit_p2sh': 0x049d7878, + 'segwit': 0x4b2430c +} +XPUB_HEADERS = { + 'standard': 0x0488b21e, + 'segwit_p2sh': 0x049d7cb2, + 'segwit': 0x4b24746 +} + + # Bitcoin network constants TESTNET = False NOLNET = False ADDRTYPE_P2PKH = 0 ADDRTYPE_P2SH = 5 SEGWIT_HRP = "bc" -XPRV_HEADER = 0x0488ade4 -XPUB_HEADER = 0x0488b21e + + HEADERS_URL = "https://headers.electrum.org/blockchain_headers" GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" SERVERLIST = 'servers.json' @@ -63,7 +79,6 @@ DEFAULT_SERVERS = read_json_dict('servers.json') def set_testnet(): global ADDRTYPE_P2PKH, ADDRTYPE_P2SH - global XPRV_HEADER, XPUB_HEADER global TESTNET, HEADERS_URL global GENESIS global SEGWIT_HRP @@ -72,8 +87,6 @@ def set_testnet(): ADDRTYPE_P2PKH = 111 ADDRTYPE_P2SH = 196 SEGWIT_HRP = "tb" - XPRV_HEADER = 0x04358394 - XPUB_HEADER = 0x043587cf HEADERS_URL = "https://headers.electrum.org/testnet_headers" GENESIS = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943" SERVERLIST = 'servers_testnet.json' @@ -816,11 +829,11 @@ def _CKD_pub(cK, c, s): def xprv_header(xtype): - return bfh("%08x" % (XPRV_HEADER + xtype)) + return bfh("%08x" % XPRV_HEADERS[xtype]) def xpub_header(xtype): - return bfh("%08x" % (XPUB_HEADER + xtype)) + return bfh("%08x" % XPUB_HEADERS[xtype]) def serialize_xprv(xtype, c, k, depth=0, fingerprint=b'\x00'*4, child_number=b'\x00'*4): @@ -841,10 +854,11 @@ def deserialize_xkey(xkey, prv): fingerprint = xkey[5:9] child_number = xkey[9:13] c = xkey[13:13+32] - header = XPRV_HEADER if prv else XPUB_HEADER - xtype = int('0x' + bh2u(xkey[0:4]), 16) - header - if xtype not in [0, 1]: - raise BaseException('Invalid header') + header = int('0x' + bh2u(xkey[0:4]), 16) + headers = XPRV_HEADERS if prv else XPUB_HEADERS + if header not in headers.values(): + raise BaseException('Invalid xpub format', hex(header)) + xtype = list(headers.keys())[list(headers.values()).index(header)] n = 33 if prv else 32 K_or_k = xkey[13+n:] return xtype, depth, fingerprint, child_number, c, K_or_k diff --git a/lib/keystore.py b/lib/keystore.py @@ -76,8 +76,6 @@ class KeyStore(PrintError): return False return bool(self.get_tx_derivations(tx)) - def is_segwit(self): - return False class Software_KeyStore(KeyStore): @@ -335,8 +333,6 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub): pk = bip32_private_key(sequence, k, c) return pk - def is_segwit(self): - return bool(deserialize_xpub(self.xpub)[0]) class Old_KeyStore(Deterministic_KeyStore): @@ -699,8 +695,7 @@ def from_seed(seed, passphrase): keystore.add_seed(seed) keystore.passphrase = passphrase bip32_seed = Mnemonic.mnemonic_to_seed(seed, passphrase) - xtype = 0 if t == 'standard' else 1 - keystore.add_xprv_from_seed(bip32_seed, xtype, "m/") + keystore.add_xprv_from_seed(bip32_seed, t, "m/") else: raise BaseException(t) return keystore diff --git a/lib/wallet.py b/lib/wallet.py @@ -1544,8 +1544,15 @@ class Simple_Wallet(Abstract_Wallet): def load_keystore(self): self.keystore = load_keystore(self.storage, 'keystore') - self.is_segwit = self.keystore.is_segwit() - self.txin_type = 'p2wpkh' if self.is_segwit else 'p2pkh' + xtype = deserialize_xpub(self.keystore.xpub)[0] + if xtype == 'standard': + self.txin_type = 'p2pkh' + elif xtype == 'segwit': + self.txin_type = 'p2wpkh' + elif xtype == 'segwit_p2sh': + self.txin_type = 'p2wpkh-p2sh' + else: + raise BaseException('unknown txin_type', xtype) def get_pubkey(self, c, i): return self.derive_pubkeys(c, i) @@ -1640,8 +1647,7 @@ class Standard_Wallet(Simple_Deterministic_Wallet): wallet_type = 'standard' def pubkeys_to_redeem_script(self, pubkey): - if self.is_segwit: - return transaction.segwit_script(pubkey) + return transaction.segwit_script(pubkey) def pubkeys_to_address(self, pubkey): if self.txin_type == 'p2pkh': @@ -1652,7 +1658,7 @@ class Standard_Wallet(Simple_Deterministic_Wallet): redeem_script = self.pubkeys_to_redeem_script(pubkey) return bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script))) else: - raise NotImplementedError() + raise NotImplementedError(self.txin_type) class Multisig_Wallet(Deterministic_Wallet): @@ -1693,8 +1699,13 @@ class Multisig_Wallet(Deterministic_Wallet): name = 'x%d/'%(i+1) self.keystores[name] = load_keystore(self.storage, name) self.keystore = self.keystores['x1/'] - self.is_segwit = self.keystore.is_segwit() - self.txin_type = 'p2wsh' if self.is_segwit else 'p2sh' + xtype = deserialize_xpub(self.keystore.xpub)[0] + if xtype == 'standard': + self.txin_type = 'p2sh' + elif xtype == 'segwit': + self.txin_type = 'p2wsh' + elif xtype == 'segwit_p2sh': + self.txin_type = 'p2wsh-p2sh' def save_keystore(self): for name, k in self.keystores.items(): diff --git a/plugins/ledger/ledger.py b/plugins/ledger/ledger.py @@ -59,6 +59,7 @@ class Ledger_Client(): #self.get_client() # prompt for the PIN before displaying the dialog if necessary #self.handler.show_message("Computing master public key") try: + xtype = 'segwit_p2sh' if bip32_path.startswith("m/49'/") else 'standard' splitPath = bip32_path.split('/') if splitPath[0] == 'm': splitPath = splitPath[1:] @@ -75,11 +76,8 @@ class Ledger_Client(): publicKey = compress_public_key(nodeData['publicKey']) depth = len(splitPath) lastChild = splitPath[len(splitPath) - 1].split('\'') - if len(lastChild) == 1: - childnum = int(lastChild[0]) - else: - childnum = 0x80000000 | int(lastChild[0]) - xpub = bitcoin.serialize_xpub(0, nodeData['chainCode'], publicKey, depth, self.i4b(fingerprint), self.i4b(childnum)) + childnum = int(lastChild[0]) if len(lastChild) == 1 else 0x80000000 | int(lastChild[0]) + xpub = bitcoin.serialize_xpub(xtype, nodeData['chainCode'], publicKey, depth, self.i4b(fingerprint), self.i4b(childnum)) return xpub except Exception as e: traceback.print_exc(file=sys.stdout) @@ -173,9 +171,6 @@ class Ledger_KeyStore(Hardware_KeyStore): self.signing = False self.cfg = d.get('cfg', {'mode':0,'pair':''}) - def is_segwit(self): - return self.derivation.startswith("m/49'/") - def dump(self): obj = Hardware_KeyStore.dump(self) obj['cfg'] = self.cfg diff --git a/plugins/trezor/clientbase.py b/plugins/trezor/clientbase.py @@ -151,7 +151,8 @@ class TrezorClientBase(GuiMixin, PrintError): address_n = self.expand_path(bip32_path) creating = False #self.next_account_number() == 0 node = self.get_public_node(address_n, creating).node - return serialize_xpub(0, node.chain_code, node.public_key, node.depth, self.i4b(node.fingerprint), self.i4b(node.child_num)) + xtype = 'segwit_p2sh' if bip32_path.startswith("m/49'/") else 'standard' + return serialize_xpub(xtype, node.chain_code, node.public_key, node.depth, self.i4b(node.fingerprint), self.i4b(node.child_num)) #def address_from_derivation(self, derivation): # return self.get_address('Bitcoin', self.expand_path(derivation))