commit 5e92e09044b718f5e4df092dc048ccf36031da60
parent 11ba5c441ab745ddec3f0e2176817bcabae0fa17
Author: ThomasV <thomasv@electrum.org>
Date: Sun, 16 Oct 2016 13:16:40 +0200
fix pubkey ordering in multisig wallets. fix #1975
Diffstat:
3 files changed, 60 insertions(+), 41 deletions(-)
diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
@@ -1667,27 +1667,23 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
def show_master_public_keys(self):
dialog = WindowModalDialog(self, "Master Public Keys")
- mpk_dict = self.wallet.get_master_public_keys()
+ mpk_list = self.wallet.get_master_public_keys()
vbox = QVBoxLayout()
mpk_text = ShowQRTextEdit()
mpk_text.setMaximumHeight(100)
mpk_text.addCopyButton(self.app)
- sorted_keys = sorted(mpk_dict.keys())
def show_mpk(index):
- mpk_text.setText(mpk_dict[sorted_keys[index]])
+ mpk_text.setText(mpk_list[index])
# only show the combobox in case multiple accounts are available
- if len(mpk_dict) > 1:
+ if len(mpk_list) > 1:
def label(key):
if isinstance(self.wallet, Multisig_Wallet):
- is_mine = False#self.wallet.master_private_keys.has_key(key)
- mine_text = [_("cosigner"), _("self")]
- return "%s (%s)" % (key, mine_text[is_mine])
- return key
- labels = list(map(label, sorted_keys))
+ return _("cosigner") + ' ' + str(i+1)
+ return ''
+ labels = [ label(i) for i in range(len(mpk_list))]
on_click = lambda clayout: show_mpk(clayout.selected_index())
- labels_clayout = ChoicesLayout(_("Master Public Keys"), labels,
- on_click)
+ labels_clayout = ChoicesLayout(_("Master Public Keys"), labels, on_click)
vbox.addLayout(labels_clayout.layout())
show_mpk(0)
diff --git a/lib/wallet.py b/lib/wallet.py
@@ -1063,11 +1063,17 @@ class Abstract_Wallet(PrintError):
txin['prev_tx'] = self.get_input_tx(tx_hash)
# add output info for hw wallets
- tx.output_info = []
- for i, txout in enumerate(tx.outputs()):
+ info = {}
+ xpubs = self.get_master_public_keys()
+ for txout in tx.outputs():
_type, addr, amount = txout
- change, address_index = self.get_address_index(addr) if self.is_change(addr) else (None, None)
- tx.output_info.append((change, address_index))
+ if self.is_change(addr):
+ index = self.get_address_index(addr)
+ pubkeys = self.get_public_keys(addr)
+ # sort xpubs using the order of pubkeys
+ sorted_pubkeys, sorted_xpubs = zip(*sorted(zip(pubkeys, xpubs)))
+ info[addr] = index, sorted_xpubs, self.m if isinstance(self, Multisig_Wallet) else None
+ tx.output_info = info
# sign
for k in self.get_keystores():
@@ -1301,7 +1307,7 @@ class Imported_Wallet(Abstract_Wallet):
return False
def get_master_public_keys(self):
- return {}
+ return []
def is_beyond_limit(self, address, is_change):
return False
@@ -1506,7 +1512,7 @@ class Deterministic_Wallet(Abstract_Wallet):
return True
def get_master_public_keys(self):
- return {'x':self.get_master_public_key()}
+ return [self.get_master_public_key()]
def get_fingerprint(self):
return self.get_master_public_key()
@@ -1599,7 +1605,7 @@ class Multisig_Wallet(Deterministic_Wallet):
return address
def new_pubkeys(self, c, i):
- return [k.derive_pubkey(c, i) for k in self.keystores.values()]
+ return [k.derive_pubkey(c, i) for k in self.get_keystores()]
def load_keystore(self):
self.keystores = {}
@@ -1616,7 +1622,7 @@ class Multisig_Wallet(Deterministic_Wallet):
return self.keystores.get('x1/')
def get_keystores(self):
- return self.keystores.values()
+ return [self.keystores[i] for i in sorted(self.keystores.keys())]
def update_password(self, old_pw, new_pw):
for name, keystore in self.keystores.items():
@@ -1640,7 +1646,7 @@ class Multisig_Wallet(Deterministic_Wallet):
return self.keystore.get_master_public_key()
def get_master_public_keys(self):
- return dict(map(lambda x: (x[0], x[1].get_master_public_key()), self.keystores.items()))
+ return [k.get_master_public_key() for k in self.get_keystores()]
def get_fingerprint(self):
return ''.join(sorted(self.get_master_public_keys()))
diff --git a/plugins/trezor/plugin.py b/plugins/trezor/plugin.py
@@ -312,31 +312,48 @@ class TrezorCompatiblePlugin(HW_PluginBase):
def tx_outputs(self, derivation, tx):
outputs = []
has_change = False
- for i, (_type, address, amount) in enumerate(tx.outputs()):
- txoutputtype = self.types.TxOutputType()
- txoutputtype.amount = amount
- change, index = tx.output_info[i]
- if _type == TYPE_SCRIPT:
- txoutputtype.script_type = self.types.PAYTOOPRETURN
- txoutputtype.op_return_data = address[2:]
- elif _type == TYPE_ADDRESS:
+
+ for _type, address, amount in tx.outputs():
+ 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 = bc_address_to_hash_160(address)
- if addrtype == 0 and change is not None and not has_change:
- address_path = "%s/%d/%d"%(derivation, change, index)
- address_n = self.client_class.expand_path(address_path)
- txoutputtype.address_n.extend(address_n)
- # do not show more than one change address to device
- has_change = True
- else:
- txoutputtype.address = address
+ index, xpubs, m = info
if addrtype == 0:
- txoutputtype.script_type = self.types.PAYTOADDRESS
+ address_n = self.client_class.expand_path(derivation + "/%d/%d"%index)
+ txoutputtype = self.types.TxOutputType(
+ amount = amount,
+ script_type = self.types.PAYTOADDRESS,
+ address_n = address_n,
+ )
elif addrtype == 5:
- txoutputtype.script_type = self.types.PAYTOSCRIPTHASH
- else:
- raise BaseException('addrtype')
+ 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'', b'', b''],
+ m = m)
+ txoutputtype = self.types.TxOutputType(
+ multisig = multisig,
+ amount = amount,
+ script_type = self.types.PAYTOMULTISIG)
else:
- raise BaseException('addrtype')
+ 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:
+ addrtype, hash_160 = bc_address_to_hash_160(address)
+ if addrtype == 0:
+ txoutputtype.script_type = self.types.PAYTOADDRESS
+ elif addrtype == 5:
+ txoutputtype.script_type = self.types.PAYTOSCRIPTHASH
+ else:
+ raise BaseException('addrtype')
+ txoutputtype.address = address
+
outputs.append(txoutputtype)
return outputs