URI: 
       tenable multisig with trezor - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit c224a9ad9d63b3f20f7e0c796352ba2b0f518a4c
   DIR parent 93e8c7da6ee3217db7f0195fc782c936b25688c2
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Sat,  4 Jul 2015 12:10:52 +0200
       
       enable multisig with trezor
       
       Diffstat:
         M lib/transaction.py                  |      30 ++++++++++++++++++++++++++++++
         M plugins/trezor.py                   |      93 +++++++++++++++++++++----------
       
       2 files changed, 95 insertions(+), 28 deletions(-)
       ---
   DIR diff --git a/lib/transaction.py b/lib/transaction.py
       t@@ -487,6 +487,36 @@ class Transaction:
                self.raw = raw
                self.deserialize()
        
       +    def update_signatures(self, raw):
       +        """Add new signatures to a transaction"""
       +        d = deserialize(raw)
       +        for i, txin in enumerate(self.inputs):
       +            sigs1 = txin.get('signatures')
       +            sigs2 = d['inputs'][i].get('signatures')
       +            for sig in sigs2:
       +                if sig in sigs1:
       +                    continue
       +                for_sig = Hash(self.tx_for_sig(i).decode('hex'))
       +                # der to string
       +                order = ecdsa.ecdsa.generator_secp256k1.order()
       +                r, s = ecdsa.util.sigdecode_der(sig.decode('hex'), order)
       +                sig_string = ecdsa.util.sigencode_string(r, s, order)
       +                pubkeys = txin.get('pubkeys')
       +                compressed = True
       +                for recid in range(4):
       +                    public_key = MyVerifyingKey.from_signature(sig_string, recid, for_sig, curve = SECP256k1)
       +                    pubkey = point_to_ser(public_key.pubkey.point, compressed).encode('hex')
       +                    if pubkey in pubkeys:
       +                        public_key.verify_digest(sig_string, for_sig, sigdecode = ecdsa.util.sigdecode_string)
       +                        j = pubkeys.index(pubkey)
       +                        print_error("adding sig", i, j, pubkey, sig)
       +                        self.inputs[i]['signatures'][j] = sig
       +                        self.inputs[i]['x_pubkeys'][j] = pubkey
       +                        break
       +        # redo raw
       +        self.raw = self.serialize()
       +
       +
            def deserialize(self):
                d = deserialize(self.raw)
                self.inputs = d['inputs']
   DIR diff --git a/plugins/trezor.py b/plugins/trezor.py
       t@@ -1,5 +1,3 @@
       -from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
       -import PyQt4.QtCore as QtCore
        from binascii import unhexlify
        from struct import pack
        from sys import stderr
       t@@ -7,13 +5,17 @@ from time import sleep
        from base64 import b64encode, b64decode
        import unicodedata
        import threading
       +import re
       +
       +from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
       +import PyQt4.QtCore as QtCore
        
        import electrum
        from electrum.account import BIP32_Account
        from electrum.bitcoin import EncodeBase58Check, public_key_to_bc_address, bc_address_to_hash_160
        from electrum.i18n import _
        from electrum.plugins import BasePlugin, hook, always_hook, run_hook
       -from electrum.transaction import deserialize
       +from electrum.transaction import Transaction, deserialize, is_extended_pubkey, x_to_xpub
        from electrum.wallet import BIP32_HD_Wallet
        from electrum.util import print_error, print_msg
        from electrum.wallet import pw_decode, bip32_private_derivation, bip32_root
       t@@ -30,6 +32,8 @@ try:
        except ImportError:
            TREZOR = False
        
       +import trezorlib.ckd_public as ckd_public
       +
        def log(msg):
            stderr.write("%s\n" % msg)
            stderr.flush()
       t@@ -234,43 +238,68 @@ class Plugin(BasePlugin):
                #finally:
                self.handler.stop()
        
       -        #values = [i['value'] for i in tx.inputs]
                raw = signed_tx.encode('hex')
       -        tx.update(raw)
       -        #for i, txinput in enumerate(tx.inputs):
       -        #    txinput['value'] = values[i]
       +        tx.update_signatures(raw)
       +
        
            def tx_inputs(self, tx, for_sig=False):
                inputs = []
       -        for txinput in tx.inputs:
       +        for txin in tx.inputs:
       +            print txin
       +
                    txinputtype = types.TxInputType()
       -            if ('is_coinbase' in txinput and txinput['is_coinbase']):
       +            if txin.get('is_coinbase'):
                        prev_hash = "\0"*32
                        prev_index = 0xffffffff # signed int -1
                    else:
       -
                        if for_sig:
       -                    x_pubkey = txinput['x_pubkeys'][0]
       -                    xpub, s = BIP32_Account.parse_xpubkey(x_pubkey)
       -                    xpub_n = self.get_client().expand_path(self.xpub_path[xpub])
       -                    txinputtype.address_n.extend(xpub_n + s)
       -
       -                prev_hash = unhexlify(txinput['prevout_hash'])
       -                prev_index = txinput['prevout_n']
       +                    x_pubkeys = txin['x_pubkeys']
       +                    if len(x_pubkeys) == 1:
       +                        x_pubkey = x_pubkeys[0]
       +                        xpub, s = BIP32_Account.parse_xpubkey(x_pubkey)
       +                        xpub_n = self.get_client().expand_path(self.xpub_path[xpub])
       +                        txinputtype.address_n.extend(xpub_n + s)
       +                    else:
       +                        def f(x_pubkey):
       +                            xpub, s = BIP32_Account.parse_xpubkey(x_pubkey)
       +                            node = ckd_public.deserialize(xpub)
       +                            return types.HDNodePathType(node=node, address_n=s)
       +                        pubkeys = map(f, x_pubkeys)
       +                        multisig = types.MultisigRedeemScriptType(
       +                            pubkeys=pubkeys,
       +                            signatures=map(lambda x: x if x else '', txin.get('signatures')),
       +                            m=txin.get('num_sig'),
       +                        )
       +                        txinputtype = types.TxInputType(
       +                            script_type=types.SPENDMULTISIG,
       +                            multisig= multisig
       +                        )
       +                        # find which key is mine
       +                        for x_pubkey in x_pubkeys:
       +                            xpub, s = BIP32_Account.parse_xpubkey(x_pubkey)
       +                            if xpub in self.xpub_path:
       +                                xpub_n = self.get_client().expand_path(self.xpub_path[xpub])
       +                                txinputtype.address_n.extend(xpub_n + s)
       +                                break
       +                            else:
       +                                raise
       +
       +                prev_hash = unhexlify(txin['prevout_hash'])
       +                prev_index = txin['prevout_n']
        
                    txinputtype.prev_hash = prev_hash
                    txinputtype.prev_index = prev_index
        
       -            if 'scriptSig' in txinput:
       -                script_sig = txinput['scriptSig'].decode('hex')
       +            if 'scriptSig' in txin:
       +                script_sig = txin['scriptSig'].decode('hex')
                        txinputtype.script_sig = script_sig
        
       -            if 'sequence' in txinput:
       -                sequence = txinput['sequence']
       +            if 'sequence' in txin:
       +                sequence = txin['sequence']
                        txinputtype.sequence = sequence
        
                    inputs.append(txinputtype)
       -            #TODO P2SH
       +
                return inputs
        
            def tx_outputs(self, tx):
       t@@ -440,14 +469,22 @@ class TrezorWallet(BIP32_HD_Wallet):
                xpub_path = {}
                for txin in tx.inputs:
                    tx_hash = txin['prevout_hash']
       -            prev_tx[tx_hash] = self.transactions[tx_hash]
       -            address = txin['address']
       -            address_path = self.address_id(address)
       -            account_id, (change, address_index) = self.get_address_index(address)
       +
       +            ptx = self.transactions.get(tx_hash)
       +            if ptx is None:
       +                ptx = self.network.synchronous_get([('blockchain.transaction.get', [tx_hash])])[0]
       +                ptx = Transaction(ptx)
       +            prev_tx[tx_hash] = ptx
        
                    for x_pubkey in txin['x_pubkeys']:
       -                xpub, s = BIP32_Account.parse_xpubkey(x_pubkey)
       -                xpub_path[xpub] = "44'/0'/%s'"%account_id
       +                if not is_extended_pubkey(x_pubkey):
       +                    continue
       +                xpub = x_to_xpub(x_pubkey)
       +                for k, v in self.master_public_keys.items():
       +                    if v == xpub:
       +                        account_id = re.match("x/(\d+)'", k).group(1)
       +                        account_derivation = "44'/0'/%s'"%account_id
       +                xpub_path[xpub] = account_derivation
        
                self.plugin.sign_transaction(tx, prev_tx, xpub_path)