Browse Source

enable multisig with trezor

master
ThomasV 11 years ago
parent
commit
c224a9ad9d
  1. 30
      lib/transaction.py
  2. 85
      plugins/trezor.py

30
lib/transaction.py

@ -487,6 +487,36 @@ class Transaction:
self.raw = raw self.raw = raw
self.deserialize() 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): def deserialize(self):
d = deserialize(self.raw) d = deserialize(self.raw)
self.inputs = d['inputs'] self.inputs = d['inputs']

85
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 binascii import unhexlify
from struct import pack from struct import pack
from sys import stderr from sys import stderr
@ -7,13 +5,17 @@ from time import sleep
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
import unicodedata import unicodedata
import threading import threading
import re
from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
import PyQt4.QtCore as QtCore
import electrum import electrum
from electrum.account import BIP32_Account from electrum.account import BIP32_Account
from electrum.bitcoin import EncodeBase58Check, public_key_to_bc_address, bc_address_to_hash_160 from electrum.bitcoin import EncodeBase58Check, public_key_to_bc_address, bc_address_to_hash_160
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugins import BasePlugin, hook, always_hook, run_hook 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.wallet import BIP32_HD_Wallet
from electrum.util import print_error, print_msg from electrum.util import print_error, print_msg
from electrum.wallet import pw_decode, bip32_private_derivation, bip32_root from electrum.wallet import pw_decode, bip32_private_derivation, bip32_root
@ -30,6 +32,8 @@ try:
except ImportError: except ImportError:
TREZOR = False TREZOR = False
import trezorlib.ckd_public as ckd_public
def log(msg): def log(msg):
stderr.write("%s\n" % msg) stderr.write("%s\n" % msg)
stderr.flush() stderr.flush()
@ -234,43 +238,68 @@ class Plugin(BasePlugin):
#finally: #finally:
self.handler.stop() self.handler.stop()
#values = [i['value'] for i in tx.inputs]
raw = signed_tx.encode('hex') raw = signed_tx.encode('hex')
tx.update(raw) tx.update_signatures(raw)
#for i, txinput in enumerate(tx.inputs):
# txinput['value'] = values[i]
def tx_inputs(self, tx, for_sig=False): def tx_inputs(self, tx, for_sig=False):
inputs = [] inputs = []
for txinput in tx.inputs: for txin in tx.inputs:
print txin
txinputtype = types.TxInputType() txinputtype = types.TxInputType()
if ('is_coinbase' in txinput and txinput['is_coinbase']): if txin.get('is_coinbase'):
prev_hash = "\0"*32 prev_hash = "\0"*32
prev_index = 0xffffffff # signed int -1 prev_index = 0xffffffff # signed int -1
else: else:
if for_sig: if for_sig:
x_pubkey = txinput['x_pubkeys'][0] 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) 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]) xpub_n = self.get_client().expand_path(self.xpub_path[xpub])
txinputtype.address_n.extend(xpub_n + s) txinputtype.address_n.extend(xpub_n + s)
break
else:
raise
prev_hash = unhexlify(txinput['prevout_hash']) prev_hash = unhexlify(txin['prevout_hash'])
prev_index = txinput['prevout_n'] prev_index = txin['prevout_n']
txinputtype.prev_hash = prev_hash txinputtype.prev_hash = prev_hash
txinputtype.prev_index = prev_index txinputtype.prev_index = prev_index
if 'scriptSig' in txinput: if 'scriptSig' in txin:
script_sig = txinput['scriptSig'].decode('hex') script_sig = txin['scriptSig'].decode('hex')
txinputtype.script_sig = script_sig txinputtype.script_sig = script_sig
if 'sequence' in txinput: if 'sequence' in txin:
sequence = txinput['sequence'] sequence = txin['sequence']
txinputtype.sequence = sequence txinputtype.sequence = sequence
inputs.append(txinputtype) inputs.append(txinputtype)
#TODO P2SH
return inputs return inputs
def tx_outputs(self, tx): def tx_outputs(self, tx):
@ -440,14 +469,22 @@ class TrezorWallet(BIP32_HD_Wallet):
xpub_path = {} xpub_path = {}
for txin in tx.inputs: for txin in tx.inputs:
tx_hash = txin['prevout_hash'] tx_hash = txin['prevout_hash']
prev_tx[tx_hash] = self.transactions[tx_hash]
address = txin['address'] ptx = self.transactions.get(tx_hash)
address_path = self.address_id(address) if ptx is None:
account_id, (change, address_index) = self.get_address_index(address) 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']: for x_pubkey in txin['x_pubkeys']:
xpub, s = BIP32_Account.parse_xpubkey(x_pubkey) if not is_extended_pubkey(x_pubkey):
xpub_path[xpub] = "44'/0'/%s'"%account_id 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) self.plugin.sign_transaction(tx, prev_tx, xpub_path)

Loading…
Cancel
Save