electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit c224a9ad9d63b3f20f7e0c796352ba2b0f518a4c
parent 93e8c7da6ee3217db7f0195fc782c936b25688c2
Author: ThomasV <thomasv@gitorious>
Date:   Sat,  4 Jul 2015 12:10:52 +0200

enable multisig with trezor

Diffstat:
Mlib/transaction.py | 30++++++++++++++++++++++++++++++
Mplugins/trezor.py | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
2 files changed, 95 insertions(+), 28 deletions(-)

diff --git a/lib/transaction.py b/lib/transaction.py @@ -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'] diff --git a/plugins/trezor.py b/plugins/trezor.py @@ -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 @@ -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 @@ -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() @@ -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): @@ -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)