URI: 
       tExtend Wallet Import Format with txin type. Extend class Imported_Wallet. - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit e8b564c0e728e1ca15d39f48a0153c984e2acc3c
   DIR parent 4864c802dd0b82c294d4fd1858047990eb463d1e
  HTML 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:
         M electrum                            |      12 ++++++++++--
         M gui/qt/main_window.py               |      12 ++++++++----
         M lib/__init__.py                     |       2 +-
         M lib/base_wizard.py                  |      42 ++++++++++++++++++-------------
         M lib/bitcoin.py                      |      86 ++++++++++++++++++++-----------
         M lib/commands.py                     |       5 +++--
         M lib/keystore.py                     |      47 ++++++++-----------------------
         M lib/transaction.py                  |       3 ++-
         M lib/util.py                         |       4 ++++
         M lib/wallet.py                       |     191 ++++++++++++++++++++-----------
       
       10 files changed, 243 insertions(+), 161 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -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:
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -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_()
       t@@ -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()
   DIR diff --git a/lib/__init__.py b/lib/__init__.py
       t@@ -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
   DIR diff --git a/lib/base_wizard.py b/lib/base_wizard.py
       t@@ -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)
       t@@ -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):
       t@@ -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:
       t@@ -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):
       t@@ -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):
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -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):
       t@@ -65,7 +65,6 @@ XPUB_HEADERS = {
        
        # Bitcoin network constants
        TESTNET = False
       -NOLNET = False
        ADDRTYPE_P2PKH = 0
        ADDRTYPE_P2SH = 5
        SEGWIT_HRP = "bc"
       t@@ -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:
       t@@ -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):
       t@@ -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)
        
       t@@ -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
       t@@ -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
   DIR diff --git a/lib/commands.py b/lib/commands.py
       t@@ -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)
       t@@ -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 = {
   DIR diff --git a/lib/keystore.py b/lib/keystore.py
       t@@ -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']:
       t@@ -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):
       t@@ -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):
        
       t@@ -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)
       t@@ -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)
       t@@ -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):
       t@@ -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)
        
       t@@ -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
   DIR diff --git a/lib/transaction.py b/lib/transaction.py
       t@@ -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)
   DIR diff --git a/lib/util.py b/lib/util.py
       t@@ -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')]
        
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -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():
       t@@ -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
       t@@ -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 []
        
       t@@ -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):
       t@@ -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)
       t@@ -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
        
        
        
       t@@ -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
       t@@ -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)
       t@@ -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():