Browse Source

split bip32 from bitcoin.py

3.3.3.1
SomberNight 7 years ago
parent
commit
a88a2dea82
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 4
      electrum/base_wizard.py
  2. 269
      electrum/bip32.py
  3. 276
      electrum/bitcoin.py
  4. 29
      electrum/keystore.py
  5. 7
      electrum/paymentrequest.py
  6. 4
      electrum/plugin.py
  7. 14
      electrum/plugins/coldcard/coldcard.py
  8. 8
      electrum/plugins/cosigner_pool/qt.py
  9. 5
      electrum/plugins/digitalbitbox/digitalbitbox.py
  10. 2
      electrum/plugins/keepkey/clientbase.py
  11. 6
      electrum/plugins/keepkey/keepkey.py
  12. 7
      electrum/plugins/keepkey/qt.py
  13. 10
      electrum/plugins/ledger/ledger.py
  14. 2
      electrum/plugins/safe_t/clientbase.py
  15. 7
      electrum/plugins/safe_t/qt.py
  16. 6
      electrum/plugins/safe_t/safe_t.py
  17. 2
      electrum/plugins/trezor/clientbase.py
  18. 7
      electrum/plugins/trezor/qt.py
  19. 6
      electrum/plugins/trezor/trezor.py
  20. 25
      electrum/plugins/trustedcoin/trustedcoin.py
  21. 21
      electrum/tests/test_bitcoin.py
  22. 31
      electrum/transaction.py
  23. 3
      electrum/verifier.py
  24. 16
      electrum/wallet.py

4
electrum/base_wizard.py

@ -30,6 +30,7 @@ from functools import partial
from . import bitcoin from . import bitcoin
from . import keystore from . import keystore
from .bip32 import is_bip32_derivation, xpub_type
from .keystore import bip44_derivation, purpose48_derivation from .keystore import bip44_derivation, purpose48_derivation
from .wallet import (Imported_Wallet, Standard_Wallet, Multisig_Wallet, from .wallet import (Imported_Wallet, Standard_Wallet, Multisig_Wallet,
wallet_types, Wallet, Abstract_Wallet) wallet_types, Wallet, Abstract_Wallet)
@ -339,7 +340,7 @@ class BaseWizard(object):
try: try:
self.choice_and_line_dialog( self.choice_and_line_dialog(
run_next=f, title=_('Script type and Derivation path'), message1=message1, run_next=f, title=_('Script type and Derivation path'), message1=message1,
message2=message2, choices=choices, test_text=bitcoin.is_bip32_derivation) message2=message2, choices=choices, test_text=is_bip32_derivation)
return return
except ScriptTypeNotSupported as e: except ScriptTypeNotSupported as e:
self.show_error(e) self.show_error(e)
@ -419,7 +420,6 @@ class BaseWizard(object):
def on_keystore(self, k): def on_keystore(self, k):
has_xpub = isinstance(k, keystore.Xpub) has_xpub = isinstance(k, keystore.Xpub)
if has_xpub: if has_xpub:
from .bitcoin import xpub_type
t1 = xpub_type(k.xpub) t1 = xpub_type(k.xpub)
if self.wallet_type == 'standard': if self.wallet_type == 'standard':
if has_xpub and t1 not in ['standard', 'p2wpkh', 'p2wpkh-p2sh']: if has_xpub and t1 not in ['standard', 'p2wpkh', 'p2wpkh-p2sh']:

269
electrum/bip32.py

