commit e8b564c0e728e1ca15d39f48a0153c984e2acc3c
parent 4864c802dd0b82c294d4fd1858047990eb463d1e
Author: ThomasV <thomasv@electrum.org>
Date: Mon, 25 Sep 2017 21:35:14 +0200
Extend Wallet Import Format with txin type. Extend class Imported_Wallet.
Diffstat:
10 files changed, 243 insertions(+), 161 deletions(-)
diff --git a/electrum b/electrum
@@ -137,11 +137,19 @@ def run_non_RPC(config):
wallet = Imported_Wallet(storage)
for x in text.split():
wallet.import_address(x)
+ elif keystore.is_private_key_list(text):
+ k = keystore.Imported_KeyStore({})
+ storage.put('keystore', k.dump())
+ storage.put('use_encryption', bool(password))
+ wallet = Imported_Wallet(storage)
+ for x in text.split():
+ wallet.import_private_key(x, password)
+ storage.write()
else:
if keystore.is_seed(text):
k = keystore.from_seed(text, passphrase)
- elif keystore.is_any_key(text):
- k = keystore.from_keys(text)
+ elif keystore.is_master_key(text):
+ k = keystore.from_master_key(text)
else:
sys.exit("Error: Seed or key not recognized")
if password:
diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
@@ -1864,20 +1864,24 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
if not address:
return
try:
- pk_list = self.wallet.get_private_key(address, password)
+ pk, redeem_script = self.wallet.export_private_key(address, password)
except Exception as e:
traceback.print_exc(file=sys.stdout)
self.show_message(str(e))
return
-
d = WindowModalDialog(self, _("Private key"))
d.setMinimumSize(600, 200)
vbox = QVBoxLayout()
vbox.addWidget( QLabel(_("Address") + ': ' + address))
vbox.addWidget( QLabel(_("Private key") + ':'))
- keys_e = ShowQRTextEdit(text='\n'.join(pk_list))
+ keys_e = ShowQRTextEdit(text=pk)
keys_e.addCopyButton(self.app)
vbox.addWidget(keys_e)
+ if redeem_script:
+ vbox.addWidget( QLabel(_("Redeem Script") + ':'))
+ rds_e = ShowQRTextEdit(text=redeem_script)
+ rds_e.addCopyButton(self.app)
+ vbox.addWidget(rds_e)
vbox.addLayout(Buttons(CloseButton(d)))
d.setLayout(vbox)
d.exec_()
@@ -2353,7 +2357,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
if not self.wallet.can_import_privkey():
return
title, msg = _('Import private keys'), _("Enter private keys")
- self._do_import(title, msg, lambda x: self.wallet.import_key(x, password))
+ self._do_import(title, msg, lambda x: self.wallet.import_private_key(x, password))
def update_fiat(self):
b = self.fx and self.fx.is_enabled()
diff --git a/lib/__init__.py b/lib/__init__.py
@@ -1,6 +1,6 @@
from .version import ELECTRUM_VERSION
from .util import format_satoshis, print_msg, print_error, set_verbosity
-from .wallet import Synchronizer, Wallet, Imported_Wallet
+from .wallet import Synchronizer, Wallet
from .storage import WalletStorage
from .coinchooser import COIN_CHOOSERS
from .network import Network, pick_random_server
diff --git a/lib/base_wizard.py b/lib/base_wizard.py
@@ -82,7 +82,7 @@ class BaseWizard(object):
('standard', _("Standard wallet")),
('2fa', _("Wallet with two-factor authentication")),
('multisig', _("Multi-signature wallet")),
- ('imported', _("Watch Bitcoin addresses")),
+ ('imported', _("Import Bitcoin addresses or private keys")),
]
choices = [pair for pair in wallet_kinds if pair[0] in wallet_types]
self.choice_dialog(title=title, message=message, choices=choices, run_next=self.on_wallet_type)
@@ -102,7 +102,7 @@ class BaseWizard(object):
self.load_2fa()
action = self.storage.get_action()
elif choice == 'imported':
- action = 'import_addresses'
+ action = 'import_addresses_or_keys'
self.run(action)
def choose_multisig(self):
@@ -137,26 +137,32 @@ class BaseWizard(object):
self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)
- def import_addresses(self):
- v = keystore.is_address_list
+ def import_addresses_or_keys(self):
+ v = lambda x: keystore.is_address_list(x) or keystore.is_private_key_list(x)
title = _("Import Bitcoin Addresses")
- message = _("Enter a list of Bitcoin addresses. This will create a watching-only wallet.")
- self.add_xpub_dialog(title=title, message=message, run_next=self.on_import_addresses, is_valid=v)
-
- def on_import_addresses(self, text):
- assert keystore.is_address_list(text)
- self.wallet = Imported_Wallet(self.storage)
- for x in text.split():
- self.wallet.import_address(x)
+ message = _("Enter a list of Bitcoin addresses (this will create a watching-only wallet), or a list of private keys.")
+ self.add_xpub_dialog(title=title, message=message, run_next=self.on_import, is_valid=v)
+
+ def on_import(self, text):
+ if keystore.is_address_list(text):
+ self.wallet = Imported_Wallet(self.storage)
+ for x in text.split():
+ self.wallet.import_address(x)
+ elif keystore.is_private_key_list(text):
+ k = keystore.Imported_KeyStore({})
+ self.storage.put('keystore', k.dump())
+ self.wallet = Imported_Wallet(self.storage)
+ for x in text.split():
+ self.wallet.import_private_key(x, None)
self.terminate()
def restore_from_key(self):
if self.wallet_type == 'standard':
- v = keystore.is_any_key
- title = _("Create keystore from keys")
+ v = keystore.is_master_key
+ title = _("Create keystore from a master key")
message = ' '.join([
- _("To create a watching-only wallet, please enter your master public key (xpub)."),
- _("To create a spending wallet, please enter a master private key (xprv), or a list of Bitcoin private keys.")
+ _("To create a watching-only wallet, please enter your master public key (xpub/ypub/zpub)."),
+ _("To create a spending wallet, please enter a master private key (xprv/yprv/zprv).")
])
self.add_xpub_dialog(title=title, message=message, run_next=self.on_restore_from_key, is_valid=v)
else:
@@ -164,7 +170,7 @@ class BaseWizard(object):
self.add_cosigner_dialog(index=i, run_next=self.on_restore_from_key, is_valid=keystore.is_bip32_key)
def on_restore_from_key(self, text):
- k = keystore.from_keys(text)
+ k = keystore.from_master_key(text)
self.on_keystore(k)
def choose_hw_device(self):
@@ -357,7 +363,7 @@ class BaseWizard(object):
self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('choose_keystore'))
def on_cosigner(self, text, password, i):
- k = keystore.from_keys(text, password)
+ k = keystore.from_master_key(text, password)
self.on_keystore(k)
def choose_seed_type(self):
diff --git a/lib/bitcoin.py b/lib/bitcoin.py
@@ -35,7 +35,7 @@ import pyaes
from .util import bfh, bh2u, to_string
from . import version
-from .util import print_error, InvalidPassword, assert_bytes, to_bytes
+from .util import print_error, InvalidPassword, assert_bytes, to_bytes, inv_dict
from . import segwit_addr
def read_json_dict(filename):
@@ -65,7 +65,6 @@ XPUB_HEADERS = {
# Bitcoin network constants
TESTNET = False
-NOLNET = False
ADDRTYPE_P2PKH = 0
ADDRTYPE_P2SH = 5
SEGWIT_HRP = "bc"
@@ -334,6 +333,30 @@ def script_to_p2wsh(script):
return hash_to_segwit_addr(sha256(bfh(script)))
+def pubkey_to_address(txin_type, pubkey):
+ if txin_type == 'p2pkh':
+ return public_key_to_p2pkh(bfh(pubkey))
+ elif txin_type == 'p2wpkh':
+ return hash_to_segwit_addr(hash_160(bfh(pubkey)))
+ elif txin_type == 'p2wpkh-p2sh':
+ scriptSig = transaction.p2wpkh_nested_script(pubkey)
+ return hash160_to_p2sh(hash_160(bfh(scriptSig)))
+ else:
+ raise NotImplementedError(txin_type)
+
+def redeem_script_to_address(txin_type, redeem_script):
+ if txin_type == 'p2sh':
+ return hash160_to_p2sh(hash_160(bfh(redeem_script)))
+ elif txin_type == 'p2wsh':
+ return script_to_p2wsh(redeem_script)
+ elif txin_type == 'p2wsh-p2sh':
+ scriptSig = transaction.p2wsh_nested_script(redeem_script)
+ return hash160_to_p2sh(hash_160(bfh(scriptSig)))
+ else:
+ raise NotImplementedError(txin_type)
+
+
+
def address_to_script(addr):
witver, witprog = segwit_addr.decode(SEGWIT_HRP, addr)
if witprog is not None:
@@ -448,33 +471,42 @@ def DecodeBase58Check(psz):
return key
-def PrivKeyToSecret(privkey):
- return privkey[9:9+32]
+# extended key export format for segwit
-def SecretToASecret(secret, compressed=False):
- addrtype = ADDRTYPE_P2PKH
- vchIn = bytes([(addrtype+128)&255]) + secret
- if compressed: vchIn += b'\01'
+SCRIPT_TYPES = {
+ 'p2pkh':0,
+ 'p2wpkh':1,
+ 'p2wpkh-p2sh':2,
+ 'p2sh':5,
+ 'p2wsh':6,
+ 'p2wsh-p2sh':7
+}
+
+
+def serialize_privkey(secret, compressed, txin_type):
+ prefix = bytes([(SCRIPT_TYPES[txin_type]+128)&255])
+ suffix = b'\01' if compressed else b''
+ vchIn = prefix + secret + suffix
return EncodeBase58Check(vchIn)
-def ASecretToSecret(key):
- addrtype = ADDRTYPE_P2PKH
+def deserialize_privkey(key):
+ # whether the pubkey is compressed should be visible from the keystore
vch = DecodeBase58Check(key)
- if vch and vch[0] == ((addrtype+128)&255):
- return vch[1:]
- elif is_minikey(key):
- return minikey_to_private_key(key)
+ if is_minikey(key):
+ return 'p2pkh', minikey_to_private_key(key), True
+ elif vch:
+ txin_type = inv_dict(SCRIPT_TYPES)[vch[0] - 128]
+ assert len(vch) in [33, 34]
+ compressed = len(vch) == 34
+ return txin_type, vch[1:33], compressed
else:
return False
-def regenerate_key(sec):
- b = ASecretToSecret(sec)
- if not b:
- return False
- b = b[0:32]
- return EC_KEY(b)
+def regenerate_key(pk):
+ assert len(pk) == 32
+ return EC_KEY(pk)
def GetPubKey(pubkey, compressed=False):
@@ -486,15 +518,12 @@ def GetSecret(pkey):
def is_compressed(sec):
- b = ASecretToSecret(sec)
- return len(b) == 33
+ return deserialize_privkey(sec)[2]
-def public_key_from_private_key(sec):
+def public_key_from_private_key(pk, compressed):
# rebuild public key from private key, compressed or uncompressed
- pkey = regenerate_key(sec)
- assert pkey
- compressed = is_compressed(sec)
+ pkey = regenerate_key(pk)
public_key = GetPubKey(pkey.pubkey, compressed)
return bh2u(public_key)
@@ -533,7 +562,7 @@ def is_p2sh(addr):
def is_private_key(key):
try:
- k = ASecretToSecret(key)
+ k = deserialize_privkey(key)
return k is not False
except:
return False
@@ -970,5 +999,4 @@ def bip32_public_derivation(xpub, branch, sequence):
def bip32_private_key(sequence, k, chain):
for i in sequence:
k, chain = CKD_priv(k, chain, i)
- return SecretToASecret(k, True)
-
+ return k
diff --git a/lib/commands.py b/lib/commands.py
@@ -364,11 +364,11 @@ class Commands:
@command('wp')
def importprivkey(self, privkey, password=None):
- """Import a private key. """
+ """Import a private key."""
if not self.wallet.can_import_privkey():
return "Error: This type of wallet cannot import private keys. Try to create a new wallet with that key."
try:
- addr = self.wallet.import_key(privkey, password)
+ addr = self.wallet.import_private_key(privkey, password)
out = "Keypair imported: " + addr
except BaseException as e:
out = "Error: " + str(e)
@@ -687,6 +687,7 @@ param_descriptions = {
'amount': 'Amount to be sent (in BTC). Type \'!\' to send the maximum available.',
'requested_amount': 'Requested amount (in BTC).',
'outputs': 'list of ["address", amount]',
+ 'redeem_script': 'redeem script (hexadecimal)',
}
command_options = {
diff --git a/lib/keystore.py b/lib/keystore.py
@@ -141,24 +141,22 @@ class Imported_KeyStore(Software_KeyStore):
pubkey = list(self.keypairs.keys())[0]
self.get_private_key(pubkey, password)
- def import_key(self, sec, password):
- try:
- pubkey = public_key_from_private_key(sec)
- except Exception:
- raise BaseException('Invalid private key')
- # allow overwrite
+ def import_privkey(self, sec, password):
+ txin_type, privkey, compressed = deserialize_privkey(sec)
+ pubkey = public_key_from_private_key(privkey, compressed)
self.keypairs[pubkey] = pw_encode(sec, password)
- return pubkey
+ return txin_type, pubkey
def delete_imported_key(self, key):
self.keypairs.pop(key)
def get_private_key(self, pubkey, password):
- pk = pw_decode(self.keypairs[pubkey], password)
+ sec = pw_decode(self.keypairs[pubkey], password)
+ txin_type, privkey, compressed = deserialize_privkey(sec)
# this checks the password
- if pubkey != public_key_from_private_key(pk):
+ if pubkey != public_key_from_private_key(privkey, compressed):
raise InvalidPassword()
- return pk
+ return privkey
def get_pubkey_derivation(self, x_pubkey):
if x_pubkey[0:2] in ['02', '03', '04']:
@@ -180,8 +178,6 @@ class Imported_KeyStore(Software_KeyStore):
c = pw_encode(b, new_password)
self.keypairs[k] = c
- def txin_type(self):
- return 'p2pkh'
class Deterministic_KeyStore(Software_KeyStore):
@@ -277,17 +273,6 @@ class Xpub:
return
return derivation
- def txin_type(self):
- xtype = deserialize_xpub(self.xpub)[0]
- if xtype == 'standard':
- return 'p2pkh'
- elif xtype == 'segwit':
- return 'p2wpkh'
- elif xtype == 'segwit_p2sh':
- return 'p2wpkh-p2sh'
- else:
- raise BaseException('unknown txin_type', xtype)
-
class BIP32_KeyStore(Deterministic_KeyStore, Xpub):
@@ -411,11 +396,6 @@ class Old_KeyStore(Deterministic_KeyStore):
def get_sequence(self, mpk, for_change, n):
return string_to_number(Hash(("%d:%d:"%(n, for_change)).encode('ascii') + bfh(mpk)))
- def get_address(self, for_change, n):
- pubkey = self.get_pubkey(for_change, n)
- address = public_key_to_p2pkh(bfh(pubkey))
- return address
-
@classmethod
def get_pubkey_from_mpk(self, mpk, for_change, n):
z = self.get_sequence(mpk, for_change, n)
@@ -431,8 +411,7 @@ class Old_KeyStore(Deterministic_KeyStore):
order = generator_secp256k1.order()
secexp = (secexp + self.get_sequence(self.mpk, for_change, n)) % order
pk = number_to_string(secexp, generator_secp256k1.order())
- compressed = False
- return SecretToASecret(pk, compressed)
+ return pk
def get_private_key(self, sequence, password):
seed = self.get_hex_seed(password)
@@ -491,8 +470,6 @@ class Old_KeyStore(Deterministic_KeyStore):
decoded = pw_decode(self.seed, old_password)
self.seed = pw_encode(decoded, new_password)
- def txin_type(self):
- return 'p2phk'
class Hardware_KeyStore(KeyStore, Xpub):
@@ -692,7 +669,7 @@ def is_private_key_list(text):
is_mpk = lambda x: is_old_mpk(x) or is_xpub(x)
is_private = lambda x: is_seed(x) or is_xprv(x) or is_private_key_list(x)
-is_any_key = lambda x: is_old_mpk(x) or is_xprv(x) or is_xpub(x) or is_private_key_list(x)
+is_master_key = lambda x: is_old_mpk(x) or is_xprv(x) or is_xpub(x)
is_private_key = lambda x: is_xprv(x) or is_private_key_list(x)
is_bip32_key = lambda x: is_xprv(x) or is_xpub(x)
@@ -740,15 +717,13 @@ def from_xprv(xprv):
k.xpub = xpub
return k
-def from_keys(text):
+def from_master_key(text):
if is_xprv(text):
k = from_xprv(text)
elif is_old_mpk(text):
k = from_old_mpk(text)
elif is_xpub(text):
k = from_xpub(text)
- elif is_private_key_list(text):
- k = from_private_key_list(text)
else:
raise BaseException('Invalid key')
return k
diff --git a/lib/transaction.py b/lib/transaction.py
@@ -890,7 +890,8 @@ class Transaction:
if x_pubkey in keypairs.keys():
print_error("adding signature for", x_pubkey)
sec = keypairs.get(x_pubkey)
- pubkey = public_key_from_private_key(sec)
+ compressed = True
+ pubkey = public_key_from_private_key(sec, compressed)
# add signature
pre_hash = Hash(bfh(self.serialize_preimage(i)))
pkey = regenerate_key(sec)
diff --git a/lib/util.py b/lib/util.py
@@ -40,6 +40,10 @@ from .i18n import _
import urllib.request, urllib.parse, urllib.error
import queue
+def inv_dict(d):
+ return {v: k for k, v in d.items()}
+
+
base_units = {'BTC':8, 'mBTC':5, 'uBTC':2}
fee_levels = [_('Within 25 blocks'), _('Within 10 blocks'), _('Within 5 blocks'), _('Within 2 blocks'), _('In the next block')]
diff --git a/lib/wallet.py b/lib/wallet.py
@@ -262,22 +262,24 @@ class Abstract_Wallet(PrintError):
return address in self.change_addresses
def get_address_index(self, address):
- if self.keystore.can_import():
- for pubkey in self.keystore.keypairs.keys():
- if self.pubkeys_to_address(pubkey) == address:
- return pubkey
- elif address in self.receiving_addresses:
+ if address in self.receiving_addresses:
return False, self.receiving_addresses.index(address)
if address in self.change_addresses:
return True, self.change_addresses.index(address)
raise Exception("Address not found", address)
- def get_private_key(self, address, password):
+ def export_private_key(self, address, password):
+ """ extended WIF format """
if self.is_watching_only():
return []
index = self.get_address_index(address)
pk = self.keystore.get_private_key(index, password)
- return [pk]
+ if self.txin_type in ['p2sh', 'p2wsh', 'p2wsh-p2sh']:
+ pubkeys = self.get_public_keys(address)
+ redeem_script = self.pubkeys_to_redeem_script(pubkeys)
+ else:
+ redeem_script = None
+ return bitcoin.serialize_privkey(pk, True, self.txin_type), redeem_script
def get_public_key(self, address):
if self.keystore.can_import():
@@ -1343,28 +1345,41 @@ class Imported_Wallet(Abstract_Wallet):
def __init__(self, storage):
Abstract_Wallet.__init__(self, storage)
+ def is_watching_only(self):
+ return self.keystore is None
+
+ def get_keystores(self):
+ return [self.keystore] if self.keystore else []
+
+ def check_password(self, password):
+ self.keystore.check_password(password)
+
+ def can_import_privkey(self):
+ return bool(self.keystore)
+
def load_keystore(self):
- pass
+ self.keystore = load_keystore(self.storage, 'keystore') if self.storage.get('keystore') else None
- def load_addresses(self):
- self.addresses = self.storage.get('addresses', [])
- self.receiving_addresses = self.addresses
- self.change_addresses = []
+ def save_keystore(self):
+ self.storage.put('keystore', self.keystore.dump())
- def get_keystores(self):
- return []
+ def load_addresses(self):
+ self.addresses = self.storage.get('addresses', {})
+ # convert list
+ if type(self.addresses) is list:
+ self.addresses = dict([(x, None) for x in self.addresses])
- def has_password(self):
- return False
+ def save_addresses(self):
+ self.storage.put('addresses', self.addresses)
def can_change_password(self):
- return False
+ return not self.is_watching_only()
def can_import_address(self):
- return True
+ return self.is_watching_only()
- def is_watching_only(self):
- return True
+ def can_delete_address(self):
+ return self.is_watching_only()
def has_seed(self):
return False
@@ -1375,6 +1390,9 @@ class Imported_Wallet(Abstract_Wallet):
def is_used(self, address):
return False
+ def is_change(self, address):
+ return False
+
def get_master_public_keys(self):
return []
@@ -1385,38 +1403,84 @@ class Imported_Wallet(Abstract_Wallet):
return ''
def get_addresses(self, include_change=False):
- return self.addresses
+ return sorted(self.addresses.keys())
+
+ def get_receiving_addresses(self):
+ return self.get_addresses()
+
+ def get_change_addresses(self):
+ return []
def import_address(self, address):
if address in self.addresses:
- return
- self.addresses.append(address)
+ return ''
+ self.addresses[address] = {}
self.storage.put('addresses', self.addresses)
self.storage.write()
self.add_address(address)
return address
- def can_delete_address(self):
- return True
-
def delete_address(self, address):
if address not in self.addresses:
return
- self.addresses.remove(address)
+ self.addresses.pop(address)
self.storage.put('addresses', self.addresses)
self.storage.write()
- def get_receiving_addresses(self):
- return self.addresses[:]
+ def get_address_index(self, address):
+ if self.keystore.can_import():
+ return self.addresses[address]['pubkey']
+
+ def import_private_key(self, sec, pw, redeem_script=None):
+ try:
+ txin_type, pubkey = self.keystore.import_privkey(sec, pw)
+ except Exception:
+ raise BaseException('Invalid private key', sec)
+ if txin_type in ['p2pkh', 'p2wpkh', 'p2wpkh-p2sh']:
+ if redeem_script is not None:
+ raise BaseException('Cannot use redeem script with', txin_type, sec)
+ addr = bitcoin.pubkey_to_address(txin_type, pubkey)
+ elif txin_type in ['p2sh', 'p2wsh', 'p2wsh-p2sh']:
+ if redeem_script is None:
+ raise BaseException('Redeem script required for', txin_type, sec)
+ addr = bitcoin.redeem_script_to_address(txin_type, redeem_script)
+ else:
+ raise NotImplementedError(self.txin_type)
+ self.addresses[addr] = {'type':txin_type, 'pubkey':pubkey, 'redeem_script':redeem_script}
+ self.save_keystore()
+ self.save_addresses()
+ self.storage.write()
+ self.add_address(addr)
+ return addr
- def get_change_addresses(self):
- return []
+ def export_private_key(self, address, password):
+ txin_type, pubkey, redeem_script = self.addresses[address]
+ sec = pw_decode(self.keystore.keypairs[pubkey], password)
+ return sec, redeem_script
def add_input_sig_info(self, txin, address):
- addrtype, hash160 = b58_address_to_hash160(address)
- x_pubkey = 'fd' + bh2u(bytes([addrtype]) + hash160)
- txin['x_pubkeys'] = [x_pubkey]
- txin['signatures'] = [None]
+ if self.is_watching_only():
+ addrtype, hash160 = b58_address_to_hash160(address)
+ x_pubkey = 'fd' + bh2u(bytes([addrtype]) + hash160)
+ txin['x_pubkeys'] = [x_pubkey]
+ txin['signatures'] = [None]
+ return
+
+ txin_type = self.addresses[address]['txin_type']
+ txin['type'] = txin_type
+ if txin_type in ['p2pkh', 'p2wkh', 'p2wkh-p2sh']:
+ pubkey = self.addresses[address]['pubkey']
+ txin['num_sig'] = 1
+ txin['x_pubkeys'] = [pubkey]
+ txin['signatures'] = [None]
+ else:
+ redeem_script = self.addresses[address]['redeem_script']
+ num_sig = 2
+ num_keys = 3
+ txin['num_sig'] = num_sig
+ txin['redeem_script'] = redeem_script
+ txin['signatures'] = [None] * num_keys
+
class Deterministic_Wallet(Abstract_Wallet):
@@ -1544,7 +1608,18 @@ class Simple_Wallet(Abstract_Wallet):
def load_keystore(self):
self.keystore = load_keystore(self.storage, 'keystore')
- self.txin_type = self.keystore.txin_type()
+ try:
+ xtype = deserialize_xpub(self.keystore.xpub)[0]
+ except:
+ xtype = 'standard'
+ 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)
@@ -1623,15 +1698,6 @@ class Simple_Deterministic_Wallet(Deterministic_Wallet, Simple_Wallet):
def can_import_privkey(self):
return self.keystore.can_import()
- def import_key(self, pk, pw):
- pubkey = self.keystore.import_key(pk, pw)
- self.save_keystore()
- addr = self.pubkeys_to_address(pubkey)
- self.receiving_addresses.append(addr)
- self.save_addresses()
- self.storage.write()
- self.add_address(addr)
- return addr
@@ -1639,16 +1705,7 @@ class Standard_Wallet(Simple_Deterministic_Wallet):
wallet_type = 'standard'
def pubkeys_to_address(self, pubkey):
- if self.txin_type == 'p2pkh':
- return bitcoin.public_key_to_p2pkh(bfh(pubkey))
- elif self.txin_type == 'p2wpkh':
- return bitcoin.hash_to_segwit_addr(hash_160(bfh(pubkey)))
- elif self.txin_type == 'p2wpkh-p2sh':
- scriptSig = transaction.p2wpkh_nested_script(pubkey)
- return bitcoin.hash160_to_p2sh(hash_160(bfh(scriptSig)))
- else:
- raise NotImplementedError(self.txin_type)
-
+ return bitcoin.pubkey_to_address(self.txin_type, pubkey)
class Multisig_Wallet(Deterministic_Wallet):
# generic m of n
@@ -1663,18 +1720,8 @@ class Multisig_Wallet(Deterministic_Wallet):
return self.derive_pubkeys(c, i)
def pubkeys_to_address(self, pubkeys):
- if self.txin_type == 'p2sh':
- redeem_script = self.pubkeys_to_redeem_script(pubkeys)
- return bitcoin.hash160_to_p2sh(hash_160(bfh(redeem_script)))
- elif self.txin_type == 'p2wsh':
- witness_script = self.pubkeys_to_redeem_script(pubkeys)
- return bitcoin.script_to_p2wsh(witness_script)
- elif self.txin_type == 'p2wsh-p2sh':
- witness_script = self.pubkeys_to_redeem_script(pubkeys)
- scriptSig = transaction.p2wsh_nested_script(witness_script)
- return bitcoin.hash160_to_p2sh(hash_160(bfh(scriptSig)))
- else:
- raise NotImplementedError(self.txin_type)
+ redeem_script = self.pubkeys_to_redeem_script(pubkeys)
+ return bitcoin.redeem_script_to_address(self.txin_type, redeem_script)
def pubkeys_to_redeem_script(self, pubkeys):
return transaction.multisig_script(sorted(pubkeys), self.m)
@@ -1688,7 +1735,15 @@ class Multisig_Wallet(Deterministic_Wallet):
name = 'x%d/'%(i+1)
self.keystores[name] = load_keystore(self.storage, name)
self.keystore = self.keystores['x1/']
- self.txin_type = self.keystore.txin_type()
+ 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'
+ else:
+ raise BaseException('unknown txin_type', xtype)
def save_keystore(self):
for name, k in self.keystores.items():