Merge pull request #3925 from SomberNight/wif_format_change
change WIF to "txin_type:old_wif"
This commit is contained in:
commit
381de43cac
|
@ -2094,8 +2094,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
rds_e = ShowQRTextEdit(text=redeem_script)
|
||||
rds_e.addCopyButton(self.app)
|
||||
vbox.addWidget(rds_e)
|
||||
if xtype in ['p2wpkh', 'p2wsh', 'p2wpkh-p2sh', 'p2wsh-p2sh']:
|
||||
vbox.addWidget(WWLabel(_("Warning: the format of private keys associated to segwit addresses may not be compatible with other wallets")))
|
||||
vbox.addLayout(Buttons(CloseButton(d)))
|
||||
d.setLayout(vbox)
|
||||
d.exec_()
|
||||
|
@ -2334,7 +2332,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
|
|||
_('It can not be "backed up" by simply exporting these private keys.'))
|
||||
|
||||
d = WindowModalDialog(self, _('Private keys'))
|
||||
d.setMinimumSize(850, 300)
|
||||
d.setMinimumSize(980, 300)
|
||||
vbox = QVBoxLayout(d)
|
||||
|
||||
msg = "%s\n%s\n%s" % (_("WARNING: ALL your private keys are secret."),
|
||||
|
|
|
@ -254,7 +254,7 @@ def line_dialog(parent, title, label, ok_label, default=None):
|
|||
def text_dialog(parent, title, label, ok_label, default=None, allow_multi=False):
|
||||
from .qrtextedit import ScanQRTextEdit
|
||||
dialog = WindowModalDialog(parent, title)
|
||||
dialog.setMinimumWidth(500)
|
||||
dialog.setMinimumWidth(600)
|
||||
l = QVBoxLayout()
|
||||
dialog.setLayout(l)
|
||||
l.addWidget(QLabel(label))
|
||||
|
|
|
@ -516,9 +516,8 @@ def DecodeBase58Check(psz):
|
|||
return key
|
||||
|
||||
|
||||
|
||||
# extended key export format for segwit
|
||||
|
||||
# backwards compat
|
||||
# extended WIF for segwit (used in 3.0.x; but still used internally)
|
||||
SCRIPT_TYPES = {
|
||||
'p2pkh':0,
|
||||
'p2wpkh':1,
|
||||
|
@ -529,26 +528,43 @@ SCRIPT_TYPES = {
|
|||
}
|
||||
|
||||
|
||||
def serialize_privkey(secret, compressed, txin_type):
|
||||
prefix = bytes([(SCRIPT_TYPES[txin_type]+NetworkConstants.WIF_PREFIX)&255])
|
||||
def serialize_privkey(secret, compressed, txin_type, internal_use=False):
|
||||
if internal_use:
|
||||
prefix = bytes([(SCRIPT_TYPES[txin_type] + NetworkConstants.WIF_PREFIX) & 255])
|
||||
else:
|
||||
prefix = bytes([NetworkConstants.WIF_PREFIX])
|
||||
suffix = b'\01' if compressed else b''
|
||||
vchIn = prefix + secret + suffix
|
||||
return EncodeBase58Check(vchIn)
|
||||
base58_wif = EncodeBase58Check(vchIn)
|
||||
if internal_use:
|
||||
return base58_wif
|
||||
else:
|
||||
return '{}:{}'.format(txin_type, base58_wif)
|
||||
|
||||
|
||||
def deserialize_privkey(key):
|
||||
# whether the pubkey is compressed should be visible from the keystore
|
||||
vch = DecodeBase58Check(key)
|
||||
if is_minikey(key):
|
||||
return 'p2pkh', minikey_to_private_key(key), True
|
||||
elif vch:
|
||||
txin_type = inv_dict(SCRIPT_TYPES)[vch[0] - NetworkConstants.WIF_PREFIX]
|
||||
assert len(vch) in [33, 34]
|
||||
compressed = len(vch) == 34
|
||||
return txin_type, vch[1:33], compressed
|
||||
else:
|
||||
|
||||
txin_type = None
|
||||
if ':' in key:
|
||||
txin_type, key = key.split(sep=':', maxsplit=1)
|
||||
assert txin_type in SCRIPT_TYPES
|
||||
vch = DecodeBase58Check(key)
|
||||
if not vch:
|
||||
raise BaseException("cannot deserialize", key)
|
||||
|
||||
if txin_type is None:
|
||||
# keys exported in version 3.0.x encoded script type in first byte
|
||||
txin_type = inv_dict(SCRIPT_TYPES)[vch[0] - NetworkConstants.WIF_PREFIX]
|
||||
else:
|
||||
assert vch[0] == NetworkConstants.WIF_PREFIX
|
||||
|
||||
assert len(vch) in [33, 34]
|
||||
compressed = len(vch) == 34
|
||||
return txin_type, vch[1:33], compressed
|
||||
|
||||
|
||||
def regenerate_key(pk):
|
||||
assert len(pk) == 32
|
||||
return EC_KEY(pk)
|
||||
|
|
|
@ -139,7 +139,10 @@ class Imported_KeyStore(Software_KeyStore):
|
|||
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)
|
||||
# re-serialize the key so the internal storage format is consistent
|
||||
serialized_privkey = serialize_privkey(
|
||||
privkey, compressed, txin_type, internal_use=True)
|
||||
self.keypairs[pubkey] = pw_encode(serialized_privkey, password)
|
||||
return txin_type, pubkey
|
||||
|
||||
def delete_imported_key(self, key):
|
||||
|
|
|
@ -271,6 +271,7 @@ class Test_keyImport(unittest.TestCase):
|
|||
|
||||
priv_pub_addr = (
|
||||
{'priv': 'KzMFjMC2MPadjvX5Cd7b8AKKjjpBSoRKUTpoAtN6B3J9ezWYyXS6',
|
||||
'exported_privkey': 'p2pkh:KzMFjMC2MPadjvX5Cd7b8AKKjjpBSoRKUTpoAtN6B3J9ezWYyXS6',
|
||||
'pub': '02c6467b7e621144105ed3e4835b0b4ab7e35266a2ae1c4f8baa19e9ca93452997',
|
||||
'address': '17azqT8T16coRmWKYFj3UjzJuxiYrYFRBR',
|
||||
'minikey' : False,
|
||||
|
@ -278,7 +279,17 @@ class Test_keyImport(unittest.TestCase):
|
|||
'compressed': True,
|
||||
'addr_encoding': 'base58',
|
||||
'scripthash': 'c9aecd1fef8d661a42c560bf75c8163e337099800b8face5ca3d1393a30508a7'},
|
||||
{'priv': 'p2pkh:Kzj8VjwpZ99bQqVeUiRXrKuX9mLr1o6sWxFMCBJn1umC38BMiQTD',
|
||||
'exported_privkey': 'p2pkh:Kzj8VjwpZ99bQqVeUiRXrKuX9mLr1o6sWxFMCBJn1umC38BMiQTD',
|
||||
'pub': '0352d78b4b37e0f6d4e164423436f2925fa57817467178eca550a88f2821973c41',
|
||||
'address': '1GXgZ5Qi6gmXTHVSpUPZLy4Ci2nbfb3ZNb',
|
||||
'minikey': False,
|
||||
'txin_type': 'p2pkh',
|
||||
'compressed': True,
|
||||
'addr_encoding': 'base58',
|
||||
'scripthash': 'a9b2a76fc196c553b352186dfcca81fcf323a721cd8431328f8e9d54216818c1'},
|
||||
{'priv': '5Hxn5C4SQuiV6e62A1MtZmbSeQyrLFhu5uYks62pU5VBUygK2KD',
|
||||
'exported_privkey': 'p2pkh:5Hxn5C4SQuiV6e62A1MtZmbSeQyrLFhu5uYks62pU5VBUygK2KD',
|
||||
'pub': '04e5fe91a20fac945845a5518450d23405ff3e3e1ce39827b47ee6d5db020a9075422d56a59195ada0035e4a52a238849f68e7a325ba5b2247013e0481c5c7cb3f',
|
||||
'address': '1GPHVTY8UD9my6jyP4tb2TYJwUbDetyNC6',
|
||||
'minikey': False,
|
||||
|
@ -286,7 +297,17 @@ class Test_keyImport(unittest.TestCase):
|
|||
'compressed': False,
|
||||
'addr_encoding': 'base58',
|
||||
'scripthash': 'f5914651408417e1166f725a5829ff9576d0dbf05237055bf13abd2af7f79473'},
|
||||
{'priv': 'p2pkh:5KhYQCe1xd5g2tqpmmGpUWDpDuTbA8vnpbiCNDwMPAx29WNQYfN',
|
||||
'exported_privkey': 'p2pkh:5KhYQCe1xd5g2tqpmmGpUWDpDuTbA8vnpbiCNDwMPAx29WNQYfN',
|
||||
'pub': '048f0431b0776e8210376c81280011c2b68be43194cb00bd47b7e9aa66284b713ce09556cde3fee606051a07613f3c159ef3953b8927c96ae3dae94a6ba4182e0e',
|
||||
'address': '147kiRHHm9fqeMQSgqf4k35XzuWLP9fmmS',
|
||||
'minikey': False,
|
||||
'txin_type': 'p2pkh',
|
||||
'compressed': False,
|
||||
'addr_encoding': 'base58',
|
||||
'scripthash': '6dd2e07ad2de9ba8eec4bbe8467eb53f8845acff0d9e6f5627391acc22ff62df'},
|
||||
{'priv': 'LHJnnvRzsdrTX2j5QeWVsaBkabK7gfMNqNNqxnbBVRaJYfk24iJz',
|
||||
'exported_privkey': 'p2wpkh-p2sh:Kz9XebiCXL2BZzhYJViiHDzn5iup1povWV8aqstzWU4sz1K5nVva',
|
||||
'pub': '0279ad237ca0d812fb503ab86f25e15ebd5fa5dd95c193639a8a738dcd1acbad81',
|
||||
'address': '3GeVJB3oKr7psgKR6BTXSxKtWUkfsHHhk7',
|
||||
'minikey': False,
|
||||
|
@ -294,7 +315,17 @@ class Test_keyImport(unittest.TestCase):
|
|||
'compressed': True,
|
||||
'addr_encoding': 'base58',
|
||||
'scripthash': 'd7b04e882fa6b13246829ac552a2b21461d9152eb00f0a6adb58457a3e63d7c5'},
|
||||
{'priv': 'p2wpkh-p2sh:L3CZH1pm87X4bbE6mSGvZnAZ1KcFDRomBudUkrkBG7EZhDtBVXMW',
|
||||
'exported_privkey': 'p2wpkh-p2sh:L3CZH1pm87X4bbE6mSGvZnAZ1KcFDRomBudUkrkBG7EZhDtBVXMW',
|
||||
'pub': '0229da20a15b3363b2c28e3c5093c180b56c439df0b968a970366bb1f38435361e',
|
||||
'address': '3C79goMwT7zSTjXnPoCg6VFGAnUpZAkyus',
|
||||
'minikey': False,
|
||||
'txin_type': 'p2wpkh-p2sh',
|
||||
'compressed': True,
|
||||
'addr_encoding': 'base58',
|
||||
'scripthash': '714bf6bfe1083e69539f40d4c7a7dca85d187471b35642e55f20d7e866494cf7'},
|
||||
{'priv': 'L8g5V8kFFeg2WbecahRSdobARbHz2w2STH9S8ePHVSY4fmia7Rsj',
|
||||
'exported_privkey': 'p2wpkh:Kz6SuyPM5VktY5dr2d2YqdVgBA6LCWkiHqXJaC3BzxnMPSUuYzmF',
|
||||
'pub': '03e9f948421aaa89415dc5f281a61b60dde12aae3181b3a76cd2d849b164fc6d0b',
|
||||
'address': 'bc1qqmpt7u5e9hfznljta5gnvhyvfd2kdd0r90hwue',
|
||||
'minikey': False,
|
||||
|
@ -302,8 +333,18 @@ class Test_keyImport(unittest.TestCase):
|
|||
'compressed': True,
|
||||
'addr_encoding': 'bech32',
|
||||
'scripthash': '1929acaaef3a208c715228e9f1ca0318e3a6b9394ab53c8d026137f847ecf97b'},
|
||||
{'priv': 'p2wpkh:KyDWy5WbjLA58Zesh1o8m3pADGdJ3v33DKk4m7h8BD5zDKDmDFwo',
|
||||
'exported_privkey': 'p2wpkh:KyDWy5WbjLA58Zesh1o8m3pADGdJ3v33DKk4m7h8BD5zDKDmDFwo',
|
||||
'pub': '038c57657171c1f73e34d5b3971d05867d50221ad94980f7e87cbc2344425e6a1e',
|
||||
'address': 'bc1qpakeeg4d9ydyjxd8paqrw4xy9htsg532xzxn50',
|
||||
'minikey': False,
|
||||
'txin_type': 'p2wpkh',
|
||||
'compressed': True,
|
||||
'addr_encoding': 'bech32',
|
||||
'scripthash': '242f02adde84ebb2a7dd778b2f3a81b3826f111da4d8960d826d7a4b816cb261'},
|
||||
# from http://bitscan.com/articles/security/spotlight-on-mini-private-keys
|
||||
{'priv': 'SzavMBLoXU6kDrqtUVmffv',
|
||||
'exported_privkey': 'p2pkh:L53fCHmQhbNp1B4JipfBtfeHZH7cAibzG9oK19XfiFzxHgAkz6JK',
|
||||
'pub': '02588d202afcc1ee4ab5254c7847ec25b9a135bbda0f2bc69ee1a714749fd77dc9',
|
||||
'address': '19GuvDvMMUZ8vq84wT79fvnvhMd5MnfTkR',
|
||||
'minikey': True,
|
||||
|
@ -344,6 +385,7 @@ class Test_keyImport(unittest.TestCase):
|
|||
def test_is_private_key(self):
|
||||
for priv_details in self.priv_pub_addr:
|
||||
self.assertTrue(is_private_key(priv_details['priv']))
|
||||
self.assertTrue(is_private_key(priv_details['exported_privkey']))
|
||||
self.assertFalse(is_private_key(priv_details['pub']))
|
||||
self.assertFalse(is_private_key(priv_details['address']))
|
||||
self.assertFalse(is_private_key("not a privkey"))
|
||||
|
@ -352,8 +394,7 @@ class Test_keyImport(unittest.TestCase):
|
|||
for priv_details in self.priv_pub_addr:
|
||||
txin_type, privkey, compressed = deserialize_privkey(priv_details['priv'])
|
||||
priv2 = serialize_privkey(privkey, compressed, txin_type)
|
||||
if not priv_details['minikey']:
|
||||
self.assertEqual(priv_details['priv'], priv2)
|
||||
self.assertEqual(priv_details['exported_privkey'], priv2)
|
||||
|
||||
def test_address_to_scripthash(self):
|
||||
for priv_details in self.priv_pub_addr:
|
||||
|
|
|
@ -397,23 +397,21 @@ class Abstract_Wallet(PrintError):
|
|||
def get_address_index(self, address):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_redeem_script(self, address):
|
||||
return None
|
||||
|
||||
def export_private_key(self, address, password):
|
||||
""" extended WIF format """
|
||||
if self.is_watching_only():
|
||||
return []
|
||||
index = self.get_address_index(address)
|
||||
pk, compressed = self.keystore.get_private_key(index, password)
|
||||
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, compressed, self.txin_type), redeem_script
|
||||
|
||||
txin_type = self.get_txin_type(address)
|
||||
redeem_script = self.get_redeem_script(address)
|
||||
serialized_privkey = bitcoin.serialize_privkey(pk, compressed, txin_type)
|
||||
return serialized_privkey, redeem_script
|
||||
|
||||
def get_public_keys(self, address):
|
||||
sequence = self.get_address_index(address)
|
||||
return self.get_pubkeys(*sequence)
|
||||
return [self.get_public_key(address)]
|
||||
|
||||
def add_unverified_tx(self, tx_hash, tx_height):
|
||||
if tx_height in (TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_UNCONF_PARENT) \
|
||||
|
@ -1912,12 +1910,10 @@ class Imported_Wallet(Simple_Wallet):
|
|||
self.add_address(addr)
|
||||
return addr
|
||||
|
||||
def export_private_key(self, address, password):
|
||||
def get_redeem_script(self, address):
|
||||
d = self.addresses[address]
|
||||
pubkey = d['pubkey']
|
||||
redeem_script = d['redeem_script']
|
||||
sec = pw_decode(self.keystore.keypairs[pubkey], password)
|
||||
return sec, redeem_script
|
||||
return redeem_script
|
||||
|
||||
def get_txin_type(self, address):
|
||||
return self.addresses[address].get('type', 'address')
|
||||
|
@ -2095,9 +2091,6 @@ class Simple_Deterministic_Wallet(Simple_Wallet, Deterministic_Wallet):
|
|||
def get_pubkey(self, c, i):
|
||||
return self.derive_pubkeys(c, i)
|
||||
|
||||
def get_public_keys(self, address):
|
||||
return [self.get_public_key(address)]
|
||||
|
||||
def add_input_sig_info(self, txin, address):
|
||||
derivation = self.get_address_index(address)
|
||||
x_pubkey = self.keystore.get_xpubkey(*derivation)
|
||||
|
@ -2135,6 +2128,10 @@ class Multisig_Wallet(Deterministic_Wallet):
|
|||
def get_pubkeys(self, c, i):
|
||||
return self.derive_pubkeys(c, i)
|
||||
|
||||
def get_public_keys(self, address):
|
||||
sequence = self.get_address_index(address)
|
||||
return self.get_pubkeys(*sequence)
|
||||
|
||||
def pubkeys_to_address(self, pubkeys):
|
||||
redeem_script = self.pubkeys_to_redeem_script(pubkeys)
|
||||
return bitcoin.redeem_script_to_address(self.txin_type, redeem_script)
|
||||
|
@ -2142,6 +2139,11 @@ class Multisig_Wallet(Deterministic_Wallet):
|
|||
def pubkeys_to_redeem_script(self, pubkeys):
|
||||
return transaction.multisig_script(sorted(pubkeys), self.m)
|
||||
|
||||
def get_redeem_script(self, address):
|
||||
pubkeys = self.get_public_keys(address)
|
||||
redeem_script = self.pubkeys_to_redeem_script(pubkeys)
|
||||
return redeem_script
|
||||
|
||||
def derive_pubkeys(self, c, i):
|
||||
return [k.derive_pubkey(c, i) for k in self.get_keystores()]
|
||||
|
||||
|
|
Loading…
Reference in New Issue