digitalbitbox: add support for segwit
This commit is contained in:
parent
0da1d4853b
commit
b31c51b3d0
|
@ -6,6 +6,7 @@
|
||||||
try:
|
try:
|
||||||
import electrum
|
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 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.transaction import Transaction
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.keystore import Hardware_KeyStore
|
from electrum.keystore import Hardware_KeyStore
|
||||||
|
@ -85,10 +86,17 @@ class DigitalBitbox_Client():
|
||||||
|
|
||||||
|
|
||||||
def get_xpub(self, bip32_path, xtype):
|
def get_xpub(self, bip32_path, xtype):
|
||||||
assert xtype == 'standard'
|
assert xtype in ('standard', 'p2wpkh-p2sh')
|
||||||
reply = self._get_xpub(bip32_path)
|
reply = self._get_xpub(bip32_path)
|
||||||
if reply:
|
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:
|
else:
|
||||||
raise BaseException('no reply')
|
raise BaseException('no reply')
|
||||||
|
|
||||||
|
@ -399,6 +407,10 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
||||||
return str(self.derivation)
|
return str(self.derivation)
|
||||||
|
|
||||||
|
|
||||||
|
def is_p2pkh(self):
|
||||||
|
return self.derivation.startswith("m/44'/")
|
||||||
|
|
||||||
|
|
||||||
def give_error(self, message, clear_client = False):
|
def give_error(self, message, clear_client = False):
|
||||||
if clear_client:
|
if clear_client:
|
||||||
self.client = None
|
self.client = None
|
||||||
|
@ -472,7 +484,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
p2shTransaction = False
|
p2pkhTransaction = True
|
||||||
derivations = self.get_tx_derivations(tx)
|
derivations = self.get_tx_derivations(tx)
|
||||||
inputhasharray = []
|
inputhasharray = []
|
||||||
hasharray = []
|
hasharray = []
|
||||||
|
@ -483,8 +495,8 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
||||||
if txin['type'] == 'coinbase':
|
if txin['type'] == 'coinbase':
|
||||||
self.give_error("Coinbase not supported") # should never happen
|
self.give_error("Coinbase not supported") # should never happen
|
||||||
|
|
||||||
if txin['type'] in ['p2sh']:
|
if txin['type'] != 'p2pkh':
|
||||||
p2shTransaction = True
|
p2pkhTransaction = False
|
||||||
|
|
||||||
for x_pubkey in txin['x_pubkeys']:
|
for x_pubkey in txin['x_pubkeys']:
|
||||||
if x_pubkey in derivations:
|
if x_pubkey in derivations:
|
||||||
|
@ -498,12 +510,6 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
||||||
else:
|
else:
|
||||||
self.give_error("No matching x_key for sign_transaction") # should never happen
|
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
|
# Build pubkeyarray from outputs
|
||||||
for _type, address, amount in tx.outputs():
|
for _type, address, amount in tx.outputs():
|
||||||
assert _type == TYPE_ADDRESS
|
assert _type == TYPE_ADDRESS
|
||||||
|
@ -517,15 +523,22 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
||||||
|
|
||||||
# Special serialization of the unsigned transaction for
|
# Special serialization of the unsigned transaction for
|
||||||
# the mobile verification app.
|
# the mobile verification app.
|
||||||
class CustomTXSerialization(Transaction):
|
# At the moment, verification only works for p2pkh transactions.
|
||||||
@classmethod
|
if p2pkhTransaction:
|
||||||
def input_script(self, txin, estimate_size=False):
|
class CustomTXSerialization(Transaction):
|
||||||
if txin['type'] == 'p2pkh':
|
@classmethod
|
||||||
return Transaction.get_preimage_script(txin)
|
def input_script(self, txin, estimate_size=False):
|
||||||
if txin['type'] == 'p2sh':
|
if txin['type'] == 'p2pkh':
|
||||||
return '00' + push_script(Transaction.get_preimage_script(txin))
|
return Transaction.get_preimage_script(txin)
|
||||||
raise Exception("unsupported type %s" % txin['type'])
|
if txin['type'] == 'p2sh':
|
||||||
tx_dbb_serialized = CustomTXSerialization(tx.serialize()).serialize()
|
# 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
|
# Build sign command
|
||||||
dbb_signatures = []
|
dbb_signatures = []
|
||||||
|
@ -533,8 +546,15 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
||||||
for step in range(int(steps)):
|
for step in range(int(steps)):
|
||||||
hashes = hasharray[step * self.maxInputs : (step + 1) * self.maxInputs]
|
hashes = hasharray[step * self.maxInputs : (step + 1) * self.maxInputs]
|
||||||
|
|
||||||
msg = ('{"sign": {"meta":"%s", "data":%s, "checkpub":%s} }' % \
|
msg = {
|
||||||
(to_hexstr(Hash(tx_dbb_serialized)), json.dumps(hashes), json.dumps(pubkeyarray))).encode('utf8')
|
"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)
|
dbb_client = self.plugin.get_client(self)
|
||||||
|
|
||||||
if not dbb_client.is_paired():
|
if not dbb_client.is_paired():
|
||||||
|
@ -547,8 +567,7 @@ class DigitalBitbox_KeyStore(Hardware_KeyStore):
|
||||||
if 'echo' not in reply:
|
if 'echo' not in reply:
|
||||||
raise Exception("Could not sign transaction.")
|
raise Exception("Could not sign transaction.")
|
||||||
|
|
||||||
# multisig verification not working correctly yet
|
if self.plugin.is_mobile_paired() and tx_dbb_serialized is not None:
|
||||||
if self.plugin.is_mobile_paired() and not p2shTransaction:
|
|
||||||
reply['tx'] = tx_dbb_serialized
|
reply['tx'] = tx_dbb_serialized
|
||||||
self.plugin.comserver_post_notification(reply)
|
self.plugin.comserver_post_notification(reply)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,9 @@ class Plugin(DigitalBitboxPlugin, QtPluginBase):
|
||||||
if not self.is_mobile_paired():
|
if not self.is_mobile_paired():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not keystore.is_p2pkh():
|
||||||
|
return
|
||||||
|
|
||||||
if len(addrs) == 1:
|
if len(addrs) == 1:
|
||||||
def show_address():
|
def show_address():
|
||||||
change, index = wallet.get_address_index(addrs[0])
|
change, index = wallet.get_address_index(addrs[0])
|
||||||
|
|
Loading…
Reference in New Issue