@ -0,0 +1,269 @@
# Copyright (C) 2018 The Electrum developers
# Distributed under the MIT software license, see the accompanying
# file LICENCE or http://www.opensource.org/licenses/mit-license.php
import hashlib
from typing import List
from .util import bfh, bh2u, BitcoinException, print_error
from . import constants
from . import ecc
from .crypto import hash_160, hmac_oneshot
from .bitcoin import rev_hex, int_to_hex, EncodeBase58Check, DecodeBase58Check
BIP32_PRIME = 0x80000000
def protect_against_invalid_ecpoint(func):
def func_wrapper(*args):
n = args[-1]
while True:
is_prime = n & BIP32_PRIME
try:
return func(*args[:-1], n=n)
except ecc.InvalidECPointException:
print_error('bip32 protect_against_invalid_ecpoint: skipping index')
n += 1
is_prime2 = n & BIP32_PRIME
if is_prime != is_prime2: raise OverflowError()
return func_wrapper
# Child private key derivation function (from master private key)
# k = master private key (32 bytes)
# c = master chain code (extra entropy for key derivation) (32 bytes)
# n = the index of the key we want to derive. (only 32 bits will be used)
# If n is hardened (i.e. the 32nd bit is set), the resulting private key's
# corresponding public key can NOT be determined without the master private key.
# However, if n is not hardened, the resulting private key's corresponding
# public key can be determined without the master private key.
@protect_against_invalid_ecpoint
def CKD_priv(k, c, n):
if n < 0: raise ValueError('the bip32 index needs to be non-negative')
is_prime = n & BIP32_PRIME
return _CKD_priv(k, c, bfh(rev_hex(int_to_hex(n,4))), is_prime)
def _CKD_priv(k, c, s, is_prime):
try:
keypair = ecc.ECPrivkey(k)
except ecc.InvalidECPointException as e:
raise BitcoinException('Impossible xprv (not within curve order)') from e
cK = keypair.get_public_key_bytes(compressed=True)
data = bytes([0]) + k + s if is_prime else cK + s
I = hmac_oneshot(c, data, hashlib.sha512)
I_left = ecc.string_to_number(I[0:32])
k_n = (I_left + ecc.string_to_number(k)) % ecc.CURVE_ORDER
if I_left >= ecc.CURVE_ORDER or k_n == 0:
raise ecc.InvalidECPointException()
k_n = ecc.number_to_string(k_n, ecc.CURVE_ORDER)
c_n = I[32:]
return k_n, c_n
# Child public key derivation function (from public key only)
# K = master public key
# c = master chain code
# n = index of key we want to derive
# This function allows us to find the nth public key, as long as n is
# not hardened. If n is hardened, we need the master private key to find it.
@protect_against_invalid_ecpoint
def CKD_pub(cK, c, n):
if n < 0: raise ValueError('the bip32 index needs to be non-negative')
if n & BIP32_PRIME: raise Exception()
return _CKD_pub(cK, c, bfh(rev_hex(int_to_hex(n,4))))
# helper function, callable with arbitrary string.
# note: 's' does not need to fit into 32 bits here! (c.f. trustedcoin billing)
def _CKD_pub(cK, c, s):
I = hmac_oneshot(c, cK + s, hashlib.sha512)
pubkey = ecc.ECPrivkey(I[0:32]) + ecc.ECPubkey(cK)
if pubkey.is_at_infinity():
raise ecc.InvalidECPointException()
cK_n = pubkey.get_public_key_bytes(compressed=True)
c_n = I[32:]
return cK_n, c_n
def xprv_header(xtype, *, net=None):
if net is None:
net = constants.net
return bfh("%08x" % net.XPRV_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, *, net=None):
if not ecc.is_secret_within_curve_range(k):
raise BitcoinException('Impossible xprv (not within curve order)')
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, *, net=None):
xpub = xpub_header(xtype, net=net) \
+ bytes([depth]) + fingerprint + child_number + c + cK
return EncodeBase58Check(xpub)
class InvalidMasterKeyVersionBytes(BitcoinException): pass
def deserialize_xkey(xkey, prv, *, net=None):
if net is None:
net = constants.net
xkey = DecodeBase58Check(xkey)
if len(xkey) != 78:
raise BitcoinException('Invalid length for extended key: {}'
.format(len(xkey)))
depth = xkey[4]
fingerprint = xkey[5:9]
child_number = xkey[9:13]
c = xkey[13:13+32]
header = int('0x' + bh2u(xkey[0:4]), 16)
headers = net.XPRV_HEADERS if prv else net.XPUB_HEADERS
if header not in headers.values():
raise InvalidMasterKeyVersionBytes('Invalid extended key format: {}'
.format(hex(header)))
xtype = list(headers.keys())[list(headers.values()).index(header)]
n = 33 if prv else 32
K_or_k = xkey[13+n:]
if prv and not ecc.is_secret_within_curve_range(K_or_k):
raise BitcoinException('Impossible xprv (not within curve order)')
return xtype, depth, fingerprint, child_number, c, K_or_k
def deserialize_xpub(xkey, *, net=None):
return deserialize_xkey(xkey, False, net=net)
def deserialize_xprv(xkey, *, net=None):
return deserialize_xkey(xkey, True, net=net)
def xpub_type(x):
return deserialize_xpub(x)[0]
def is_xpub(text):
try:
deserialize_xpub(text)
return True
except:
return False
def is_xprv(text):
try:
deserialize_xprv(text)
return True
except:
return False
def xpub_from_xprv(xprv):
xtype, depth, fingerprint, child_number, c, k = deserialize_xprv(xprv)
cK = ecc.ECPrivkey(k).get_public_key_bytes(compressed=True)
return serialize_xpub(xtype, c, cK, depth, fingerprint, child_number)
def bip32_root(seed, xtype):
I = hmac_oneshot(b"Bitcoin seed", seed, hashlib.sha512)
master_k = I[0:32]
master_c = I[32:]
# create xprv first, as that will check if master_k is within curve order
xprv = serialize_xprv(xtype, master_c, master_k)
cK = ecc.ECPrivkey(master_k).get_public_key_bytes(compressed=True)
xpub = serialize_xpub(xtype, master_c, cK)
return xprv, xpub
def xpub_from_pubkey(xtype, cK):
if cK[0] not in (0x02, 0x03):
raise ValueError('Unexpected first byte: {}'.format(cK[0]))
return serialize_xpub(xtype, b'\x00'*32, cK)
def bip32_derivation(s):
if not s.startswith('m/'):
raise ValueError('invalid bip32 derivation path: {}'.format(s))
s = s[2:]
for n in s.split('/'):
if n == '': continue
i = int(n[:-1]) + BIP32_PRIME if n[-1] == "'" else int(n)
yield i
def convert_bip32_path_to_list_of_uint32(n: str) -> List[int]:
"""Convert bip32 path to list of uint32 integers with prime flags
m/0/-1/1' -> [0, 0x80000001, 0x80000001]
based on code in trezorlib
"""
path = []
for x in n.split('/')[1:]:
if x == '': continue
prime = 0
if x.endswith("'"):
x = x.replace('\'', '')
prime = BIP32_PRIME
if x.startswith('-'):
prime = BIP32_PRIME
path.append(abs(int(x)) | prime)
return path
def is_bip32_derivation(x):
try:
[ i for i in bip32_derivation(x)]
return True
except :
return False
def bip32_private_derivation(xprv, branch, sequence):
if not sequence.startswith(branch):
raise ValueError('incompatible branch ({}) and sequence ({})'
.format(branch, sequence))
if branch == sequence:
return xprv, xpub_from_xprv(xprv)
xtype, depth, fingerprint, child_number, c, k = deserialize_xprv(xprv)
sequence = sequence[len(branch):]
for n in sequence.split('/'):
if n == '': continue
i = int(n[:-1]) + BIP32_PRIME if n[-1] == "'" else int(n)
parent_k = k
k, c = CKD_priv(k, c, i)
depth += 1
parent_cK = ecc.ECPrivkey(parent_k).get_public_key_bytes(compressed=True)
fingerprint = hash_160(parent_cK)[0:4]
child_number = bfh("%08X"%i)
cK = ecc.ECPrivkey(k).get_public_key_bytes(compressed=True)
xpub = serialize_xpub(xtype, c, cK, depth, fingerprint, child_number)
xprv = serialize_xprv(xtype, c, k, depth, fingerprint, child_number)
return xprv, xpub
def bip32_public_derivation(xpub, branch, sequence):
xtype, depth, fingerprint, child_number, c, cK = deserialize_xpub(xpub)
if not sequence.startswith(branch):
raise ValueError('incompatible branch ({}) and sequence ({})'
.format(branch, sequence))
sequence = sequence[len(branch):]
for n in sequence.split('/'):
if n == '': continue
i = int(n)
parent_cK = cK
cK, c = CKD_pub(cK, c, i)
depth += 1
fingerprint = hash_160(parent_cK)[0:4]
child_number = bfh("%08X"%i)
return serialize_xpub(xtype, c, cK, depth, fingerprint, child_number)
def bip32_private_key(sequence, k, chain):
for i in sequence:
k, chain = CKD_priv(k, chain, i)
return k

276
electrum/bitcoin.py

