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:
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])