Extend Wallet Import Format with txin type. Extend class Imported_Wallet.

This commit is contained in:
ThomasV 2017-09-25 21:35:14 +02:00
parent 4864c802dd
commit e8b564c0e7
10 changed files with 243 additions and 161 deletions

View File

@ -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:

View File

@ -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()

View File

@ -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

View File

@ -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)
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_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)
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):

View File

@ -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
SCRIPT_TYPES = {
'p2pkh':0,
'p2wpkh':1,
'p2wpkh-p2sh':2,
'p2sh':5,
'p2wsh':6,
'p2wsh-p2sh':7
}
def SecretToASecret(secret, compressed=False):
addrtype = ADDRTYPE_P2PKH
vchIn = bytes([(addrtype+128)&255]) + secret
if compressed: vchIn += b'\01'
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

View File

@ -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 = {

View File

@ -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

View File

@ -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)

View File

@ -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')]

View File

@ -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 load_keystore(self):
pass
def load_addresses(self):
self.addresses = self.storage.get('addresses', [])
self.receiving_addresses = self.addresses
self.change_addresses = []
def is_watching_only(self):
return self.keystore is None
def get_keystores(self):
return []
return [self.keystore] if self.keystore else []
def has_password(self):
return False
def check_password(self, password):
self.keystore.check_password(password)
def can_import_privkey(self):
return bool(self.keystore)
def load_keystore(self):
self.keystore = load_keystore(self.storage, 'keystore') if self.storage.get('keystore') else None
def save_keystore(self):
self.storage.put('keystore', self.keystore.dump())
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 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 get_change_addresses(self):
return []
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 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():