@ -26,7 +26,7 @@
import hashlib import hashlib
from typing import List, Tuple from typing import List, Tuple
from .util import bfh, bh2u, BitcoinException, print_error, assert_bytes, to_bytes, inv_dict from .util import bfh, bh2u, BitcoinException, assert_bytes, to_bytes, inv_dict
from . import version from . import version
from . import segwit_addr from . import segwit_addr
from . import constants from . import constants
@ -152,6 +152,9 @@ hash_decode = lambda x: bfh(x)[::-1]
hmac_sha_512 = lambda x, y: hmac_oneshot(x, y, hashlib.sha512) hmac_sha_512 = lambda x, y: hmac_oneshot(x, y, hashlib.sha512)
################################## electrum seeds
def is_new_seed(x, prefix=version.SEED_PREFIX): def is_new_seed(x, prefix=version.SEED_PREFIX):
from . import mnemonic from . import mnemonic
x = mnemonic.normalize_text(x) x = mnemonic.normalize_text(x)
@ -277,14 +280,14 @@ def address_to_script(addr, *, net=None):
script = bh2u(bytes([OP_n])) script = bh2u(bytes([OP_n]))
script += push_script(bh2u(bytes(witprog))) script += push_script(bh2u(bytes(witprog)))
return script return script
addrtype, hash_160 = b58_address_to_hash160(addr) addrtype, hash_160_ = b58_address_to_hash160(addr)
if addrtype == net.ADDRTYPE_P2PKH: if addrtype == net.ADDRTYPE_P2PKH:
script = '76a9' # op_dup, op_hash_160 script = '76a9' # op_dup, op_hash_160
script += push_script(bh2u(hash_160)) script += push_script(bh2u(hash_160_))
script += '88ac' # op_equalverify, op_checksig script += '88ac' # op_equalverify, op_checksig
elif addrtype == net.ADDRTYPE_P2SH: elif addrtype == net.ADDRTYPE_P2SH:
script = 'a9' # op_hash_160 script = 'a9' # op_hash_160
script += push_script(bh2u(hash_160)) script += push_script(bh2u(hash_160_))
script += '87' # op_equal script += '87' # op_equal
else: else:
raise BitcoinException('unknown address type: {}'.format(addrtype)) raise BitcoinException('unknown address type: {}'.format(addrtype))
@ -409,12 +412,6 @@ WIF_SCRIPT_TYPES = {
WIF_SCRIPT_TYPES_INV = inv_dict(WIF_SCRIPT_TYPES) WIF_SCRIPT_TYPES_INV = inv_dict(WIF_SCRIPT_TYPES)
PURPOSE48_SCRIPT_TYPES = {
'p2wsh-p2sh': 1, # specifically multisig
'p2wsh': 2, # specifically multisig
}
PURPOSE48_SCRIPT_TYPES_INV = inv_dict(PURPOSE48_SCRIPT_TYPES)
def serialize_privkey(secret: bytes, compressed: bool, txin_type: str, def serialize_privkey(secret: bytes, compressed: bool, txin_type: str,
internal_use: bool=False) -> str: internal_use: bool=False) -> str:
@ -521,262 +518,3 @@ def is_minikey(text):
def minikey_to_private_key(text): def minikey_to_private_key(text):
return sha256(text) return sha256(text)
###################################### BIP32 ##############################
BIP32_PRIME = 0x80000000
def protect_against_invalid_ecpoint(func):
def func_wrapper(*args):
n = args[-1]
while True:
is_prime = n & BIP32_PRIME
try:
return func(*args[:-1], n=n)
except ecc.InvalidECPointException:
print_error('bip32 protect_against_invalid_ecpoint: skipping index')
n += 1
is_prime2 = n & BIP32_PRIME
if is_prime != is_prime2: raise OverflowError()
return func_wrapper
# Child private key derivation function (from master private key)
# k = master private key (32 bytes)
# c = master chain code (extra entropy for key derivation) (32 bytes)
# n = the index of the key we want to derive. (only 32 bits will be used)
# If n is hardened (i.e. the 32nd bit is set), the resulting private key's
# corresponding public key can NOT be determined without the master private key.
# However, if n is not hardened, the resulting private key's corresponding
# public key can be determined without the master private key.
@protect_against_invalid_ecpoint
def CKD_priv(k, c, n):
if n < 0: raise ValueError('the bip32 index needs to be non-negative')
is_prime = n & BIP32_PRIME
return _CKD_priv(k, c, bfh(rev_hex(int_to_hex(n,4))), is_prime)
def _CKD_priv(k, c, s, is_prime):
try:
keypair = ecc.ECPrivkey(k)
except ecc.InvalidECPointException as e:
raise BitcoinException('Impossible xprv (not within curve order)') from e
cK = keypair.get_public_key_bytes(compressed=True)
data = bytes([0]) + k + s if is_prime else cK + s
I = hmac_oneshot(c, data, hashlib.sha512)
I_left = ecc.string_to_number(I[0:32])
k_n = (I_left + ecc.string_to_number(k)) % ecc.CURVE_ORDER
if I_left >= ecc.CURVE_ORDER or k_n == 0:
raise ecc.InvalidECPointException()
k_n = ecc.number_to_string(k_n, ecc.CURVE_ORDER)
c_n = I[32:]
return k_n, c_n
# Child public key derivation function (from public key only)
# K = master public key
# c = master chain code
# n = index of key we want to derive
# This function allows us to find the nth public key, as long as n is
# not hardened. If n is hardened, we need the master private key to find it.
@protect_against_invalid_ecpoint
def CKD_pub(cK, c, n):
if n < 0: raise ValueError('the bip32 index needs to be non-negative')
if n & BIP32_PRIME: raise Exception()
return _CKD_pub(cK, c, bfh(rev_hex(int_to_hex(n,4))))
# helper function, callable with arbitrary string.
# note: 's' does not need to fit into 32 bits here! (c.f. trustedcoin billing)
def _CKD_pub(cK, c, s):
I = hmac_oneshot(c, cK + s, hashlib.sha512)
pubkey = ecc.ECPrivkey(I[0:32]) + ecc.ECPubkey(cK)
if pubkey.is_at_infinity():
raise ecc.InvalidECPointException()
cK_n = pubkey.get_public_key_bytes(compressed=True)
c_n = I[32:]
return cK_n, c_n
def xprv_header(xtype, *, net=None):
if net is None:
net = constants.net
return bfh("%08x" % net.XPRV_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, *, net=None):
if not ecc.is_secret_within_curve_range(k):
raise BitcoinException('Impossible xprv (not within curve order)')
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, *, net=None):
xpub = xpub_header(xtype, net=net) \
+ bytes([depth]) + fingerprint + child_number + c + cK
return EncodeBase58Check(xpub)
class InvalidMasterKeyVersionBytes(BitcoinException): pass
def deserialize_xkey(xkey, prv, *, net=None):
if net is None:
net = constants.net
xkey = DecodeBase58Check(xkey)
if len(xkey) != 78:
raise BitcoinException('Invalid length for extended key: {}'
.format(len(xkey)))
depth = xkey[4]
fingerprint = xkey[5:9]
child_number = xkey[9:13]
c = xkey[13:13+32]
header = int('0x' + bh2u(xkey[0:4]), 16)
headers = net.XPRV_HEADERS if prv else net.XPUB_HEADERS
if header not in headers.values():
raise InvalidMasterKeyVersionBytes('Invalid extended key format: {}'
.format(hex(header)))
xtype = list(headers.keys())[list(headers.values()).index(header)]
n = 33 if prv else 32
K_or_k = xkey[13+n:]
if prv and not ecc.is_secret_within_curve_range(K_or_k):
raise BitcoinException('Impossible xprv (not within curve order)')
return xtype, depth, fingerprint, child_number, c, K_or_k
def deserialize_xpub(xkey, *, net=None):
return deserialize_xkey(xkey, False, net=net)
def deserialize_xprv(xkey, *, net=None):
return deserialize_xkey(xkey, True, net=net)
def xpub_type(x):
return deserialize_xpub(x)[0]
def is_xpub(text):
try:
deserialize_xpub(text)
return True
except:
return False
def is_xprv(text):
try:
deserialize_xprv(text)
return True
except:
return False
def xpub_from_xprv(xprv):
xtype, depth, fingerprint, child_number, c, k = deserialize_xprv(xprv)
cK = ecc.ECPrivkey(k).get_public_key_bytes(compressed=True)
return serialize_xpub(xtype, c, cK, depth, fingerprint, child_number)
def bip32_root(seed, xtype):
I = hmac_oneshot(b"Bitcoin seed", seed, hashlib.sha512)
master_k = I[0:32]
master_c = I[32:]
# create xprv first, as that will check if master_k is within curve order
xprv = serialize_xprv(xtype, master_c, master_k)
cK = ecc.ECPrivkey(master_k).get_public_key_bytes(compressed=True)
xpub = serialize_xpub(xtype, master_c, cK)
return xprv, xpub
def xpub_from_pubkey(xtype, cK):
if cK[0] not in (0x02, 0x03):
raise ValueError('Unexpected first byte: {}'.format(cK[0]))
return serialize_xpub(xtype, b'\x00'*32, cK)
def bip32_derivation(s):
if not s.startswith('m/'):
raise ValueError('invalid bip32 derivation path: {}'.format(s))
s = s[2:]
for n in s.split('/'):
if n == '': continue
i = int(n[:-1]) + BIP32_PRIME if n[-1] == "'" else int(n)
yield i
def convert_bip32_path_to_list_of_uint32(n: str) -> List[int]:
"""Convert bip32 path to list of uint32 integers with prime flags
m/0/-1/1' -> [0, 0x80000001, 0x80000001]
based on code in trezorlib
"""
path = []
for x in n.split('/')[1:]:
if x == '': continue
prime = 0
if x.endswith("'"):
x = x.replace('\'', '')
prime = BIP32_PRIME
if x.startswith('-'):
prime = BIP32_PRIME
path.append(abs(int(x)) | prime)
return path
def is_bip32_derivation(x):
try:
[ i for i in bip32_derivation(x)]
return True
except :
return False
def bip32_private_derivation(xprv, branch, sequence):
if not sequence.startswith(branch):
raise ValueError('incompatible branch ({}) and sequence ({})'
.format(branch, sequence))
if branch == sequence:
return xprv, xpub_from_xprv(xprv)
xtype, depth, fingerprint, child_number, c, k = deserialize_xprv(xprv)
sequence = sequence[len(branch):]
for n in sequence.split('/'):
if n == '': continue
i = int(n[:-1]) + BIP32_PRIME if n[-1] == "'" else int(n)
parent_k = k
k, c = CKD_priv(k, c, i)
depth += 1
parent_cK = ecc.ECPrivkey(parent_k).get_public_key_bytes(compressed=True)
fingerprint = hash_160(parent_cK)[0:4]
child_number = bfh("%08X"%i)
cK = ecc.ECPrivkey(k).get_public_key_bytes(compressed=True)
xpub = serialize_xpub(xtype, c, cK, depth, fingerprint, child_number)
xprv = serialize_xprv(xtype, c, k, depth, fingerprint, child_number)
return xprv, xpub
def bip32_public_derivation(xpub, branch, sequence):
xtype, depth, fingerprint, child_number, c, cK = deserialize_xpub(xpub)
if not sequence.startswith(branch):
raise ValueError('incompatible branch ({}) and sequence ({})'
.format(branch, sequence))
sequence = sequence[len(branch):]
for n in sequence.split('/'):
if n == '': continue
i = int(n)
parent_cK = cK
cK, c = CKD_pub(cK, c, i)
depth += 1
fingerprint = hash_160(parent_cK)[0:4]
child_number = bfh("%08X"%i)
return serialize_xpub(xtype, c, cK, depth, fingerprint, child_number)
def bip32_private_key(sequence, k, chain):
for i in sequence:
k, chain = CKD_priv(k, chain, i)
return k

