enable multisig with trezor
This commit is contained in:
@@ -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']
|
||||||
|
|||||||
@@ -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']
|
||||||
xpub, s = BIP32_Account.parse_xpubkey(x_pubkey)
|
if len(x_pubkeys) == 1:
|
||||||
xpub_n = self.get_client().expand_path(self.xpub_path[xpub])
|
x_pubkey = x_pubkeys[0]
|
||||||
txinputtype.address_n.extend(xpub_n + s)
|
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_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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user