trezor: TrezorPlugin._make_multisig to use MultisigDescriptor
This fixes a regression where the plugin was assuming ordering for txin.pubkeys (which is now a set). (previously txin.pubkeys was a list ordered according to the final sort order of keys inside the bitcoin script)
This commit is contained in:
@@ -354,25 +354,6 @@ def validate_op_return_output(output: TxOutput, *, max_size: int = None) -> None
|
||||
raise UserFacingException(_("Amount for OP_RETURN output must be zero."))
|
||||
|
||||
|
||||
def get_xpubs_and_der_suffixes_from_txinout(tx: PartialTransaction,
|
||||
txinout: Union[PartialTxInput, PartialTxOutput]) \
|
||||
-> List[Tuple[str, List[int]]]:
|
||||
xfp_to_xpub_map = {xfp: bip32node for bip32node, (xfp, path)
|
||||
in tx.xpubs.items()} # type: Dict[bytes, BIP32Node]
|
||||
xfps = [txinout.bip32_paths[pubkey][0] for pubkey in txinout.pubkeys]
|
||||
try:
|
||||
xpubs = [xfp_to_xpub_map[xfp] for xfp in xfps]
|
||||
except KeyError as e:
|
||||
raise Exception(f"Partial transaction is missing global xpub for "
|
||||
f"fingerprint ({str(e)}) in input/output") from e
|
||||
xpubs_and_deriv_suffixes = []
|
||||
for bip32node, pubkey in zip(xpubs, txinout.pubkeys):
|
||||
xfp, path = txinout.bip32_paths[pubkey]
|
||||
der_suffix = list(path)[bip32node.depth:]
|
||||
xpubs_and_deriv_suffixes.append((bip32node.to_xpub(), der_suffix))
|
||||
return xpubs_and_deriv_suffixes
|
||||
|
||||
|
||||
def only_hook_if_libraries_available(func):
|
||||
# note: this decorator must wrap @hook, not the other way around,
|
||||
# as 'hook' uses the name of the function it wraps
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from binascii import hexlify, unhexlify
|
||||
import traceback
|
||||
import sys
|
||||
from typing import NamedTuple, Any, Optional, Dict, Union, List, Tuple, TYPE_CHECKING
|
||||
from typing import NamedTuple, Any, Optional, Dict, Union, List, Tuple, TYPE_CHECKING, Sequence
|
||||
|
||||
from electrum.util import bfh, UserCancelled, UserFacingException
|
||||
from electrum.bip32 import BIP32Node
|
||||
from electrum import descriptor
|
||||
from electrum import constants
|
||||
from electrum.i18n import _
|
||||
from electrum.transaction import Transaction, PartialTransaction, PartialTxInput, PartialTxOutput, Sighash
|
||||
@@ -13,8 +14,7 @@ from electrum.plugin import Device, runs_in_hwd_thread
|
||||
from electrum.base_wizard import ScriptTypeNotSupported
|
||||
|
||||
from ..hw_wallet import HW_PluginBase
|
||||
from ..hw_wallet.plugin import (is_any_tx_output_on_change_branch, trezor_validate_op_return_output_and_get_data,
|
||||
get_xpubs_and_der_suffixes_from_txinout)
|
||||
from ..hw_wallet.plugin import is_any_tx_output_on_change_branch, trezor_validate_op_return_output_and_get_data
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import usb1
|
||||
@@ -271,7 +271,7 @@ class KeepKeyPlugin(HW_PluginBase):
|
||||
client.load_device_by_xprv(item, pin, passphrase_protection,
|
||||
label, language)
|
||||
|
||||
def _make_node_path(self, xpub, address_n):
|
||||
def _make_node_path(self, xpub: str, address_n: Sequence[int]):
|
||||
bip32node = BIP32Node.from_xkey(xpub)
|
||||
node = self.types.HDNodeType(
|
||||
depth=bip32node.depth,
|
||||
@@ -351,14 +351,9 @@ class KeepKeyPlugin(HW_PluginBase):
|
||||
script_type = self.get_keepkey_input_script_type(wallet.txin_type)
|
||||
|
||||
# prepare multisig, if available:
|
||||
xpubs = wallet.get_master_public_keys()
|
||||
if len(xpubs) > 1:
|
||||
pubkeys = wallet.get_public_keys(address)
|
||||
# sort xpubs using the order of pubkeys
|
||||
sorted_pairs = sorted(zip(pubkeys, xpubs))
|
||||
multisig = self._make_multisig(
|
||||
wallet.m,
|
||||
[(xpub, deriv_suffix) for pubkey, xpub in sorted_pairs])
|
||||
desc = wallet.get_script_descriptor_for_address(address)
|
||||
if multi := desc.get_simple_multisig():
|
||||
multisig = self._make_multisig(multi)
|
||||
else:
|
||||
multisig = None
|
||||
|
||||
@@ -378,8 +373,7 @@ class KeepKeyPlugin(HW_PluginBase):
|
||||
assert keystore
|
||||
assert (desc := txin.script_descriptor)
|
||||
if multi := desc.get_simple_multisig():
|
||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txin)
|
||||
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||
multisig = self._make_multisig(multi)
|
||||
else:
|
||||
multisig = None
|
||||
script_type = self.get_keepkey_input_script_type(desc.to_legacy_electrum_script_type())
|
||||
@@ -407,14 +401,18 @@ class KeepKeyPlugin(HW_PluginBase):
|
||||
|
||||
return inputs
|
||||
|
||||
def _make_multisig(self, m, xpubs):
|
||||
if len(xpubs) == 1:
|
||||
return None
|
||||
pubkeys = [self._make_node_path(xpub, deriv) for xpub, deriv in xpubs]
|
||||
def _make_multisig(self, desc: descriptor.MultisigDescriptor):
|
||||
pubkeys = []
|
||||
for pubkey_provider in desc.pubkeys:
|
||||
assert not pubkey_provider.is_range()
|
||||
assert pubkey_provider.extkey is not None
|
||||
xpub = pubkey_provider.pubkey
|
||||
der_suffix = pubkey_provider.get_der_suffix_int_list()
|
||||
pubkeys.append(self._make_node_path(xpub, der_suffix))
|
||||
return self.types.MultisigRedeemScriptType(
|
||||
pubkeys=pubkeys,
|
||||
signatures=[b''] * len(pubkeys),
|
||||
m=m)
|
||||
m=desc.thresh)
|
||||
|
||||
def tx_outputs(self, tx: PartialTransaction, *, keystore: 'KeepKey_KeyStore'):
|
||||
|
||||
@@ -422,8 +420,7 @@ class KeepKeyPlugin(HW_PluginBase):
|
||||
assert (desc := txout.script_descriptor)
|
||||
script_type = self.get_keepkey_output_script_type(desc.to_legacy_electrum_script_type())
|
||||
if multi := desc.get_simple_multisig():
|
||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txout)
|
||||
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||
multisig = self._make_multisig(multi)
|
||||
else:
|
||||
multisig = None
|
||||
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txout)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from binascii import hexlify, unhexlify
|
||||
import traceback
|
||||
import sys
|
||||
from typing import NamedTuple, Any, Optional, Dict, Union, List, Tuple, TYPE_CHECKING
|
||||
from typing import NamedTuple, Any, Optional, Dict, Union, List, Tuple, TYPE_CHECKING, Sequence
|
||||
|
||||
from electrum.util import bfh, versiontuple, UserCancelled, UserFacingException
|
||||
from electrum.bip32 import BIP32Node
|
||||
from electrum import descriptor
|
||||
from electrum import constants
|
||||
from electrum.i18n import _
|
||||
from electrum.plugin import Device, runs_in_hwd_thread
|
||||
@@ -13,8 +14,7 @@ from electrum.keystore import Hardware_KeyStore
|
||||
from electrum.base_wizard import ScriptTypeNotSupported
|
||||
|
||||
from ..hw_wallet import HW_PluginBase
|
||||
from ..hw_wallet.plugin import (is_any_tx_output_on_change_branch, trezor_validate_op_return_output_and_get_data,
|
||||
get_xpubs_and_der_suffixes_from_txinout)
|
||||
from ..hw_wallet.plugin import is_any_tx_output_on_change_branch, trezor_validate_op_return_output_and_get_data
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .client import SafeTClient
|
||||
@@ -241,7 +241,7 @@ class SafeTPlugin(HW_PluginBase):
|
||||
client.load_device_by_xprv(item, pin, passphrase_protection,
|
||||
label, language)
|
||||
|
||||
def _make_node_path(self, xpub, address_n):
|
||||
def _make_node_path(self, xpub: str, address_n: Sequence[int]):
|
||||
bip32node = BIP32Node.from_xkey(xpub)
|
||||
node = self.types.HDNodeType(
|
||||
depth=bip32node.depth,
|
||||
@@ -321,14 +321,9 @@ class SafeTPlugin(HW_PluginBase):
|
||||
script_type = self.get_safet_input_script_type(wallet.txin_type)
|
||||
|
||||
# prepare multisig, if available:
|
||||
xpubs = wallet.get_master_public_keys()
|
||||
if len(xpubs) > 1:
|
||||
pubkeys = wallet.get_public_keys(address)
|
||||
# sort xpubs using the order of pubkeys
|
||||
sorted_pairs = sorted(zip(pubkeys, xpubs))
|
||||
multisig = self._make_multisig(
|
||||
wallet.m,
|
||||
[(xpub, deriv_suffix) for pubkey, xpub in sorted_pairs])
|
||||
desc = wallet.get_script_descriptor_for_address(address)
|
||||
if multi := desc.get_simple_multisig():
|
||||
multisig = self._make_multisig(multi)
|
||||
else:
|
||||
multisig = None
|
||||
|
||||
@@ -348,8 +343,7 @@ class SafeTPlugin(HW_PluginBase):
|
||||
assert keystore
|
||||
assert (desc := txin.script_descriptor)
|
||||
if multi := desc.get_simple_multisig():
|
||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txin)
|
||||
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||
multisig = self._make_multisig(multi)
|
||||
else:
|
||||
multisig = None
|
||||
script_type = self.get_safet_input_script_type(desc.to_legacy_electrum_script_type())
|
||||
@@ -377,14 +371,18 @@ class SafeTPlugin(HW_PluginBase):
|
||||
|
||||
return inputs
|
||||
|
||||
def _make_multisig(self, m, xpubs):
|
||||
if len(xpubs) == 1:
|
||||
return None
|
||||
pubkeys = [self._make_node_path(xpub, deriv) for xpub, deriv in xpubs]
|
||||
def _make_multisig(self, desc: descriptor.MultisigDescriptor):
|
||||
pubkeys = []
|
||||
for pubkey_provider in desc.pubkeys:
|
||||
assert not pubkey_provider.is_range()
|
||||
assert pubkey_provider.extkey is not None
|
||||
xpub = pubkey_provider.pubkey
|
||||
der_suffix = pubkey_provider.get_der_suffix_int_list()
|
||||
pubkeys.append(self._make_node_path(xpub, der_suffix))
|
||||
return self.types.MultisigRedeemScriptType(
|
||||
pubkeys=pubkeys,
|
||||
signatures=[b''] * len(pubkeys),
|
||||
m=m)
|
||||
m=desc.thresh)
|
||||
|
||||
def tx_outputs(self, tx: PartialTransaction, *, keystore: 'SafeTKeyStore'):
|
||||
|
||||
@@ -392,8 +390,7 @@ class SafeTPlugin(HW_PluginBase):
|
||||
assert (desc := txout.script_descriptor)
|
||||
script_type = self.get_safet_output_script_type(desc.to_legacy_electrum_script_type())
|
||||
if multi := desc.get_simple_multisig():
|
||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txout)
|
||||
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||
multisig = self._make_multisig(multi)
|
||||
else:
|
||||
multisig = None
|
||||
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txout)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import traceback
|
||||
import sys
|
||||
from typing import NamedTuple, Any, Optional, Dict, Union, List, Tuple, TYPE_CHECKING
|
||||
from typing import NamedTuple, Any, Optional, Dict, Union, List, Tuple, TYPE_CHECKING, Sequence
|
||||
|
||||
from electrum.util import bfh, versiontuple, UserCancelled, UserFacingException
|
||||
from electrum.bip32 import BIP32Node, convert_bip32_path_to_list_of_uint32 as parse_path
|
||||
from electrum import descriptor
|
||||
from electrum import constants
|
||||
from electrum.i18n import _
|
||||
from electrum.plugin import Device, runs_in_hwd_thread
|
||||
@@ -14,8 +15,7 @@ from electrum.logging import get_logger
|
||||
|
||||
from ..hw_wallet import HW_PluginBase
|
||||
from ..hw_wallet.plugin import (is_any_tx_output_on_change_branch, trezor_validate_op_return_output_and_get_data,
|
||||
LibraryFoundButUnusable, OutdatedHwFirmwareException,
|
||||
get_xpubs_and_der_suffixes_from_txinout)
|
||||
LibraryFoundButUnusable, OutdatedHwFirmwareException)
|
||||
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
@@ -284,7 +284,7 @@ class TrezorPlugin(HW_PluginBase):
|
||||
else:
|
||||
raise RuntimeError("Unsupported recovery method")
|
||||
|
||||
def _make_node_path(self, xpub, address_n):
|
||||
def _make_node_path(self, xpub: str, address_n: Sequence[int]):
|
||||
bip32node = BIP32Node.from_xkey(xpub)
|
||||
node = HDNodeType(
|
||||
depth=bip32node.depth,
|
||||
@@ -386,14 +386,9 @@ class TrezorPlugin(HW_PluginBase):
|
||||
script_type = self.get_trezor_input_script_type(wallet.txin_type)
|
||||
|
||||
# prepare multisig, if available:
|
||||
xpubs = wallet.get_master_public_keys()
|
||||
if len(xpubs) > 1:
|
||||
pubkeys = wallet.get_public_keys(address)
|
||||
# sort xpubs using the order of pubkeys
|
||||
sorted_pairs = sorted(zip(pubkeys, xpubs))
|
||||
multisig = self._make_multisig(
|
||||
wallet.m,
|
||||
[(xpub, deriv_suffix) for pubkey, xpub in sorted_pairs])
|
||||
desc = wallet.get_script_descriptor_for_address(address)
|
||||
if multi := desc.get_simple_multisig():
|
||||
multisig = self._make_multisig(multi)
|
||||
else:
|
||||
multisig = None
|
||||
|
||||
@@ -419,8 +414,7 @@ class TrezorPlugin(HW_PluginBase):
|
||||
assert keystore
|
||||
assert (desc := txin.script_descriptor)
|
||||
if multi := desc.get_simple_multisig():
|
||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txin)
|
||||
txinputtype.multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||
txinputtype.multisig = self._make_multisig(multi)
|
||||
txinputtype.script_type = self.get_trezor_input_script_type(desc.to_legacy_electrum_script_type())
|
||||
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txin)
|
||||
if full_path:
|
||||
@@ -434,14 +428,18 @@ class TrezorPlugin(HW_PluginBase):
|
||||
|
||||
return inputs
|
||||
|
||||
def _make_multisig(self, m, xpubs):
|
||||
if len(xpubs) == 1:
|
||||
return None
|
||||
pubkeys = [self._make_node_path(xpub, deriv) for xpub, deriv in xpubs]
|
||||
def _make_multisig(self, desc: descriptor.MultisigDescriptor):
|
||||
pubkeys = []
|
||||
for pubkey_provider in desc.pubkeys:
|
||||
assert not pubkey_provider.is_range()
|
||||
assert pubkey_provider.extkey is not None
|
||||
xpub = pubkey_provider.pubkey
|
||||
der_suffix = pubkey_provider.get_der_suffix_int_list()
|
||||
pubkeys.append(self._make_node_path(xpub, der_suffix))
|
||||
return MultisigRedeemScriptType(
|
||||
pubkeys=pubkeys,
|
||||
signatures=[b''] * len(pubkeys),
|
||||
m=m)
|
||||
m=desc.thresh)
|
||||
|
||||
def tx_outputs(self, tx: PartialTransaction, *, keystore: 'TrezorKeyStore'):
|
||||
|
||||
@@ -449,8 +447,7 @@ class TrezorPlugin(HW_PluginBase):
|
||||
assert (desc := txout.script_descriptor)
|
||||
script_type = self.get_trezor_output_script_type(desc.to_legacy_electrum_script_type())
|
||||
if multi := desc.get_simple_multisig():
|
||||
xpubs_and_deriv_suffixes = get_xpubs_and_der_suffixes_from_txinout(tx, txout)
|
||||
multisig = self._make_multisig(multi.thresh, xpubs_and_deriv_suffixes)
|
||||
multisig = self._make_multisig(multi)
|
||||
else:
|
||||
multisig = None
|
||||
my_pubkey, full_path = keystore.find_my_pubkey_in_txinout(txout)
|
||||
|
||||
Reference in New Issue
Block a user