commit c224a9ad9d63b3f20f7e0c796352ba2b0f518a4c
parent 93e8c7da6ee3217db7f0195fc782c936b25688c2
Author: ThomasV <thomasv@gitorious>
Date: Sat, 4 Jul 2015 12:10:52 +0200
enable multisig with trezor
Diffstat:
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)