29
electrum/keystore.py

@ -25,13 +25,19 @@
# SOFTWARE. # SOFTWARE.
from unicodedata import normalize from unicodedata import normalize
import hashlib
from . import bitcoin, ecc, constants
from .bitcoin import * from . import bitcoin, ecc, constants, bip32
from .bitcoin import (deserialize_privkey, serialize_privkey,
public_key_to_p2pkh, seed_type, is_seed)
from .bip32 import (bip32_public_derivation, deserialize_xpub, CKD_pub,
bip32_root, deserialize_xprv, bip32_private_derivation,
bip32_private_key, bip32_derivation, BIP32_PRIME,
is_xpub, is_xprv)
from .ecc import string_to_number, number_to_string from .ecc import string_to_number, number_to_string
from .crypto import pw_decode, pw_encode from .crypto import pw_decode, pw_encode, Hash
from .util import (PrintError, InvalidPassword, hfu, WalletFileException, from .util import (PrintError, InvalidPassword, hfu, WalletFileException,
BitcoinException) BitcoinException, bh2u, bfh, print_error, inv_dict)
from .mnemonic import Mnemonic, load_wordlist from .mnemonic import Mnemonic, load_wordlist
from .plugin import run_hook from .plugin import run_hook
@ -332,7 +338,7 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
def add_xprv(self, xprv): def add_xprv(self, xprv):
self.xprv = xprv self.xprv = xprv
self.xpub = bitcoin.xpub_from_xprv(xprv) self.xpub = bip32.xpub_from_xprv(xprv)
def add_xprv_from_seed(self, bip32_seed, xtype, derivation): def add_xprv_from_seed(self, bip32_seed, xtype, derivation):
xprv, xpub = bip32_root(bip32_seed, xtype) xprv, xpub = bip32_root(bip32_seed, xtype)
@ -614,6 +620,13 @@ def from_bip39_seed(seed, passphrase, derivation, xtype=None):
return k return k
PURPOSE48_SCRIPT_TYPES = {
'p2wsh-p2sh': 1, # specifically multisig
'p2wsh': 2, # specifically multisig
}
PURPOSE48_SCRIPT_TYPES_INV = inv_dict(PURPOSE48_SCRIPT_TYPES)
def xtype_from_derivation(derivation: str) -> str: def xtype_from_derivation(derivation: str) -> str:
"""Returns the script type to be used for this derivation.""" """Returns the script type to be used for this derivation."""
if derivation.startswith("m/84'"): if derivation.startswith("m/84'"):
@ -781,7 +794,7 @@ def from_seed(seed, passphrase, is_p2sh=False):
def from_private_key_list(text): def from_private_key_list(text):
keystore = Imported_KeyStore({}) keystore = Imported_KeyStore({})
for x in get_private_keys(text): for x in get_private_keys(text):
keystore.import_key(x, None) keystore.import_privkey(x, None)
return keystore return keystore
def from_old_mpk(mpk): def from_old_mpk(mpk):
@ -795,7 +808,7 @@ def from_xpub(xpub):
return k return k
def from_xprv(xprv): def from_xprv(xprv):
xpub = bitcoin.xpub_from_xprv(xprv) xpub = bip32.xpub_from_xprv(xprv)
k = BIP32_KeyStore({}) k = BIP32_KeyStore({})
k.xprv = xprv k.xprv = xprv
k.xpub = xpub k.xpub = xpub

7
electrum/paymentrequest.py

@ -38,9 +38,8 @@ except ImportError:
sys.exit("Error: could not find paymentrequest_pb2.py. Create it with 'protoc --proto_path=electrum/ --python_out=electrum/ electrum/paymentrequest.proto'") sys.exit("Error: could not find paymentrequest_pb2.py. Create it with 'protoc --proto_path=electrum/ --python_out=electrum/ electrum/paymentrequest.proto'")
from . import bitcoin, ecc, util, transaction, x509, rsakey from . import bitcoin, ecc, util, transaction, x509, rsakey
from .util import print_error, bh2u, bfh from .util import print_error, bh2u, bfh, export_meta, import_meta
from .util import export_meta, import_meta from .crypto import sha256
from .bitcoin import TYPE_ADDRESS from .bitcoin import TYPE_ADDRESS
from .transaction import TxOutput from .transaction import TxOutput
@ -113,7 +112,7 @@ class PaymentRequest:
def parse(self, r): def parse(self, r):
if self.error: if self.error:
return return
self.id = bh2u(bitcoin.sha256(r)[0:16]) self.id = bh2u(sha256(r)[0:16])
try: try:
self.data = pb2.PaymentRequest() self.data = pb2.PaymentRequest()
self.data.ParseFromString(r) self.data.ParseFromString(r)

4
electrum/plugin.py

