URI: 
       ttrezor: move signing logic from wallet class to plugin - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 98d0ff75365b50147075d1a9248dfe8779a2aaf1
   DIR parent 1919d5d63c64a06396b08c1fe6c594d616a1feb2
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Sun, 28 Jun 2015 10:20:54 +0200
       
       ttrezor: move signing logic from wallet class to plugin
       
       Diffstat:
         M plugins/trezor.py                   |     304 ++++++++++++++++---------------
       
       1 file changed, 154 insertions(+), 150 deletions(-)
       ---
   DIR diff --git a/plugins/trezor.py b/plugins/trezor.py
       t@@ -47,6 +47,8 @@ class Plugin(BasePlugin):
                self._requires_settings = True
                self.wallet = None
                self.handler = None
       +        self.client = None
       +        self.transport = None
        
            def constructor(self, s):
                return TrezorWallet(s)
       t@@ -76,20 +78,47 @@ class Plugin(BasePlugin):
                    return False
                return True
        
       +    def compare_version(self, major, minor=0, patch=0):
       +        features = self.get_client().features
       +        return cmp([features.major_version, features.minor_version, features.patch_version], [major, minor, patch])
       +
       +    def atleast_version(self, major, minor=0, patch=0):
       +        return self.compare_version(major, minor, patch) >= 0
       +
            def trezor_is_connected(self):
       +        self.get_client().ping('t')
                try:
       -            self.wallet.get_client().ping('t')
       +            self.get_client().ping('t')
                except:
                    return False
                return True
        
       +    def get_client(self):
       +        if not TREZOR:
       +            give_error('please install github.com/trezor/python-trezor')
       +
       +        if not self.client or self.client.bad:
       +            try:
       +                d = HidTransport.enumerate()[0]
       +                self.transport = HidTransport(d)
       +            except:
       +                give_error('Could not connect to your Trezor. Please verify the cable is connected and that no other app is using it.')
       +            self.client = QtGuiTrezorClient(self.transport)
       +            self.client.handler = self.handler
       +            self.client.set_tx_api(self)
       +            #self.client.clear_session()# TODO Doesn't work with firmware 1.1, returns proto.Failure
       +            self.client.bad = False
       +            if not self.atleast_version(1, 2, 1):
       +                give_error('Outdated Trezor firmware. Please update the firmware from https://www.mytrezor.com')
       +        return self.client
        
            @hook
            def close_wallet(self):
                print_error("trezor: clear session")
       -        if self.wallet and self.wallet.client:
       -            self.wallet.client.clear_session()
       -            self.wallet.client.transport.close()
       +        if self.client:
       +            self.client.clear_session()
       +            self.client.transport.close()
       +            self.client = None
                self.wallet = None
        
            @hook
       t@@ -143,21 +172,36 @@ class Plugin(BasePlugin):
        
            @hook
            def receive_menu(self, menu, addrs):
       -        if not self.wallet.is_watching_only() and self.wallet.atleast_version(1, 3) and len(addrs) == 1:
       -            menu.addAction(_("Show on TREZOR"), lambda: self.wallet.show_address(addrs[0]))
       +        if not self.wallet.is_watching_only() and self.atleast_version(1, 3) and len(addrs) == 1:
       +            menu.addAction(_("Show on TREZOR"), lambda: self.show_address(addrs[0]))
       +
       +    def show_address(self, address):
       +        if not self.wallet.check_proper_device():
       +            give_error('Wrong device or password')
       +        try:
       +            address_path = self.wallet.address_id(address)
       +            address_n = self.get_client().expand_path(address_path)
       +        except Exception, e:
       +            give_error(e)
       +        try:
       +            self.get_client().get_address('Bitcoin', address_n, True)
       +        except Exception, e:
       +            give_error(e)
       +        finally:
       +            self.handler.stop()
        
            def settings_widget(self, window):
                return EnterButton(_('Settings'), self.settings_dialog)
        
            def settings_dialog(self):
       -        get_label = lambda: self.wallet.get_client().features.label
       +        get_label = lambda: self.get_client().features.label
                update_label = lambda: current_label_label.setText("Label: %s" % get_label())
        
                d = QDialog()
                layout = QGridLayout(d)
                layout.addWidget(QLabel("Trezor Options"),0,0)
                layout.addWidget(QLabel("ID:"),1,0)
       -        layout.addWidget(QLabel(" %s" % self.wallet.get_client().get_device_id()),1,1)
       +        layout.addWidget(QLabel(" %s" % self.get_client().get_device_id()),1,1)
        
                def modify_label():
                    response = QInputDialog().getText(None, "Set New Trezor Label", "New Trezor Label:  (upon submission confirm on Trezor)")
       t@@ -165,7 +209,7 @@ class Plugin(BasePlugin):
                        return
                    new_label = str(response[0])
                    self.handler.show_message("Please confirm label change on Trezor")
       -            status = self.wallet.get_client().apply_settings(label=new_label)
       +            status = self.get_client().apply_settings(label=new_label)
                    self.handler.stop()
                    update_label()
        
       t@@ -181,6 +225,100 @@ class Plugin(BasePlugin):
                else:
                    return False
        
       +    def sign_transaction(self, tx):
       +        if tx.is_complete():
       +            return
       +        client = self.get_client()
       +        inputs = self.tx_inputs(tx)
       +        outputs = self.tx_outputs(tx)
       +        try:
       +            signed_tx = client.sign_tx('Bitcoin', inputs, outputs)[1]
       +        except Exception, e:
       +            give_error(e)
       +        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]
       +
       +    def tx_inputs(self, tx):
       +        inputs = []
       +        for txinput in tx.inputs:
       +            txinputtype = types.TxInputType()
       +            if ('is_coinbase' in txinput and txinput['is_coinbase']):
       +                prev_hash = "\0"*32
       +                prev_index = 0xffffffff # signed int -1
       +            else:
       +                address = txinput['address']
       +                try:
       +                    address_path = self.wallet.address_id(address)
       +                    address_n = self.get_client().expand_path(address_path)
       +                    txinputtype.address_n.extend(address_n)
       +                except: pass
       +
       +                prev_hash = unhexlify(txinput['prevout_hash'])
       +                prev_index = txinput['prevout_n']
       +
       +            txinputtype.prev_hash = prev_hash
       +            txinputtype.prev_index = prev_index
       +
       +            if 'scriptSig' in txinput:
       +                script_sig = txinput['scriptSig'].decode('hex')
       +                txinputtype.script_sig = script_sig
       +
       +            if 'sequence' in txinput:
       +                sequence = txinput['sequence']
       +                txinputtype.sequence = sequence
       +
       +            inputs.append(txinputtype)
       +            #TODO P2SH
       +        return inputs
       +
       +    def tx_outputs(self, tx):
       +        outputs = []
       +
       +        for type, address, amount in tx.outputs:
       +            assert type == 'address'
       +            txoutputtype = types.TxOutputType()
       +            if self.wallet.is_change(address):
       +                address_path = self.wallet.address_id(address)
       +                address_n = self.get_client().expand_path(address_path)
       +                txoutputtype.address_n.extend(address_n)
       +            else:
       +                txoutputtype.address = address
       +            txoutputtype.amount = amount
       +            addrtype, hash_160 = bc_address_to_hash_160(address)
       +            if addrtype == 0:
       +                txoutputtype.script_type = types.PAYTOADDRESS
       +            elif addrtype == 5:
       +                txoutputtype.script_type = types.PAYTOSCRIPTHASH
       +            else:
       +                raise BaseException('addrtype')
       +            outputs.append(txoutputtype)
       +
       +        return outputs
       +
       +    def electrum_tx_to_txtype(self, tx):
       +        t = types.TransactionType()
       +        d = deserialize(tx.raw)
       +        t.version = d['version']
       +        t.lock_time = d['lockTime']
       +        inputs = self.tx_inputs(tx)
       +        t.inputs.extend(inputs)
       +        for vout in d['outputs']:
       +            o = t.bin_outputs.add()
       +            o.amount = vout['value']
       +            o.script_pubkey = vout['scriptPubKey'].decode('hex')
       +        return t
       +
       +    def get_tx(self, tx_hash):
       +        tx = self.wallet.transactions[tx_hash]
       +        tx.deserialize()
       +        return self.electrum_tx_to_txtype(tx)
       +
       +
        
        
        class TrezorWallet(BIP32_HD_Wallet):
       t@@ -189,10 +327,9 @@ class TrezorWallet(BIP32_HD_Wallet):
        
            def __init__(self, storage):
                BIP32_HD_Wallet.__init__(self, storage)
       -        self.transport = None
       -        self.client = None
                self.mpk = None
                self.device_checked = False
       +        self.proper_device = False
                self.force_watching_only = False
        
            def get_action(self):
       t@@ -219,32 +356,7 @@ class TrezorWallet(BIP32_HD_Wallet):
                return self.force_watching_only
        
            def get_client(self):
       -        if not TREZOR:
       -            give_error('please install github.com/trezor/python-trezor')
       -
       -        if not self.client or self.client.bad:
       -            try:
       -                d = HidTransport.enumerate()[0]
       -                self.transport = HidTransport(d)
       -            except:
       -                give_error('Could not connect to your Trezor. Please verify the cable is connected and that no other app is using it.')
       -            self.client = QtGuiTrezorClient(self.transport)
       -            self.client.handler = self.plugin.handler
       -            self.client.set_tx_api(self)
       -            #self.client.clear_session()# TODO Doesn't work with firmware 1.1, returns proto.Failure
       -            self.client.bad = False
       -            self.device_checked = False
       -            self.proper_device = False
       -            if not self.atleast_version(1, 2, 1):
       -                give_error('Outdated Trezor firmware. Please update the firmware from https://www.mytrezor.com')
       -        return self.client
       -
       -    def compare_version(self, major, minor=0, patch=0):
       -        features = self.get_client().features
       -        return cmp([features.major_version, features.minor_version, features.patch_version], [major, minor, patch])
       -
       -    def atleast_version(self, major, minor=0, patch=0):
       -        return self.compare_version(major, minor, patch) >= 0
       +        return self.plugin.get_client()
        
            def address_id(self, address):
                account_id, (change, address_index) = self.get_address_index(address)
       t@@ -273,8 +385,8 @@ class TrezorWallet(BIP32_HD_Wallet):
                    return xpub, None
        
            def get_public_key(self, bip32_path):
       -        address_n = self.get_client().expand_path(bip32_path)
       -        node = self.get_client().get_public_node(address_n).node
       +        address_n = self.plugin.get_client().expand_path(bip32_path)
       +        node = self.plugin.get_client().get_public_node(address_n).node
                xpub = "0488B21E".decode('hex') + chr(node.depth) + self.i4b(node.fingerprint) + self.i4b(node.child_num) + node.chain_code + node.public_key
                return EncodeBase58Check(xpub)
        
       t@@ -303,31 +415,16 @@ class TrezorWallet(BIP32_HD_Wallet):
                #    twd.stop()
                #return str(decrypted_msg)
        
       -    def show_address(self, address):
       -        if not self.check_proper_device():
       -            give_error('Wrong device or password')
       -        try:
       -            address_path = self.address_id(address)
       -            address_n = self.get_client().expand_path(address_path)
       -        except Exception, e:
       -            give_error(e)
       -        try:
       -            self.get_client().get_address('Bitcoin', address_n, True)
       -        except Exception, e:
       -            give_error(e)
       -        finally:
       -            self.plugin.handler.stop()
       -
            def sign_message(self, address, message, password):
                if not self.check_proper_device():
                    give_error('Wrong device or password')
                try:
                    address_path = self.address_id(address)
       -            address_n = self.get_client().expand_path(address_path)
       +            address_n = self.plugin.get_client().expand_path(address_path)
                except Exception, e:
                    give_error(e)
                try:
       -            msg_sig = self.get_client().sign_message('Bitcoin', address_n, message)
       +            msg_sig = self.plugin.get_client().sign_message('Bitcoin', address_n, message)
                except Exception, e:
                    give_error(e)
                finally:
       t@@ -336,102 +433,9 @@ class TrezorWallet(BIP32_HD_Wallet):
                return str(b64_msg_sig)
        
            def sign_transaction(self, tx, password):
       -        if tx.is_complete():
       -            return
                if not self.check_proper_device():
                    give_error('Wrong device or password')
       -        client = self.get_client()
       -        inputs = self.tx_inputs(tx)
       -        outputs = self.tx_outputs(tx)
       -        try:
       -            signed_tx = self.get_client().sign_tx('Bitcoin', inputs, outputs)[1]
       -        except Exception, e:
       -            give_error(e)
       -        finally:
       -            self.plugin.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]
       -
       -    def tx_inputs(self, tx):
       -        inputs = []
       -        for txinput in tx.inputs:
       -            txinputtype = types.TxInputType()
       -            if ('is_coinbase' in txinput and txinput['is_coinbase']):
       -                prev_hash = "\0"*32
       -                prev_index = 0xffffffff # signed int -1
       -            else:
       -                address = txinput['address']
       -                try:
       -                    address_path = self.address_id(address)
       -                    address_n = self.get_client().expand_path(address_path)
       -                    txinputtype.address_n.extend(address_n)
       -                except: pass
       -
       -                prev_hash = unhexlify(txinput['prevout_hash'])
       -                prev_index = txinput['prevout_n']
       -
       -            txinputtype.prev_hash = prev_hash
       -            txinputtype.prev_index = prev_index
       -
       -            if 'scriptSig' in txinput:
       -                script_sig = txinput['scriptSig'].decode('hex')
       -                txinputtype.script_sig = script_sig
       -
       -            if 'sequence' in txinput:
       -                sequence = txinput['sequence']
       -                txinputtype.sequence = sequence
       -
       -            inputs.append(txinputtype)
       -            #TODO P2SH
       -        return inputs
       -
       -    def tx_outputs(self, tx):
       -        outputs = []
       -
       -        for type, address, amount in tx.outputs:
       -            assert type == 'address'
       -            txoutputtype = types.TxOutputType()
       -            if self.is_change(address):
       -                address_path = self.address_id(address)
       -                address_n = self.get_client().expand_path(address_path)
       -                txoutputtype.address_n.extend(address_n)
       -            else:
       -                txoutputtype.address = address
       -            txoutputtype.amount = amount
       -            addrtype, hash_160 = bc_address_to_hash_160(address)
       -            if addrtype == 0:
       -                txoutputtype.script_type = types.PAYTOADDRESS
       -            elif addrtype == 5:
       -                txoutputtype.script_type = types.PAYTOSCRIPTHASH
       -            else:
       -                raise BaseException('addrtype')
       -            outputs.append(txoutputtype)
       -
       -        return outputs
       -
       -    def electrum_tx_to_txtype(self, tx):
       -        t = types.TransactionType()
       -        d = deserialize(tx.raw)
       -        t.version = d['version']
       -        t.lock_time = d['lockTime']
       -
       -        inputs = self.tx_inputs(tx)
       -        t.inputs.extend(inputs)
       -
       -        for vout in d['outputs']:
       -            o = t.bin_outputs.add()
       -            o.amount = vout['value']
       -            o.script_pubkey = vout['scriptPubKey'].decode('hex')
       -
       -        return t
       -
       -    def get_tx(self, tx_hash):
       -        tx = self.transactions[tx_hash]
       -        tx.deserialize()
       -        return self.electrum_tx_to_txtype(tx)
       +        self.plugin.sign_transaction(tx)
        
            def check_proper_device(self):
                self.get_client().ping('t')