New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ledger wallet support #398
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -96,16 +96,13 @@ def is_pin_validated(self, client): | |
return True | ||
raise e | ||
|
||
def supports_multi_output(self): | ||
return self.multiOutputSupported | ||
|
||
def perform_hw1_preflight(self): | ||
try: | ||
firmwareInfo = self.dongleObject.getFirmwareVersion() | ||
firmware = firmwareInfo['version'].split(".") | ||
self.multiOutputSupported = int(firmware[0]) >= 1 and int(firmware[1]) >= 1 and int(firmware[2]) >= 4 | ||
bitcoinCashSupport = int(firmware[0]) >= 1 and int(firmware[1]) >= 1 and int(firmware[2]) >= 8 | ||
|
||
if not checkFirmware(firmware): | ||
if not checkFirmware(firmware) or not bitcoinCashSupport: | ||
self.dongleObject.dongle.close() | ||
raise Exception("HW1 firmware version too old. Please update at https://www.ledgerwallet.com") | ||
try: | ||
|
@@ -141,7 +138,7 @@ def checkDevice(self): | |
self.perform_hw1_preflight() | ||
except BTChipException as e: | ||
if (e.sw == 0x6d00): | ||
raise BaseException("Device not in Bitcoin mode") | ||
raise BaseException("Device not in Bitcoin Cash mode") | ||
raise e | ||
self.preflightDone = True | ||
|
||
|
@@ -295,16 +292,13 @@ def sign_transaction(self, tx, password): | |
for txout in tx.outputs(): | ||
output_type, addr, amount = txout | ||
txOutput += int_to_hex(amount, 8) | ||
script = tx.pay_script(output_type, addr) | ||
script = tx.pay_script(addr) | ||
txOutput += var_int(len(script)//2) | ||
txOutput += script | ||
txOutput = bfh(txOutput) | ||
|
||
# Recognize outputs - only one output and one change is authorized | ||
if not p2shTransaction: | ||
if not self.get_client_electrum().supports_multi_output(): | ||
if len(tx.outputs()) > 2: | ||
self.give_error("Transaction with more than 2 outputs not supported") | ||
for _type, address, amount in tx.outputs(): | ||
assert _type == TYPE_ADDRESS | ||
info = tx.output_info.get(address) | ||
|
@@ -321,47 +315,38 @@ def sign_transaction(self, tx, password): | |
# Get trusted inputs from the original transactions | ||
for utxo in inputs: | ||
sequence = int_to_hex(utxo[5], 4) | ||
if not p2shTransaction: | ||
txtmp = bitcoinTransaction(bfh(utxo[0])) | ||
trustedInput = self.get_client().getTrustedInput(txtmp, utxo[1]) | ||
trustedInput['sequence'] = sequence | ||
chipInputs.append(trustedInput) | ||
redeemScripts.append(txtmp.outputs[utxo[1]].script) | ||
else: | ||
tmp = bfh(utxo[3])[::-1] | ||
tmp += bfh(int_to_hex(utxo[1], 4)) | ||
chipInputs.append({'value' : tmp, 'sequence' : sequence}) | ||
redeemScripts.append(bfh(utxo[2])) | ||
txtmp = bitcoinTransaction(bfh(utxo[0])) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this support both P2SH and non-P2SH transactions using the Nano S? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've only tested 2 cosigner multisig, but it should (since all transactions are BIP143, unlike core where its only segwit) |
||
tmp = bfh(utxo[3])[::-1] | ||
tmp += bfh(int_to_hex(utxo[1], 4)) | ||
tmp += txtmp.outputs[utxo[1]].amount | ||
chipInputs.append({'value' : tmp, 'witness' : True, 'sequence' : sequence}) | ||
redeemScripts.append(bfh(utxo[2])) | ||
|
||
# Sign all inputs | ||
firstTransaction = True | ||
inputIndex = 0 | ||
rawTx = tx.serialize() | ||
self.get_client().enableAlternate2fa(False) | ||
if True: | ||
while inputIndex < len(inputs): | ||
self.get_client().startUntrustedTransaction(firstTransaction, inputIndex, | ||
chipInputs, redeemScripts[inputIndex]) | ||
outputData = self.get_client().finalizeInputFull(txOutput) | ||
outputData['outputData'] = txOutput | ||
if firstTransaction: | ||
transactionOutput = outputData['outputData'] | ||
if outputData['confirmationNeeded']: | ||
outputData['address'] = output | ||
self.handler.finished() | ||
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...")) | ||
else: | ||
# Sign input with the provided PIN | ||
inputSignature = self.get_client().untrustedHashSign(inputsPaths[inputIndex], pin, lockTime=tx.locktime) | ||
inputSignature[0] = 0x30 # force for 1.4.9+ | ||
signatures.append(inputSignature) | ||
inputIndex = inputIndex + 1 | ||
if pin != 'paired': | ||
firstTransaction = False | ||
self.get_client().startUntrustedTransaction(True, inputIndex, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice clean up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only BIP143 signing is needed |
||
chipInputs, redeemScripts[inputIndex]) | ||
outputData = self.get_client().finalizeInputFull(txOutput) | ||
outputData['outputData'] = txOutput | ||
transactionOutput = outputData['outputData'] | ||
if outputData['confirmationNeeded']: | ||
outputData['address'] = output | ||
self.handler.finished() | ||
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, lockTime=tx.locktime, sighashType=tx.nHashType()) | ||
inputSignature[0] = 0x30 # force for 1.4.9+ | ||
signatures.append(inputSignature) | ||
inputIndex = inputIndex + 1 | ||
except UserWarning: | ||
self.handler.show_error(_('Cancelled by user')) | ||
return | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does the change from
4
to8
signify? might be worth pulling this number out into a named variable for readability 👍There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK, that's the version for the Bitcoin Cash app in the Nano S.
The current version is 1.1.8 @bderose
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, however the current version checking is a bit odd (ie. 1.1.9 would work but not 1.2.0), so a few things could be tweaked to provide backwards compatibility. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One solution would be to use something like the
semver
package to do this comparison. I realize we may not want to introduce new dependencies, though. In that case, this SO question has some good suggestions.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I've added the version comparison from
distutils.version
so no new dependencies are required, and it seems to work well.