@ -33,7 +33,7 @@ import threading
from .i18n import _ from .i18n import _
from .util import (profiler, PrintError, DaemonThread, UserCancelled, from .util import (profiler, PrintError, DaemonThread, UserCancelled,
ThreadJob, print_error) ThreadJob, print_error)
from . import bitcoin from . import bip32
from . import plugins from . import plugins
from .simple_config import SimpleConfig from .simple_config import SimpleConfig
@ -432,7 +432,7 @@ class DeviceMgr(ThreadJob, PrintError):
def force_pair_xpub(self, plugin, handler, info, xpub, derivation, devices): def force_pair_xpub(self, plugin, handler, info, xpub, derivation, devices):
# The wallet has not been previously paired, so let the user # The wallet has not been previously paired, so let the user
# choose an unpaired device and compare its first address. # choose an unpaired device and compare its first address.
xtype = bitcoin.xpub_type(xpub) xtype = bip32.xpub_type(xpub)
client = self.client_lookup(info.device.id_) client = self.client_lookup(info.device.id_)
if client and client.is_pairable(): if client and client.is_pairable():
# See comment above for same code # See comment above for same code

14
electrum/plugins/coldcard/coldcard.py

@ -3,25 +3,21 @@
# #
# #
from struct import pack, unpack from struct import pack, unpack
import hashlib
import os, sys, time, io import os, sys, time, io
import traceback import traceback
from electrum import bitcoin from electrum.bip32 import serialize_xpub, deserialize_xpub, InvalidMasterKeyVersionBytes
from electrum.bitcoin import serialize_xpub, deserialize_xpub, InvalidMasterKeyVersionBytes
from electrum import constants
from electrum.bitcoin import TYPE_ADDRESS, int_to_hex
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import BasePlugin, Device from electrum.plugin import Device
from electrum.keystore import Hardware_KeyStore, xpubkey_to_pubkey, Xpub from electrum.keystore import Hardware_KeyStore, xpubkey_to_pubkey, Xpub
from electrum.transaction import Transaction from electrum.transaction import Transaction
from electrum.wallet import Standard_Wallet from electrum.wallet import Standard_Wallet
from electrum.crypto import hash_160 from electrum.crypto import hash_160
from ..hw_wallet import HW_PluginBase
from ..hw_wallet.plugin import is_any_tx_output_on_change_branch
from electrum.util import print_error, bfh, bh2u, versiontuple from electrum.util import print_error, bfh, bh2u, versiontuple
from electrum.base_wizard import ScriptTypeNotSupported from electrum.base_wizard import ScriptTypeNotSupported
from ..hw_wallet import HW_PluginBase
try: try:
import hid import hid
from ckcc.protocol import CCProtocolPacker, CCProtocolUnpacker from ckcc.protocol import CCProtocolPacker, CCProtocolUnpacker
@ -46,7 +42,7 @@ try:
from electrum.ecc import ECPubkey from electrum.ecc import ECPubkey
xtype, depth, parent_fingerprint, child_number, chain_code, K_or_k \ xtype, depth, parent_fingerprint, child_number, chain_code, K_or_k \
= bitcoin.deserialize_xpub(expect_xpub) = deserialize_xpub(expect_xpub)
pubkey = ECPubkey(K_or_k) pubkey = ECPubkey(K_or_k)
try: try:

8
electrum/plugins/cosigner_pool/qt.py

@ -30,7 +30,7 @@ from PyQt5.QtGui import *
from PyQt5.QtCore import * from PyQt5.QtCore import *
from PyQt5.QtWidgets import QPushButton from PyQt5.QtWidgets import QPushButton
from electrum import bitcoin, util, keystore, ecc from electrum import util, keystore, ecc, bip32, crypto
from electrum import transaction from electrum import transaction
from electrum.plugin import BasePlugin, hook from electrum.plugin import BasePlugin, hook
from electrum.i18n import _ from electrum.i18n import _
@ -132,8 +132,8 @@ class Plugin(BasePlugin):
self.cosigner_list = [] self.cosigner_list = []
for key, keystore in wallet.keystores.items(): for key, keystore in wallet.keystores.items():
xpub = keystore.get_master_public_key() xpub = keystore.get_master_public_key()
K = bitcoin.deserialize_xpub(xpub)[-1] K = bip32.deserialize_xpub(xpub)[-1]
_hash = bh2u(bitcoin.Hash(K)) _hash = bh2u(crypto.Hash(K))
if not keystore.is_watching_only(): if not keystore.is_watching_only():
self.keys.append((key, _hash, window)) self.keys.append((key, _hash, window))
else: else:
@ -222,7 +222,7 @@ class Plugin(BasePlugin):
if not xprv: if not xprv:
return return
try: try:
k = bitcoin.deserialize_xprv(xprv)[-1] k = bip32.deserialize_xprv(xprv)[-1]
EC = ecc.ECPrivkey(k) EC = ecc.ECPrivkey(k)
message = bh2u(EC.decrypt_message(message)) message = bh2u(EC.decrypt_message(message))
except Exception as e: except Exception as e:

5
electrum/plugins/digitalbitbox/digitalbitbox.py

@ -5,8 +5,9 @@
try: try:
from electrum.crypto import Hash, EncodeAES, DecodeAES from electrum.crypto import Hash, EncodeAES, DecodeAES
from electrum.bitcoin import (TYPE_ADDRESS, push_script, var_int, public_key_to_p2pkh, is_address, from electrum.bitcoin import (TYPE_ADDRESS, push_script, var_int, public_key_to_p2pkh,
serialize_xpub, deserialize_xpub) is_address)
from electrum.bip32 import serialize_xpub, deserialize_xpub
from electrum import ecc from electrum import ecc
from electrum.ecc import msg_magic from electrum.ecc import msg_magic
from electrum.wallet import Standard_Wallet from electrum.wallet import Standard_Wallet

2
electrum/plugins/keepkey/clientbase.py

@ -4,7 +4,7 @@ from struct import pack
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import PrintError, UserCancelled from electrum.util import PrintError, UserCancelled
from electrum.keystore import bip39_normalize_passphrase from electrum.keystore import bip39_normalize_passphrase
from electrum.bitcoin import serialize_xpub, convert_bip32_path_to_list_of_uint32 from electrum.bip32 import serialize_xpub, convert_bip32_path_to_list_of_uint32
class GuiMixin(object): class GuiMixin(object):

6
electrum/plugins/keepkey/keepkey.py

@ -3,14 +3,12 @@ import traceback
import sys import sys
from electrum.util import bfh, bh2u, UserCancelled from electrum.util import bfh, bh2u, UserCancelled
from electrum.bitcoin import (xpub_from_pubkey, deserialize_xpub, from electrum.bitcoin import TYPE_ADDRESS, TYPE_SCRIPT
TYPE_ADDRESS, TYPE_SCRIPT) from electrum.bip32 import deserialize_xpub
from electrum import constants from electrum import constants
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import BasePlugin
from electrum.transaction import deserialize, Transaction from electrum.transaction import deserialize, Transaction
from electrum.keystore import Hardware_KeyStore, is_xpubkey, parse_xpubkey from electrum.keystore import Hardware_KeyStore, is_xpubkey, parse_xpubkey
from electrum.wallet import Standard_Wallet
from electrum.base_wizard import ScriptTypeNotSupported from electrum.base_wizard import ScriptTypeNotSupported
from ..hw_wallet import HW_PluginBase from ..hw_wallet import HW_PluginBase

