commit f8f00188ed36aabde056596c4a00009ae29f6f7f
parent 064fdaca2d2c2513f4ea845c413b02cfb60a272e
Author: ThomasV <thomasv@electrum.org>
Date: Thu, 22 Mar 2018 12:18:59 +0100
Merge pull request #4148 from SomberNight/hw_wallet_output_ismine_der
trezor/keepkey/dbb: provide derivation info for all is_mine txn outputs
Diffstat:
4 files changed, 144 insertions(+), 86 deletions(-)
diff --git a/lib/wallet.py b/lib/wallet.py
@@ -1444,7 +1444,7 @@ class Abstract_Wallet(PrintError):
xpubs = self.get_master_public_keys()
for txout in tx.outputs():
_type, addr, amount = txout
- if self.is_change(addr):
+ if self.is_mine(addr):
index = self.get_address_index(addr)
pubkeys = self.get_public_keys(addr)
# sort xpubs using the order of pubkeys
diff --git a/plugins/keepkey/plugin.py b/plugins/keepkey/plugin.py
@@ -303,56 +303,83 @@ class KeepKeyCompatiblePlugin(HW_PluginBase):
return inputs
def tx_outputs(self, derivation, tx, segwit=False):
+
+ def create_output_by_derivation(info):
+ index, xpubs, m = info
+ if len(xpubs) == 1:
+ script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOADDRESS
+ address_n = self.client_class.expand_path(derivation + "/%d/%d" % index)
+ txoutputtype = self.types.TxOutputType(
+ amount=amount,
+ script_type=script_type,
+ address_n=address_n,
+ )
+ else:
+ script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOMULTISIG
+ address_n = self.client_class.expand_path("/%d/%d" % index)
+ nodes = map(self.ckd_public.deserialize, xpubs)
+ pubkeys = [self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes]
+ multisig = self.types.MultisigRedeemScriptType(
+ pubkeys=pubkeys,
+ signatures=[b''] * len(pubkeys),
+ m=m)
+ txoutputtype = self.types.TxOutputType(
+ multisig=multisig,
+ amount=amount,
+ address_n=self.client_class.expand_path(derivation + "/%d/%d" % index),
+ script_type=script_type)
+ return txoutputtype
+
+ def create_output_by_address():
+ txoutputtype = self.types.TxOutputType()
+ txoutputtype.amount = amount
+ if _type == TYPE_SCRIPT:
+ txoutputtype.script_type = self.types.PAYTOOPRETURN
+ txoutputtype.op_return_data = address[2:]
+ elif _type == TYPE_ADDRESS:
+ if is_segwit_address(address):
+ txoutputtype.script_type = self.types.PAYTOWITNESS
+ else:
+ addrtype, hash_160 = b58_address_to_hash160(address)
+ if addrtype == constants.net.ADDRTYPE_P2PKH:
+ txoutputtype.script_type = self.types.PAYTOADDRESS
+ elif addrtype == constants.net.ADDRTYPE_P2SH:
+ txoutputtype.script_type = self.types.PAYTOSCRIPTHASH
+ else:
+ raise BaseException('addrtype: ' + str(addrtype))
+ txoutputtype.address = address
+ return txoutputtype
+
+ def is_any_output_on_change_branch():
+ for _type, address, amount in tx.outputs():
+ info = tx.output_info.get(address)
+ if info is not None:
+ index, xpubs, m = info
+ if index[0] == 1:
+ return True
+ return False
+
outputs = []
has_change = False
+ any_output_on_change_branch = is_any_output_on_change_branch()
for _type, address, amount in tx.outputs():
+ use_create_by_derivation = False
+
info = tx.output_info.get(address)
if info is not None and not has_change:
- has_change = True # no more than one change address
- addrtype, hash_160 = b58_address_to_hash160(address)
index, xpubs, m = info
- if len(xpubs) == 1:
- script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOADDRESS
- address_n = self.client_class.expand_path(derivation + "/%d/%d"%index)
- txoutputtype = self.types.TxOutputType(
- amount = amount,
- script_type = script_type,
- address_n = address_n,
- )
- else:
- script_type = self.types.PAYTOP2SHWITNESS if segwit else self.types.PAYTOMULTISIG
- address_n = self.client_class.expand_path("/%d/%d"%index)
- nodes = map(self.ckd_public.deserialize, xpubs)
- pubkeys = [ self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes]
- multisig = self.types.MultisigRedeemScriptType(
- pubkeys = pubkeys,
- signatures = [b''] * len(pubkeys),
- m = m)
- txoutputtype = self.types.TxOutputType(
- multisig = multisig,
- amount = amount,
- address_n = self.client_class.expand_path(derivation + "/%d/%d"%index),
- script_type = script_type)
+ on_change_branch = index[0] == 1
+ # prioritise hiding outputs on the 'change' branch from user
+ # because no more than one change address allowed
+ if on_change_branch == any_output_on_change_branch:
+ use_create_by_derivation = True
+ has_change = True
+
+ if use_create_by_derivation:
+ txoutputtype = create_output_by_derivation(info)
else:
- txoutputtype = self.types.TxOutputType()
- txoutputtype.amount = amount
- if _type == TYPE_SCRIPT:
- txoutputtype.script_type = self.types.PAYTOOPRETURN
- txoutputtype.op_return_data = address[2:]
- elif _type == TYPE_ADDRESS:
- if is_segwit_address(address):
- txoutputtype.script_type = self.types.PAYTOWITNESS
- else:
- addrtype, hash_160 = b58_address_to_hash160(address)
- if addrtype == constants.net.ADDRTYPE_P2PKH:
- txoutputtype.script_type = self.types.PAYTOADDRESS
- elif addrtype == constants.net.ADDRTYPE_P2SH:
- txoutputtype.script_type = self.types.PAYTOSCRIPTHASH
- else:
- raise BaseException('addrtype: ' + str(addrtype))
- txoutputtype.address = address
-
+ txoutputtype = create_output_by_address()
outputs.append(txoutputtype)
return outputs
diff --git a/plugins/ledger/ledger.py b/plugins/ledger/ledger.py
@@ -377,7 +377,8 @@ class Ledger_KeyStore(Hardware_KeyStore):
for _type, address, amount in tx.outputs():
assert _type == TYPE_ADDRESS
info = tx.output_info.get(address)
- if (info is not None) and (len(tx.outputs()) != 1):
+ if (info is not None) and len(tx.outputs()) > 1 \
+ and info[0][0] == 1: # "is on 'change' branch"
index, xpubs, m = info
changePath = self.get_derivation()[2:] + "/%d/%d"%index
changeAmount = amount
diff --git a/plugins/trezor/trezor.py b/plugins/trezor/trezor.py
@@ -380,56 +380,86 @@ class TrezorPlugin(HW_PluginBase):
return inputs
def tx_outputs(self, derivation, tx, script_gen=SCRIPT_GEN_LEGACY):
+
+ def create_output_by_derivation(info):
+ index, xpubs, m = info
+ if len(xpubs) == 1:
+ if script_gen == SCRIPT_GEN_NATIVE_SEGWIT:
+ script_type = self.types.OutputScriptType.PAYTOWITNESS
+ elif script_gen == SCRIPT_GEN_P2SH_SEGWIT:
+ script_type = self.types.OutputScriptType.PAYTOP2SHWITNESS
+ else:
+ script_type = self.types.OutputScriptType.PAYTOADDRESS
+ address_n = self.client_class.expand_path(derivation + "/%d/%d" % index)
+ txoutputtype = self.types.TxOutputType(
+ amount=amount,
+ script_type=script_type,
+ address_n=address_n,
+ )
+ else:
+ if script_gen == SCRIPT_GEN_NATIVE_SEGWIT:
+ script_type = self.types.OutputScriptType.PAYTOWITNESS
+ elif script_gen == SCRIPT_GEN_P2SH_SEGWIT:
+ script_type = self.types.OutputScriptType.PAYTOP2SHWITNESS
+ else:
+ script_type = self.types.OutputScriptType.PAYTOMULTISIG
+ address_n = self.client_class.expand_path("/%d/%d" % index)
+ nodes = map(self.ckd_public.deserialize, xpubs)
+ pubkeys = [self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes]
+ multisig = self.types.MultisigRedeemScriptType(
+ pubkeys=pubkeys,
+ signatures=[b''] * len(pubkeys),
+ m=m)
+ txoutputtype = self.types.TxOutputType(
+ multisig=multisig,
+ amount=amount,
+ address_n=self.client_class.expand_path(derivation + "/%d/%d" % index),
+ script_type=script_type)
+ return txoutputtype
+
+ def create_output_by_address():
+ txoutputtype = self.types.TxOutputType()
+ txoutputtype.amount = amount
+ if _type == TYPE_SCRIPT:
+ txoutputtype.script_type = self.types.OutputScriptType.PAYTOOPRETURN
+ txoutputtype.op_return_data = address[2:]
+ elif _type == TYPE_ADDRESS:
+ txoutputtype.script_type = self.types.OutputScriptType.PAYTOADDRESS
+ txoutputtype.address = address
+ return txoutputtype
+
+ def is_any_output_on_change_branch():
+ for _type, address, amount in tx.outputs():
+ info = tx.output_info.get(address)
+ if info is not None:
+ index, xpubs, m = info
+ if index[0] == 1:
+ return True
+ return False
+
outputs = []
has_change = False
+ any_output_on_change_branch = is_any_output_on_change_branch()
for _type, address, amount in tx.outputs():
+ use_create_by_derivation = False
+
info = tx.output_info.get(address)
if info is not None and not has_change:
- has_change = True # no more than one change address
index, xpubs, m = info
- if len(xpubs) == 1:
- if script_gen == SCRIPT_GEN_NATIVE_SEGWIT:
- script_type = self.types.OutputScriptType.PAYTOWITNESS
- elif script_gen == SCRIPT_GEN_P2SH_SEGWIT:
- script_type = self.types.OutputScriptType.PAYTOP2SHWITNESS
- else:
- script_type = self.types.OutputScriptType.PAYTOADDRESS
- address_n = self.client_class.expand_path(derivation + "/%d/%d"%index)
- txoutputtype = self.types.TxOutputType(
- amount = amount,
- script_type = script_type,
- address_n = address_n,
- )
- else:
- if script_gen == SCRIPT_GEN_NATIVE_SEGWIT:
- script_type = self.types.OutputScriptType.PAYTOWITNESS
- elif script_gen == SCRIPT_GEN_P2SH_SEGWIT:
- script_type = self.types.OutputScriptType.PAYTOP2SHWITNESS
- else:
- script_type = self.types.OutputScriptType.PAYTOMULTISIG
- address_n = self.client_class.expand_path("/%d/%d"%index)
- nodes = map(self.ckd_public.deserialize, xpubs)
- pubkeys = [ self.types.HDNodePathType(node=node, address_n=address_n) for node in nodes]
- multisig = self.types.MultisigRedeemScriptType(
- pubkeys = pubkeys,
- signatures = [b''] * len(pubkeys),
- m = m)
- txoutputtype = self.types.TxOutputType(
- multisig = multisig,
- amount = amount,
- address_n = self.client_class.expand_path(derivation + "/%d/%d"%index),
- script_type = script_type)
+ on_change_branch = index[0] == 1
+ # prioritise hiding outputs on the 'change' branch from user
+ # because no more than one change address allowed
+ # note: ^ restriction can be removed once we require fw
+ # that has https://github.com/trezor/trezor-mcu/pull/306
+ if on_change_branch == any_output_on_change_branch:
+ use_create_by_derivation = True
+ has_change = True
+
+ if use_create_by_derivation:
+ txoutputtype = create_output_by_derivation(info)
else:
- txoutputtype = self.types.TxOutputType()
- txoutputtype.amount = amount
- if _type == TYPE_SCRIPT:
- txoutputtype.script_type = self.types.OutputScriptType.PAYTOOPRETURN
- txoutputtype.op_return_data = address[2:]
- elif _type == TYPE_ADDRESS:
- txoutputtype.script_type = self.types.OutputScriptType.PAYTOADDRESS
- txoutputtype.address = address
-
+ txoutputtype = create_output_by_address()
outputs.append(txoutputtype)
return outputs