ledger: support sending to OP_RETURN outputs

closes #5849

based on:
ca9b432ff0
7bb27eff84
This commit is contained in:
SomberNight 2019-12-17 21:10:14 +01:00
parent 02baae10d7
commit 6b8c447eb9
No known key found for this signature in database
GPG key ID: B33B5F232C6271E9
2 changed files with 26 additions and 6 deletions

View file

@ -188,13 +188,23 @@ def is_any_tx_output_on_change_branch(tx: PartialTransaction) -> bool:
def trezor_validate_op_return_output_and_get_data(output: TxOutput) -> bytes: def trezor_validate_op_return_output_and_get_data(output: TxOutput) -> bytes:
validate_op_return_output(output)
script = output.scriptpubkey script = output.scriptpubkey
if not (script[0] == opcodes.OP_RETURN and if not (script[0] == opcodes.OP_RETURN and
script[1] == len(script) - 2 and script[1] <= 75): script[1] == len(script) - 2 and script[1] <= 75):
raise UserFacingException(_("Only OP_RETURN scripts, with one constant push, are supported.")) raise UserFacingException(_("Only OP_RETURN scripts, with one constant push, are supported."))
return script[2:]
def validate_op_return_output(output: TxOutput, *, max_size: int = None) -> None:
script = output.scriptpubkey
if script[0] != opcodes.OP_RETURN:
raise UserFacingException(_("Only OP_RETURN scripts are supported."))
if max_size is not None and len(script) > max_size:
raise UserFacingException(_("OP_RETURN payload too large." + "\n"
+ f"(scriptpubkey size {len(script)} > {max_size})"))
if output.value != 0: if output.value != 0:
raise UserFacingException(_("Amount for OP_RETURN output must be zero.")) raise UserFacingException(_("Amount for OP_RETURN output must be zero."))
return script[2:]
def get_xpubs_and_der_suffixes_from_txinout(tx: PartialTransaction, def get_xpubs_and_der_suffixes_from_txinout(tx: PartialTransaction,

View file

@ -2,6 +2,7 @@ from struct import pack, unpack
import hashlib import hashlib
import sys import sys
import traceback import traceback
from typing import Optional
from electrum import ecc from electrum import ecc
from electrum import bip32 from electrum import bip32
@ -17,7 +18,7 @@ from electrum.base_wizard import ScriptTypeNotSupported
from electrum.logging import get_logger from electrum.logging import get_logger
from ..hw_wallet import HW_PluginBase, HardwareClientBase from ..hw_wallet import HW_PluginBase, HardwareClientBase
from ..hw_wallet.plugin import is_any_tx_output_on_change_branch from ..hw_wallet.plugin import is_any_tx_output_on_change_branch, validate_op_return_output
_logger = get_logger(__name__) _logger = get_logger(__name__)
@ -61,9 +62,10 @@ def test_pin_unlocked(func):
class Ledger_Client(HardwareClientBase): class Ledger_Client(HardwareClientBase):
def __init__(self, hidDevice): def __init__(self, hidDevice, *, is_hw1: bool = False):
self.dongleObject = btchip(hidDevice) self.dongleObject = btchip(hidDevice)
self.preflightDone = False self.preflightDone = False
self._is_hw1 = is_hw1
def is_pairable(self): def is_pairable(self):
return True return True
@ -80,6 +82,9 @@ class Ledger_Client(HardwareClientBase):
def label(self): def label(self):
return "" return ""
def is_hw1(self) -> bool:
return self._is_hw1
def has_usable_connection_with_device(self): def has_usable_connection_with_device(self):
try: try:
self.dongleObject.getFirmwareVersion() self.dongleObject.getFirmwareVersion()
@ -233,7 +238,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
def get_client(self): def get_client(self):
return self.plugin.get_client(self).dongleObject return self.plugin.get_client(self).dongleObject
def get_client_electrum(self): def get_client_electrum(self) -> Optional[Ledger_Client]:
return self.plugin.get_client(self) return self.plugin.get_client(self)
def give_error(self, message, clear_client = False): def give_error(self, message, clear_client = False):
@ -382,7 +387,11 @@ class Ledger_KeyStore(Hardware_KeyStore):
has_change = False has_change = False
any_output_on_change_branch = is_any_tx_output_on_change_branch(tx) any_output_on_change_branch = is_any_tx_output_on_change_branch(tx)
for txout in tx.outputs(): for txout in tx.outputs():
assert txout.address if not txout.address:
if self.get_client_electrum().is_hw1():
self.give_error(_("Only address outputs are supported by {}").format(self.device))
# note: max_size based on https://github.com/LedgerHQ/ledger-app-btc/commit/3a78dee9c0484821df58975803e40d58fbfc2c38#diff-c61ccd96a6d8b54d48f54a3bc4dfa7e2R26
validate_op_return_output(txout, max_size=190)
if txout.is_mine and len(tx.outputs()) > 1 \ if txout.is_mine and len(tx.outputs()) > 1 \
and not has_change: and not has_change:
# prioritise hiding outputs on the 'change' branch from user # prioritise hiding outputs on the 'change' branch from user
@ -570,7 +579,8 @@ class LedgerPlugin(HW_PluginBase):
client = self.get_btchip_device(device) client = self.get_btchip_device(device)
if client is not None: if client is not None:
client = Ledger_Client(client) is_hw1 = device.product_key[0] == 0x2581
client = Ledger_Client(client, is_hw1=is_hw1)
return client return client
def setup_device(self, device_info, wizard, purpose): def setup_device(self, device_info, wizard, purpose):