7
electrum/plugins/keepkey/qt.py

@ -7,9 +7,8 @@ from PyQt5.Qt import QVBoxLayout, QLabel
from electrum.gui.qt.util import * from electrum.gui.qt.util import *
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import hook, DeviceMgr from electrum.plugin import hook
from electrum.util import PrintError, UserCancelled, bh2u from electrum.util import bh2u
from electrum.wallet import Wallet, Standard_Wallet
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
from ..hw_wallet.plugin import only_hook_if_libraries_available from ..hw_wallet.plugin import only_hook_if_libraries_available
@ -253,7 +252,7 @@ class QtPlugin(QtPluginBase):
else: else:
msg = _("Enter the master private key beginning with xprv:") msg = _("Enter the master private key beginning with xprv:")
def set_enabled(): def set_enabled():
from keystore import is_xprv from electrum.bip32 import is_xprv
wizard.next_button.setEnabled(is_xprv(clean_text(text))) wizard.next_button.setEnabled(is_xprv(clean_text(text)))
text.textChanged.connect(set_enabled) text.textChanged.connect(set_enabled)
next_enabled = False next_enabled = False

10
electrum/plugins/ledger/ledger.py

@ -3,18 +3,18 @@ import hashlib
import sys import sys
import traceback import traceback
from electrum import bitcoin
from electrum.bitcoin import TYPE_ADDRESS, int_to_hex, var_int from electrum.bitcoin import TYPE_ADDRESS, int_to_hex, var_int
from electrum.bip32 import serialize_xpub
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import BasePlugin
from electrum.keystore import Hardware_KeyStore from electrum.keystore import Hardware_KeyStore
from electrum.transaction import Transaction from electrum.transaction import Transaction
from electrum.wallet import Standard_Wallet from electrum.wallet import Standard_Wallet
from ..hw_wallet import HW_PluginBase
from ..hw_wallet.plugin import is_any_tx_output_on_change_branch
from electrum.util import print_error, bfh, bh2u, versiontuple from electrum.util import print_error, bfh, bh2u, versiontuple
from electrum.base_wizard import ScriptTypeNotSupported from electrum.base_wizard import ScriptTypeNotSupported
from ..hw_wallet import HW_PluginBase
from ..hw_wallet.plugin import is_any_tx_output_on_change_branch
try: try:
import hid import hid
from btchip.btchipComm import HIDDongleHIDAPI, DongleWait from btchip.btchipComm import HIDDongleHIDAPI, DongleWait
@ -112,7 +112,7 @@ class Ledger_Client():
depth = len(splitPath) depth = len(splitPath)
lastChild = splitPath[len(splitPath) - 1].split('\'') lastChild = splitPath[len(splitPath) - 1].split('\'')
childnum = int(lastChild[0]) if len(lastChild) == 1 else 0x80000000 | int(lastChild[0]) 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)) xpub = serialize_xpub(xtype, nodeData['chainCode'], publicKey, depth, self.i4b(fingerprint), self.i4b(childnum))
return xpub return xpub
def has_detached_pin_support(self, client): def has_detached_pin_support(self, client):

2
electrum/plugins/safe_t/clientbase.py

@ -4,7 +4,7 @@ from struct import pack
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import PrintError, UserCancelled from electrum.util import PrintError, UserCancelled
from electrum.keystore import bip39_normalize_passphrase from electrum.keystore import bip39_normalize_passphrase
from electrum.bitcoin import serialize_xpub, convert_bip32_path_to_list_of_uint32 from electrum.bip32 import serialize_xpub, convert_bip32_path_to_list_of_uint32
class GuiMixin(object): class GuiMixin(object):

7
electrum/plugins/safe_t/qt.py

@ -7,9 +7,8 @@ from PyQt5.Qt import QVBoxLayout, QLabel
from electrum.gui.qt.util import * from electrum.gui.qt.util import *
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import hook, DeviceMgr from electrum.plugin import hook
from electrum.util import PrintError, UserCancelled, bh2u from electrum.util import bh2u
from electrum.wallet import Wallet, Standard_Wallet
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
from ..hw_wallet.plugin import only_hook_if_libraries_available from ..hw_wallet.plugin import only_hook_if_libraries_available
@ -127,7 +126,7 @@ class QtPlugin(QtPluginBase):
else: else:
msg = _("Enter the master private key beginning with xprv:") msg = _("Enter the master private key beginning with xprv:")
def set_enabled(): def set_enabled():
from electrum.keystore import is_xprv from electrum.bip32 import is_xprv
wizard.next_button.setEnabled(is_xprv(clean_text(text))) wizard.next_button.setEnabled(is_xprv(clean_text(text)))
text.textChanged.connect(set_enabled) text.textChanged.connect(set_enabled)
next_enabled = False next_enabled = False

6
electrum/plugins/safe_t/safe_t.py

@ -3,11 +3,11 @@ import traceback
import sys import sys
from electrum.util import bfh, bh2u, versiontuple, UserCancelled from electrum.util import bfh, bh2u, versiontuple, UserCancelled
from electrum.bitcoin import (b58_address_to_hash160, xpub_from_pubkey, deserialize_xpub, from electrum.bitcoin import TYPE_ADDRESS, TYPE_SCRIPT
TYPE_ADDRESS, TYPE_SCRIPT, is_address) from electrum.bip32 import deserialize_xpub
from electrum import constants from electrum import constants
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import BasePlugin, Device from electrum.plugin import Device
from electrum.transaction import deserialize, Transaction from electrum.transaction import deserialize, Transaction
from electrum.keystore import Hardware_KeyStore, is_xpubkey, parse_xpubkey from electrum.keystore import Hardware_KeyStore, is_xpubkey, parse_xpubkey
from electrum.base_wizard import ScriptTypeNotSupported from electrum.base_wizard import ScriptTypeNotSupported

2
electrum/plugins/trezor/clientbase.py

@ -4,7 +4,7 @@ from struct import pack
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import PrintError, UserCancelled from electrum.util import PrintError, UserCancelled
from electrum.keystore import bip39_normalize_passphrase from electrum.keystore import bip39_normalize_passphrase
from electrum.bitcoin import serialize_xpub, convert_bip32_path_to_list_of_uint32 from electrum.bip32 import serialize_xpub, convert_bip32_path_to_list_of_uint32
class GuiMixin(object): class GuiMixin(object):

7
electrum/plugins/trezor/qt.py

@ -7,9 +7,8 @@ from PyQt5.Qt import QVBoxLayout, QLabel
from electrum.gui.qt.util import * from electrum.gui.qt.util import *
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import hook, DeviceMgr from electrum.plugin import hook
from electrum.util import PrintError, UserCancelled, bh2u from electrum.util import bh2u
from electrum.wallet import Wallet, Standard_Wallet
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
from ..hw_wallet.plugin import only_hook_if_libraries_available from ..hw_wallet.plugin import only_hook_if_libraries_available
@ -222,7 +221,7 @@ class QtPlugin(QtPluginBase):
else: else:
msg = _("Enter the master private key beginning with xprv:") msg = _("Enter the master private key beginning with xprv:")
def set_enabled(): def set_enabled():
from electrum.keystore import is_xprv from electrum.bip32 import is_xprv
wizard.next_button.setEnabled(is_xprv(clean_text(text))) wizard.next_button.setEnabled(is_xprv(clean_text(text)))
text.textChanged.connect(set_enabled) text.textChanged.connect(set_enabled)
next_enabled = False next_enabled = False

