Merge pull request #2361 from btchip/ledger-segwit
Segwit and RBF support for Ledger HW
This commit is contained in:
commit
8c32d29272
|
@ -11,6 +11,7 @@ from electrum.bitcoin import TYPE_ADDRESS, int_to_hex, var_int
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.plugins import BasePlugin, hook
|
from electrum.plugins import BasePlugin, hook
|
||||||
from electrum.keystore import Hardware_KeyStore, parse_xpubkey
|
from electrum.keystore import Hardware_KeyStore, parse_xpubkey
|
||||||
|
from electrum.transaction import push_script
|
||||||
from ..hw_wallet import HW_PluginBase
|
from ..hw_wallet import HW_PluginBase
|
||||||
from electrum.util import format_satoshis_plain, print_error, is_verbose
|
from electrum.util import format_satoshis_plain, print_error, is_verbose
|
||||||
|
|
||||||
|
@ -172,6 +173,9 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
||||||
self.signing = False
|
self.signing = False
|
||||||
self.cfg = d.get('cfg', {'mode':0,'pair':''})
|
self.cfg = d.get('cfg', {'mode':0,'pair':''})
|
||||||
|
|
||||||
|
def is_segwit(self):
|
||||||
|
return self.plugin.segwit
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
obj = Hardware_KeyStore.dump(self)
|
obj = Hardware_KeyStore.dump(self)
|
||||||
obj['cfg'] = self.cfg
|
obj['cfg'] = self.cfg
|
||||||
|
@ -268,6 +272,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
||||||
output = None
|
output = None
|
||||||
outputAmount = None
|
outputAmount = None
|
||||||
p2shTransaction = False
|
p2shTransaction = False
|
||||||
|
segwitTransaction = True
|
||||||
reorganize = False
|
reorganize = False
|
||||||
pin = ""
|
pin = ""
|
||||||
self.get_client() # prompt for the PIN before displaying the dialog if necessary
|
self.get_client() # prompt for the PIN before displaying the dialog if necessary
|
||||||
|
@ -281,6 +286,9 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
||||||
if txin['type'] in ['p2sh']:
|
if txin['type'] in ['p2sh']:
|
||||||
p2shTransaction = True
|
p2shTransaction = True
|
||||||
|
|
||||||
|
if txin['type'] in ['p2wpkh-p2sh']:
|
||||||
|
segwitTransaction = True
|
||||||
|
|
||||||
pubkeys, x_pubkeys = tx.get_sorted_pubkeys(txin)
|
pubkeys, x_pubkeys = tx.get_sorted_pubkeys(txin)
|
||||||
for i, x_pubkey in enumerate(x_pubkeys):
|
for i, x_pubkey in enumerate(x_pubkeys):
|
||||||
if x_pubkey in derivations:
|
if x_pubkey in derivations:
|
||||||
|
@ -291,7 +299,12 @@ class Ledger_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
|
||||||
|
|
||||||
inputs.append([txin['prev_tx'].raw, txin['prevout_n'], txin.get('redeemScript'), txin['prevout_hash'], signingPos ])
|
redeemScript = txin.get('redeemScript')
|
||||||
|
if segwitTransaction and not redeemScript:
|
||||||
|
pkh = bitcoin.hash_160(pubkeys[0].decode('hex')).encode('hex')
|
||||||
|
redeemScript = '76a9' + push_script(pkh) + '88ac'
|
||||||
|
|
||||||
|
inputs.append([txin['prev_tx'].raw, txin['prevout_n'], redeemScript, txin['prevout_hash'], signingPos, txin['sequence'] ])
|
||||||
inputsPaths.append(hwAddress)
|
inputsPaths.append(hwAddress)
|
||||||
pubKeys.append(pubkeys)
|
pubKeys.append(pubkeys)
|
||||||
|
|
||||||
|
@ -330,14 +343,24 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
||||||
try:
|
try:
|
||||||
# Get trusted inputs from the original transactions
|
# Get trusted inputs from the original transactions
|
||||||
for utxo in inputs:
|
for utxo in inputs:
|
||||||
if not p2shTransaction:
|
sequence = int_to_hex(utxo[5], 4)
|
||||||
|
if segwitTransaction:
|
||||||
txtmp = bitcoinTransaction(bytearray(utxo[0].decode('hex')))
|
txtmp = bitcoinTransaction(bytearray(utxo[0].decode('hex')))
|
||||||
chipInputs.append(self.get_client().getTrustedInput(txtmp, utxo[1]))
|
tmp = utxo[3].decode('hex')[::-1].encode('hex')
|
||||||
|
tmp += int_to_hex(utxo[1], 4)
|
||||||
|
tmp += str(txtmp.outputs[utxo[1]].amount).encode('hex')
|
||||||
|
chipInputs.append({'value' : tmp.decode('hex'), 'witness' : True, 'sequence' : sequence})
|
||||||
|
redeemScripts.append(bytearray(utxo[2].decode('hex')))
|
||||||
|
elif not p2shTransaction:
|
||||||
|
txtmp = bitcoinTransaction(bytearray(utxo[0].decode('hex')))
|
||||||
|
trustedInput = self.get_client().getTrustedInput(txtmp, utxo[1])
|
||||||
|
trustedInput['sequence'] = sequence
|
||||||
|
chipInputs.append(trustedInput)
|
||||||
redeemScripts.append(txtmp.outputs[utxo[1]].script)
|
redeemScripts.append(txtmp.outputs[utxo[1]].script)
|
||||||
else:
|
else:
|
||||||
tmp = utxo[3].decode('hex')[::-1].encode('hex')
|
tmp = utxo[3].decode('hex')[::-1].encode('hex')
|
||||||
tmp += int_to_hex(utxo[1], 4)
|
tmp += int_to_hex(utxo[1], 4)
|
||||||
chipInputs.append({'value' : tmp.decode('hex')})
|
chipInputs.append({'value' : tmp.decode('hex'), 'sequence' : sequence})
|
||||||
redeemScripts.append(bytearray(utxo[2].decode('hex')))
|
redeemScripts.append(bytearray(utxo[2].decode('hex')))
|
||||||
|
|
||||||
# Sign all inputs
|
# Sign all inputs
|
||||||
|
@ -345,6 +368,29 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
||||||
inputIndex = 0
|
inputIndex = 0
|
||||||
rawTx = tx.serialize()
|
rawTx = tx.serialize()
|
||||||
self.get_client().enableAlternate2fa(False)
|
self.get_client().enableAlternate2fa(False)
|
||||||
|
if segwitTransaction:
|
||||||
|
self.get_client().startUntrustedTransaction(True, inputIndex,
|
||||||
|
chipInputs, redeemScripts[inputIndex])
|
||||||
|
outputData = self.get_client().finalizeInputFull(txOutput)
|
||||||
|
outputData['outputData'] = txOutput
|
||||||
|
transactionOutput = outputData['outputData']
|
||||||
|
if outputData['confirmationNeeded']:
|
||||||
|
outputData['address'] = output
|
||||||
|
self.handler.clear_dialog()
|
||||||
|
pin = self.handler.get_auth( outputData ) # does the authenticate dialog and returns pin
|
||||||
|
if not pin:
|
||||||
|
raise UserWarning()
|
||||||
|
if pin != 'paired':
|
||||||
|
self.handler.show_message(_("Confirmed. Signing Transaction..."))
|
||||||
|
while inputIndex < len(inputs):
|
||||||
|
singleInput = [ chipInputs[inputIndex] ]
|
||||||
|
self.get_client().startUntrustedTransaction(False, 0,
|
||||||
|
singleInput, redeemScripts[inputIndex])
|
||||||
|
inputSignature = self.get_client().untrustedHashSign(inputsPaths[inputIndex], pin)
|
||||||
|
inputSignature[0] = 0x30 # force for 1.4.9+
|
||||||
|
signatures.append(inputSignature)
|
||||||
|
inputIndex = inputIndex + 1
|
||||||
|
else:
|
||||||
while inputIndex < len(inputs):
|
while inputIndex < len(inputs):
|
||||||
self.get_client().startUntrustedTransaction(firstTransaction, inputIndex,
|
self.get_client().startUntrustedTransaction(firstTransaction, inputIndex,
|
||||||
chipInputs, redeemScripts[inputIndex])
|
chipInputs, redeemScripts[inputIndex])
|
||||||
|
@ -385,6 +431,11 @@ class Ledger_KeyStore(Hardware_KeyStore):
|
||||||
|
|
||||||
# Reformat transaction
|
# Reformat transaction
|
||||||
inputIndex = 0
|
inputIndex = 0
|
||||||
|
if segwitTransaction:
|
||||||
|
for txin in tx.inputs():
|
||||||
|
txin['signatures'] = [str(signatures[inputIndex][0:-1]).encode('hex')]
|
||||||
|
inputIndex = inputIndex + 1
|
||||||
|
else:
|
||||||
while inputIndex < len(inputs):
|
while inputIndex < len(inputs):
|
||||||
if p2shTransaction:
|
if p2shTransaction:
|
||||||
signaturesPack = [signatures[inputIndex]] * len(pubKeys[inputIndex])
|
signaturesPack = [signatures[inputIndex]] * len(pubKeys[inputIndex])
|
||||||
|
@ -418,6 +469,7 @@ class LedgerPlugin(HW_PluginBase):
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, parent, config, name):
|
def __init__(self, parent, config, name):
|
||||||
|
self.segwit = config.get("segwit")
|
||||||
HW_PluginBase.__init__(self, parent, config, name)
|
HW_PluginBase.__init__(self, parent, config, name)
|
||||||
if self.libraries_available:
|
if self.libraries_available:
|
||||||
self.device_manager().register_devices(self.DEVICE_IDS)
|
self.device_manager().register_devices(self.DEVICE_IDS)
|
||||||
|
@ -469,7 +521,6 @@ class LedgerPlugin(HW_PluginBase):
|
||||||
#assert self.main_thread != threading.current_thread()
|
#assert self.main_thread != threading.current_thread()
|
||||||
devmgr = self.device_manager()
|
devmgr = self.device_manager()
|
||||||
handler = keystore.handler
|
handler = keystore.handler
|
||||||
handler = keystore.handler
|
|
||||||
with devmgr.hid_lock:
|
with devmgr.hid_lock:
|
||||||
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
|
client = devmgr.client_for_keystore(self, handler, keystore, force_pair)
|
||||||
# returns the client for a given keystore. can use xpub
|
# returns the client for a given keystore. can use xpub
|
||||||
|
|
Loading…
Reference in New Issue