enable multisig with trezor

This commit is contained in:
ThomasV 2015-07-04 12:10:52 +02:00
parent 93e8c7da6e
commit c224a9ad9d
2 changed files with 94 additions and 27 deletions

View File

@ -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']

View File

@ -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)
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(txinput['prevout_hash'])
prev_index = txinput['prevout_n']
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)