6
electrum/plugins/trezor/trezor.py

@ -3,11 +3,11 @@ import traceback
import sys import sys
from electrum.util import bfh, bh2u, versiontuple, UserCancelled from electrum.util import bfh, bh2u, versiontuple, UserCancelled
from electrum.bitcoin import (xpub_from_pubkey, deserialize_xpub, from electrum.bitcoin import TYPE_ADDRESS, TYPE_SCRIPT
TYPE_ADDRESS, TYPE_SCRIPT) from electrum.bip32 import deserialize_xpub
from electrum import constants from electrum import constants
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import BasePlugin, Device from electrum.plugin import Device
from electrum.transaction import deserialize, Transaction from electrum.transaction import deserialize, Transaction
from electrum.keystore import Hardware_KeyStore, is_xpubkey, parse_xpubkey from electrum.keystore import Hardware_KeyStore, is_xpubkey, parse_xpubkey
from electrum.base_wizard import ScriptTypeNotSupported from electrum.base_wizard import ScriptTypeNotSupported

25
electrum/plugins/trustedcoin/trustedcoin.py

@ -24,14 +24,19 @@
# SOFTWARE. # SOFTWARE.
import asyncio import asyncio
import socket import socket
import os
import json import json
import base64 import base64
import time
import hashlib
from urllib.parse import urljoin from urllib.parse import urljoin
from urllib.parse import quote from urllib.parse import quote
from electrum import bitcoin, ecc, constants, keystore, version from electrum import ecc, constants, keystore, version, bip32
from electrum.bitcoin import * from electrum.bitcoin import TYPE_ADDRESS, is_new_seed, public_key_to_p2pkh
from electrum.bip32 import (deserialize_xpub, deserialize_xprv, bip32_private_key, CKD_pub,
serialize_xpub, bip32_root, bip32_private_derivation)
from electrum.crypto import sha256
from electrum.transaction import TxOutput from electrum.transaction import TxOutput
from electrum.mnemonic import Mnemonic from electrum.mnemonic import Mnemonic
from electrum.wallet import Multisig_Wallet, Deterministic_Wallet from electrum.wallet import Multisig_Wallet, Deterministic_Wallet
@ -348,7 +353,7 @@ class Wallet_2fa(Multisig_Wallet):
def get_user_id(storage): def get_user_id(storage):
def make_long_id(xpub_hot, xpub_cold): def make_long_id(xpub_hot, xpub_cold):
return bitcoin.sha256(''.join(sorted([xpub_hot, xpub_cold]))) return sha256(''.join(sorted([xpub_hot, xpub_cold])))
xpub1 = storage.get('x1/')['xpub'] xpub1 = storage.get('x1/')['xpub']
xpub2 = storage.get('x2/')['xpub'] xpub2 = storage.get('x2/')['xpub']
long_id = make_long_id(xpub1, xpub2) long_id = make_long_id(xpub1, xpub2)
@ -357,15 +362,15 @@ def get_user_id(storage):
def make_xpub(xpub, s): def make_xpub(xpub, s):
version, _, _, _, c, cK = deserialize_xpub(xpub) version, _, _, _, c, cK = deserialize_xpub(xpub)
cK2, c2 = bitcoin._CKD_pub(cK, c, s) cK2, c2 = bip32._CKD_pub(cK, c, s)
return bitcoin.serialize_xpub(version, c2, cK2) return serialize_xpub(version, c2, cK2)
def make_billing_address(wallet, num): def make_billing_address(wallet, num):
long_id, short_id = wallet.get_user_id() long_id, short_id = wallet.get_user_id()
xpub = make_xpub(get_billing_xpub(), long_id) xpub = make_xpub(get_billing_xpub(), long_id)
version, _, _, _, c, cK = deserialize_xpub(xpub) version, _, _, _, c, cK = deserialize_xpub(xpub)
cK, c = bitcoin.CKD_pub(cK, c, num) cK, c = CKD_pub(cK, c, num)
return bitcoin.public_key_to_p2pkh(cK) return public_key_to_p2pkh(cK)
class TrustedCoinPlugin(BasePlugin): class TrustedCoinPlugin(BasePlugin):
@ -379,7 +384,7 @@ class TrustedCoinPlugin(BasePlugin):
@staticmethod @staticmethod
def is_valid_seed(seed): def is_valid_seed(seed):
return bitcoin.is_new_seed(seed, SEED_PREFIX) return is_new_seed(seed, SEED_PREFIX)
def is_available(self): def is_available(self):
return True return True
@ -479,8 +484,6 @@ class TrustedCoinPlugin(BasePlugin):
@classmethod @classmethod
def get_xkeys(self, seed, passphrase, derivation): def get_xkeys(self, seed, passphrase, derivation):
from electrum.mnemonic import Mnemonic
from electrum.keystore import bip32_root, bip32_private_derivation
bip32_seed = Mnemonic.mnemonic_to_seed(seed, passphrase) bip32_seed = Mnemonic.mnemonic_to_seed(seed, passphrase)
xprv, xpub = bip32_root(bip32_seed, 'standard') xprv, xpub = bip32_root(bip32_seed, 'standard')
xprv, xpub = bip32_private_derivation(xprv, "m/", derivation) xprv, xpub = bip32_private_derivation(xprv, "m/", derivation)

21
electrum/tests/test_bitcoin.py

@ -1,16 +1,17 @@
import base64 import base64
import sys import sys
from electrum.bitcoin import ( from electrum.bitcoin import (public_key_to_p2pkh, address_from_private_key,
public_key_to_p2pkh, is_address, is_private_key, is_new_seed, is_old_seed,
bip32_root, bip32_public_derivation, bip32_private_derivation, var_int, op_push, address_to_script,
Hash, address_from_private_key, deserialize_privkey, serialize_privkey, is_segwit_address,
is_address, is_private_key, xpub_from_xprv, is_new_seed, is_old_seed, is_b58_address, address_to_scripthash, is_minikey,
var_int, op_push, address_to_script, is_compressed, seed_type, EncodeBase58Check,
deserialize_privkey, serialize_privkey, is_segwit_address, script_num_to_hex, push_script, add_number_to_script, int_to_hex)
is_b58_address, address_to_scripthash, is_minikey, is_compressed, is_xpub, from electrum.bip32 import (bip32_root, bip32_public_derivation, bip32_private_derivation,
xpub_type, is_xprv, is_bip32_derivation, seed_type, EncodeBase58Check, xpub_from_xprv, xpub_type, is_xprv, is_bip32_derivation,
script_num_to_hex, push_script, add_number_to_script, int_to_hex, convert_bip32_path_to_list_of_uint32) is_xpub, convert_bip32_path_to_list_of_uint32)
from electrum.crypto import Hash
from electrum import ecc, crypto, constants from electrum import ecc, crypto, constants
from electrum.ecc import number_to_string, string_to_number from electrum.ecc import number_to_string, string_to_number
from electrum.transaction import opcodes from electrum.transaction import opcodes

31
electrum/transaction.py

