electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit e299df7b82f626f669098ccc0dbe041934d72d86
parent 0bc53d34d16b891af78b6ebf86722100789c0ac2
Author: ThomasV <thomasv@electrum.org>
Date:   Thu,  5 Oct 2017 14:44:44 +0200

add message signing/decryption for segwit addresses

Diffstat:
Mgui/qt/main_window.py | 8+++-----
Mlib/bitcoin.py | 10++++++----
Mlib/keystore.py | 17+++++++----------
Mlib/wallet.py | 80++++++++++++++++++++++++++++++++++++++-----------------------------------------
4 files changed, 54 insertions(+), 61 deletions(-)

diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py @@ -1898,7 +1898,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): if not bitcoin.is_address(address): self.show_message('Invalid Bitcoin address.') return - if not bitcoin.is_p2pkh(address): + txin_type = self.wallet.get_txin_type(address) + if txin_type not in ['p2pkh', 'p2wpkh', 'p2wpkh-p2sh']: self.show_message('Cannot sign messages with this type of address.' + '\n\n' + self.msg_sign) return if not self.wallet.is_mine(address): @@ -1916,9 +1917,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): if not bitcoin.is_address(address): self.show_message('Invalid Bitcoin address.') return - if not bitcoin.is_p2pkh(address): - self.show_message('Cannot verify messages with this type of address.' + '\n\n' + self.msg_sign) - return try: # This can throw on invalid base64 sig = base64.b64decode(str(signature.toPlainText())) @@ -1932,7 +1930,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def sign_verify_message(self, address=''): d = WindowModalDialog(self, _('Sign/verify Message')) - d.setMinimumSize(410, 290) + d.setMinimumSize(610, 290) layout = QGridLayout(d) diff --git a/lib/bitcoin.py b/lib/bitcoin.py @@ -536,8 +536,7 @@ def public_key_from_private_key(pk, compressed): def address_from_private_key(sec): txin_type, privkey, compressed = deserialize_privkey(sec) public_key = public_key_from_private_key(privkey, compressed) - address = pubkey_to_address(txin_type, public_key) - return address + return pubkey_to_address(txin_type, public_key) def is_segwit_address(addr): witver, witprog = segwit_addr.decode(SEGWIT_HRP, addr) @@ -607,8 +606,11 @@ def verify_message(address, sig, message): public_key, compressed = pubkey_from_signature(sig, h) # check public key using the address pubkey = point_to_ser(public_key.pubkey.point, compressed) - addr = public_key_to_p2pkh(pubkey) - if address != addr: + for txin_type in ['p2pkh','p2wpkh','p2wpkh-p2sh']: + addr = pubkey_to_address(txin_type, bh2u(pubkey)) + if address == addr: + break + else: raise Exception("Bad signature") # check message public_key.verify_digest(sig[1:], h, sigdecode = ecdsa.util.sigdecode_string) diff --git a/lib/keystore.py b/lib/keystore.py @@ -87,14 +87,13 @@ class Software_KeyStore(KeyStore): return not self.is_watching_only() def sign_message(self, sequence, message, password): - privkey = self.get_private_key(sequence, password) - compressed = self.use_compressed_pubkeys + privkey, compressed = self.get_private_key(sequence, password) key = regenerate_key(privkey) return key.sign_message(message, compressed) def decrypt_message(self, sequence, message, password): - sec = self.get_private_key(sequence, password) - ec = regenerate_key(sec) + privkey, compressed = self.get_private_key(sequence, password) + ec = regenerate_key(privkey) decrypted = ec.decrypt_message(message) return decrypted @@ -106,7 +105,7 @@ class Software_KeyStore(KeyStore): # Add private keys keypairs = self.get_tx_derivations(tx) for k, v in keypairs.items(): - keypairs[k] = self.get_private_key(v, password) + keypairs[k] = self.get_private_key(v, password)[0] # Sign if keypairs: tx.sign(keypairs) @@ -156,7 +155,7 @@ class Imported_KeyStore(Software_KeyStore): # this checks the password if pubkey != public_key_from_private_key(privkey, compressed): raise InvalidPassword() - return privkey + return privkey, compressed def get_pubkey_derivation(self, x_pubkey): if x_pubkey[0:2] in ['02', '03', '04']: @@ -279,7 +278,6 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub): def __init__(self, d): Xpub.__init__(self) Deterministic_KeyStore.__init__(self, d) - self.use_compressed_pubkeys = True self.xpub = d.get('xpub') self.xprv = d.get('xprv') @@ -331,7 +329,7 @@ class BIP32_KeyStore(Deterministic_KeyStore, Xpub): xprv = self.get_master_private_key(password) _, _, _, _, c, k = deserialize_xprv(xprv) pk = bip32_private_key(sequence, k, c) - return pk + return pk, True @@ -340,7 +338,6 @@ class Old_KeyStore(Deterministic_KeyStore): def __init__(self, d): Deterministic_KeyStore.__init__(self, d) self.mpk = d.get('mpk') - self.use_compressed_pubkeys = False def get_hex_seed(self, password): return pw_decode(self.seed, password).encode('utf8') @@ -421,7 +418,7 @@ class Old_KeyStore(Deterministic_KeyStore): for_change, n = sequence secexp = self.stretch_key(seed) pk = self.get_private_key_from_stretched_exponent(for_change, n, secexp) - return pk + return pk, False def check_seed(self, seed): secexp = self.stretch_key(seed) diff --git a/lib/wallet.py b/lib/wallet.py @@ -273,8 +273,7 @@ class Abstract_Wallet(PrintError): if self.is_watching_only(): return [] index = self.get_address_index(address) - pk = self.keystore.get_private_key(index, password) - compressed = self.keystore.use_compressed_pubkeys + 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) @@ -282,13 +281,6 @@ class Abstract_Wallet(PrintError): redeem_script = None return bitcoin.serialize_privkey(pk, compressed, self.txin_type), redeem_script - def get_public_key(self, address): - if self.keystore.can_import(): - pubkey = self.get_address_index(address) - else: - sequence = self.get_address_index(address) - pubkey = self.get_pubkey(*sequence) - return pubkey def get_public_keys(self, address): sequence = self.get_address_index(address) @@ -1336,6 +1328,15 @@ class Abstract_Wallet(PrintError): def has_password(self): return self.storage.get('use_encryption', False) + def sign_message(self, address, message, password): + index = self.get_address_index(address) + return self.keystore.sign_message(index, message, password) + + def decrypt_message(self, pubkey, message, password): + addr = self.pubkeys_to_address(pubkey) + index = self.get_address_index(addr) + return self.keystore.decrypt_message(index, message, password) + class Imported_Wallet(Abstract_Wallet): # wallet made of imported addresses @@ -1429,8 +1430,10 @@ class Imported_Wallet(Abstract_Wallet): self.storage.write() def get_address_index(self, address): - if self.keystore.can_import(): - return self.addresses[address]['pubkey'] + return self.get_public_key(address) + + def get_public_key(self, address): + return self.addresses[address].get('pubkey') def import_private_key(self, sec, pw, redeem_script=None): try: @@ -1461,7 +1464,11 @@ class Imported_Wallet(Abstract_Wallet): sec = pw_decode(self.keystore.keypairs[pubkey], password) return sec, redeem_script + def get_txin_type(self, address): + return self.addresses[address].get('type', 'address') + def add_input_sig_info(self, txin, address): + txin['type'] = self.get_txin_type(address) if self.is_watching_only(): addrtype, hash160 = b58_address_to_hash160(address) x_pubkey = 'fd' + bh2u(bytes([addrtype]) + hash160) @@ -1469,8 +1476,6 @@ class Imported_Wallet(Abstract_Wallet): txin['signatures'] = [None] return - txin_type = self.addresses[address]['type'] - txin['type'] = txin_type if txin_type in ['p2pkh', 'p2wkh', 'p2wkh-p2sh']: pubkey = self.addresses[address]['pubkey'] txin['num_sig'] = 1 @@ -1484,7 +1489,10 @@ class Imported_Wallet(Abstract_Wallet): txin['redeem_script'] = redeem_script txin['signatures'] = [None] * num_keys - + def pubkeys_to_address(self, pubkey): + for addr, v in self.addresses.items(): + if v.get('pubkey') == pubkey: + return addr class Deterministic_Wallet(Abstract_Wallet): @@ -1604,10 +1612,21 @@ class Deterministic_Wallet(Abstract_Wallet): def get_fingerprint(self): return self.get_master_public_key() + def get_txin_type(self, address): + return self.txin_type + -class Simple_Wallet(Abstract_Wallet): +class Simple_Deterministic_Wallet(Deterministic_Wallet): - """ Wallet with a single pubkey per address """ + """ Deterministic Wallet with a single pubkey per address """ + + def __init__(self, storage): + Deterministic_Wallet.__init__(self, storage) + + def get_public_key(self, address): + sequence = self.get_address_index(address) + pubkey = self.get_pubkey(*sequence) + return pubkey def load_keystore(self): self.keystore = load_keystore(self.storage, 'keystore') @@ -1631,30 +1650,12 @@ class Simple_Wallet(Abstract_Wallet): return [self.get_public_key(address)] def add_input_sig_info(self, txin, address): - if not self.keystore.can_import(): - derivation = self.get_address_index(address) - x_pubkey = self.keystore.get_xpubkey(*derivation) - else: - x_pubkey = self.get_public_key(address) + derivation = self.get_address_index(address) + x_pubkey = self.keystore.get_xpubkey(*derivation) txin['x_pubkeys'] = [x_pubkey] txin['signatures'] = [None] txin['num_sig'] = 1 - def sign_message(self, address, message, password): - index = self.get_address_index(address) - return self.keystore.sign_message(index, message, password) - - def decrypt_message(self, pubkey, message, password): - addr = self.pubkeys_to_address(pubkey) - index = self.get_address_index(addr) - return self.keystore.decrypt_message(index, message, password) - - -class Simple_Deterministic_Wallet(Deterministic_Wallet, Simple_Wallet): - - def __init__(self, storage): - Deterministic_Wallet.__init__(self, storage) - def get_master_public_key(self): return self.keystore.get_master_public_key() @@ -1687,9 +1688,6 @@ class Simple_Deterministic_Wallet(Deterministic_Wallet, Simple_Wallet): def save_keystore(self): self.storage.put('keystore', self.keystore.dump()) - def can_delete_address(self): - return self.keystore.can_import() - def delete_address(self, address): pubkey = self.get_public_key(address) self.keystore.delete_imported_key(pubkey) @@ -1698,9 +1696,6 @@ class Simple_Deterministic_Wallet(Deterministic_Wallet, Simple_Wallet): self.save_addresses() self.storage.write() - def can_import_privkey(self): - return self.keystore.can_import() - @@ -1710,6 +1705,7 @@ class Standard_Wallet(Simple_Deterministic_Wallet): def pubkeys_to_address(self, pubkey): return bitcoin.pubkey_to_address(self.txin_type, pubkey) + class Multisig_Wallet(Deterministic_Wallet): # generic m of n gap_limit = 20