electrum

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

commit 8a200aaf33711c728665b6cfb0c519d05c419848
parent b97402b796497626812b9fdf1106ed2d61ac9171
Author: ThomasV <thomasv@electrum.org>
Date:   Thu, 23 Nov 2017 09:27:16 +0100

Merge pull request #3354 from benma/dbb_segwit

digitalbitbox: add support for segwit
Diffstat:
Mplugins/digitalbitbox/digitalbitbox.py | 67+++++++++++++++++++++++++++++++++++++++++++------------------------
Mplugins/digitalbitbox/qt.py | 3+++
2 files changed, 46 insertions(+), 24 deletions(-)

diff --git a/plugins/digitalbitbox/digitalbitbox.py b/plugins/digitalbitbox/digitalbitbox.py @@ -6,6 +6,7 @@ try: import electrum from electrum.bitcoin import TYPE_ADDRESS, push_script, var_int, msg_magic, Hash, verify_message, pubkey_from_signature, point_to_ser, public_key_to_p2pkh, EncodeAES, DecodeAES, MyVerifyingKey + from electrum.bitcoin import serialize_xpub, deserialize_xpub from electrum.transaction import Transaction from electrum.i18n import _ from electrum.keystore import Hardware_KeyStore @@ -85,10 +86,17 @@ class DigitalBitbox_Client(): def get_xpub(self, bip32_path, xtype): - assert xtype == 'standard' + assert xtype in ('standard', 'p2wpkh-p2sh') reply = self._get_xpub(bip32_path) if reply: - return reply['xpub'] + xpub = reply['xpub'] + # Change type of xpub to the requested type. The firmware + # only ever returns the standard type, but it is agnostic + # to the type when signing. + if xtype != 'standard': + _, depth, fingerprint, child_number, c, cK = deserialize_xpub(xpub) + xpub = serialize_xpub(xtype, c, cK, depth, fingerprint, child_number) + return xpub else: raise BaseException('no reply') @@ -399,6 +407,10 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): return str(self.derivation) + def is_p2pkh(self): + return self.derivation.startswith("m/44'/") + + def give_error(self, message, clear_client = False): if clear_client: self.client = None @@ -472,7 +484,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): return try: - p2shTransaction = False + p2pkhTransaction = True derivations = self.get_tx_derivations(tx) inputhasharray = [] hasharray = [] @@ -483,8 +495,8 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): if txin['type'] == 'coinbase': self.give_error("Coinbase not supported") # should never happen - if txin['type'] in ['p2sh']: - p2shTransaction = True + if txin['type'] != 'p2pkh': + p2pkhTransaction = False for x_pubkey in txin['x_pubkeys']: if x_pubkey in derivations: @@ -498,12 +510,6 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): else: self.give_error("No matching x_key for sign_transaction") # should never happen - # Sanity check - if p2shTransaction: - for txinput in tx.inputs(): - if txinput['type'] != 'p2sh': - self.give_error("P2SH / regular input mixed in same transaction not supported") # should never happen - # Build pubkeyarray from outputs for _type, address, amount in tx.outputs(): assert _type == TYPE_ADDRESS @@ -517,15 +523,22 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): # Special serialization of the unsigned transaction for # the mobile verification app. - class CustomTXSerialization(Transaction): - @classmethod - def input_script(self, txin, estimate_size=False): - if txin['type'] == 'p2pkh': - return Transaction.get_preimage_script(txin) - if txin['type'] == 'p2sh': - return '00' + push_script(Transaction.get_preimage_script(txin)) - raise Exception("unsupported type %s" % txin['type']) - tx_dbb_serialized = CustomTXSerialization(tx.serialize()).serialize() + # At the moment, verification only works for p2pkh transactions. + if p2pkhTransaction: + class CustomTXSerialization(Transaction): + @classmethod + def input_script(self, txin, estimate_size=False): + if txin['type'] == 'p2pkh': + return Transaction.get_preimage_script(txin) + if txin['type'] == 'p2sh': + # Multisig verification has partial support, but is disabled. This is the + # expected serialization though, so we leave it here until we activate it. + return '00' + push_script(Transaction.get_preimage_script(txin)) + raise Exception("unsupported type %s" % txin['type']) + tx_dbb_serialized = CustomTXSerialization(tx.serialize()).serialize() + else: + # We only need this for the signing echo / verification. + tx_dbb_serialized = None # Build sign command dbb_signatures = [] @@ -533,8 +546,15 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): for step in range(int(steps)): hashes = hasharray[step * self.maxInputs : (step + 1) * self.maxInputs] - msg = ('{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \ - (to_hexstr(Hash(tx_dbb_serialized)), json.dumps(hashes), json.dumps(pubkeyarray))).encode('utf8') + msg = { + "sign": { + "data": hashes, + "checkpub": pubkeyarray, + }, + } + if tx_dbb_serialized is not None: + msg["sign"]["meta"] = to_hexstr(Hash(tx_dbb_serialized)) + msg = json.dumps(msg).encode('ascii') dbb_client = self.plugin.get_client(self) if not dbb_client.is_paired(): @@ -547,8 +567,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore): if 'echo' not in reply: raise Exception("Could not sign transaction.") - # multisig verification not working correctly yet - if self.plugin.is_mobile_paired() and not p2shTransaction: + if self.plugin.is_mobile_paired() and tx_dbb_serialized is not None: reply['tx'] = tx_dbb_serialized self.plugin.comserver_post_notification(reply) diff --git a/plugins/digitalbitbox/qt.py b/plugins/digitalbitbox/qt.py @@ -25,6 +25,9 @@ class Plugin(DigitalBitboxPlugin, QtPluginBase): if not self.is_mobile_paired(): return + if not keystore.is_p2pkh(): + return + if len(addrs) == 1: def show_address(): change, index = wallet.get_address_index(addrs[0])