@ -27,23 +27,22 @@
# Note: The deserialization code originally comes from ABE. # Note: The deserialization code originally comes from ABE.
from typing import (Sequence, Union, NamedTuple, Tuple, Optional, Iterable,
Callable)
from .util import print_error, profiler
from . import ecc
from . import bitcoin
from .bitcoin import *
import struct import struct
import traceback import traceback
import sys import sys
from typing import (Sequence, Union, NamedTuple, Tuple, Optional, Iterable,
# Callable, List)
# Workalike python implementation of Bitcoin's CDataStream class.
# from . import ecc, bitcoin, constants, segwit_addr
from .util import print_error, profiler, to_bytes, bh2u, bfh
from .bitcoin import (TYPE_ADDRESS, TYPE_PUBKEY, TYPE_SCRIPT, hash_160,
hash160_to_p2sh, hash160_to_p2pkh, hash_to_segwit_addr,
hash_encode, var_int, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC, COIN,
op_push, int_to_hex, push_script, b58_address_to_hash160)
from .crypto import Hash
from .keystore import xpubkey_to_address, xpubkey_to_pubkey from .keystore import xpubkey_to_address, xpubkey_to_pubkey
NO_SIGNATURE = 'ff' NO_SIGNATURE = 'ff'
PARTIAL_TXN_HEADER_MAGIC = b'EPTF\xff' PARTIAL_TXN_HEADER_MAGIC = b'EPTF\xff'
@ -78,6 +77,8 @@ TxOutputHwInfo = NamedTuple("TxOutputHwInfo", [('address_index', Tuple),
class BCDataStream(object): class BCDataStream(object):
"""Workalike python implementation of Bitcoin's CDataStream class."""
def __init__(self): def __init__(self):
self.input = None self.input = None
self.read_cursor = 0 self.read_cursor = 0
@ -353,7 +354,7 @@ def parse_scriptSig(d, _bytes):
if item[0] == 0: if item[0] == 0:
# segwit embedded into p2sh # segwit embedded into p2sh
# witness version 0 # witness version 0
d['address'] = bitcoin.hash160_to_p2sh(bitcoin.hash_160(item)) d['address'] = bitcoin.hash160_to_p2sh(hash_160(item))
if len(item) == 22: if len(item) == 22:
d['type'] = 'p2wpkh-p2sh' d['type'] = 'p2wpkh-p2sh'
elif len(item) == 34: elif len(item) == 34:
@ -901,7 +902,7 @@ class Transaction:
witver, witprog = segwit_addr.decode(constants.net.SEGWIT_HRP, addr) witver, witprog = segwit_addr.decode(constants.net.SEGWIT_HRP, addr)
if witprog is not None: if witprog is not None:
return 'p2wpkh' return 'p2wpkh'
addrtype, hash_160 = b58_address_to_hash160(addr) addrtype, hash_160_ = b58_address_to_hash160(addr)
if addrtype == constants.net.ADDRTYPE_P2PKH: if addrtype == constants.net.ADDRTYPE_P2PKH:
return 'p2pkh' return 'p2pkh'
elif addrtype == constants.net.ADDRTYPE_P2SH: elif addrtype == constants.net.ADDRTYPE_P2SH:
@ -977,7 +978,7 @@ class Transaction:
return multisig_script(pubkeys, txin['num_sig']) return multisig_script(pubkeys, txin['num_sig'])
elif txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']: elif txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']:
pubkey = pubkeys[0] pubkey = pubkeys[0]
pkh = bh2u(bitcoin.hash_160(bfh(pubkey))) pkh = bh2u(hash_160(bfh(pubkey)))
return '76a9' + push_script(pkh) + '88ac' return '76a9' + push_script(pkh) + '88ac'
elif txin['type'] == 'p2pk': elif txin['type'] == 'p2pk':
pubkey = pubkeys[0] pubkey = pubkeys[0]

3
electrum/verifier.py

@ -27,7 +27,8 @@ from typing import Sequence, Optional, TYPE_CHECKING
import aiorpcx import aiorpcx
from .util import bh2u, VerifiedTxInfo, NetworkJobOnDefaultServer from .util import bh2u, VerifiedTxInfo, NetworkJobOnDefaultServer
from .bitcoin import Hash, hash_decode, hash_encode from .crypto import Hash
from .bitcoin import hash_decode, hash_encode
from .transaction import Transaction from .transaction import Transaction
from .blockchain import hash_header from .blockchain import hash_header
from .interface import GracefulDisconnect from .interface import GracefulDisconnect

16
electrum/wallet.py

@ -44,18 +44,20 @@ from .util import (NotEnoughFunds, PrintError, UserCancelled, profiler,
format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates, format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates,
TimeoutException, WalletFileException, BitcoinException, TimeoutException, WalletFileException, BitcoinException,
InvalidPassword, format_time, timestamp_to_datetime, Satoshis, InvalidPassword, format_time, timestamp_to_datetime, Satoshis,
Fiat) Fiat, bfh, bh2u)
from .bitcoin import * from .bitcoin import (COIN, TYPE_ADDRESS, is_address, address_to_script,
is_minikey)
from .version import * from .version import *
from .crypto import Hash
from .keystore import load_keystore, Hardware_KeyStore from .keystore import load_keystore, Hardware_KeyStore
from .storage import multisig_type, STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW, WalletStorage from .storage import multisig_type, STO_EV_PLAINTEXT, STO_EV_USER_PW, STO_EV_XPUB_PW, WalletStorage
from . import transaction, bitcoin, coinchooser, paymentrequest, contacts from . import transaction, bitcoin, coinchooser, paymentrequest, ecc, bip32
from .transaction import Transaction, TxOutput, TxOutputHwInfo from .transaction import Transaction, TxOutput, TxOutputHwInfo
from .plugin import run_hook from .plugin import run_hook
from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL, from .address_synchronizer import (AddressSynchronizer, TX_HEIGHT_LOCAL,
TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED) TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED)
from .paymentrequest import PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED from .paymentrequest import (PR_PAID, PR_UNPAID, PR_UNKNOWN, PR_EXPIRED,
from .paymentrequest import InvoiceStore InvoiceStore)
from .contacts import Contacts from .contacts import Contacts
from .network import Network from .network import Network
from .simple_config import SimpleConfig from .simple_config import SimpleConfig
@ -1499,7 +1501,7 @@ class Simple_Deterministic_Wallet(Simple_Wallet, Deterministic_Wallet):
def load_keystore(self): def load_keystore(self):
self.keystore = load_keystore(self.storage, 'keystore') self.keystore = load_keystore(self.storage, 'keystore')
try: try:
xtype = bitcoin.xpub_type(self.keystore.xpub) xtype = bip32.xpub_type(self.keystore.xpub)
except: except:
xtype = 'standard' xtype = 'standard'
self.txin_type = 'p2pkh' if xtype == 'standard' else xtype self.txin_type = 'p2pkh' if xtype == 'standard' else xtype
@ -1569,7 +1571,7 @@ class Multisig_Wallet(Deterministic_Wallet):
name = 'x%d/'%(i+1) name = 'x%d/'%(i+1)
self.keystores[name] = load_keystore(self.storage, name) self.keystores[name] = load_keystore(self.storage, name)
self.keystore = self.keystores['x1/'] self.keystore = self.keystores['x1/']
xtype = bitcoin.xpub_type(self.keystore.xpub) xtype = bip32.xpub_type(self.keystore.xpub)
self.txin_type = 'p2sh' if xtype == 'standard' else xtype self.txin_type = 'p2sh' if xtype == 'standard' else xtype
def save_keystore(self): def save_keystore(self):

Loading…
Cancel
Save