commit 16f36ee6e2663a1fdcfd193f57d1fa25b6a45866
parent c4ce16e2b621e5103503471baea2e8e103751252
Author: ThomasV <thomasv@gitorious>
Date: Fri, 31 Oct 2014 13:01:16 +0100
Extend transaction serialization, format to handle unsigned inputs where only the address is known, the public key is unknown.
Diffstat:
M | lib/transaction.py | | | 77 | ++++++++++++++++++++++++++++++++++------------------------------------------- |
M | lib/wallet.py | | | 114 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
2 files changed, 93 insertions(+), 98 deletions(-)
diff --git a/lib/transaction.py b/lib/transaction.py
@@ -332,9 +332,16 @@ def parse_xpub(x_pubkey):
from account import OldAccount
mpk, s = OldAccount.parse_xpubkey(x_pubkey)
pubkey = OldAccount.get_pubkey_from_mpk(mpk.decode('hex'), s[0], s[1])
+ elif x_pubkey[0:2] == 'fd':
+ addrtype = ord(x_pubkey[2:4].decode('hex'))
+ hash160 = x_pubkey[4:].decode('hex')
+ pubkey = None
+ address = hash_160_to_bc_address(hash160, addrtype)
else:
raise BaseException("Cannnot parse pubkey")
- return pubkey
+ if pubkey:
+ address = public_key_to_bc_address(pubkey.decode('hex'))
+ return pubkey, address
def parse_scriptSig(d, bytes):
@@ -365,7 +372,7 @@ def parse_scriptSig(d, bytes):
x_pubkey = decoded[1][1].encode('hex')
try:
signatures = parse_sig([sig])
- pubkey = parse_xpub(x_pubkey)
+ pubkey, address = parse_xpub(x_pubkey)
except:
import traceback
traceback.print_exc(file=sys.stdout)
@@ -375,7 +382,7 @@ def parse_scriptSig(d, bytes):
d['x_pubkeys'] = [x_pubkey]
d['num_sig'] = 1
d['pubkeys'] = [pubkey]
- d['address'] = public_key_to_bc_address(pubkey.decode('hex'))
+ d['address'] = address
return
# p2sh transaction, 2 of n
@@ -639,7 +646,11 @@ class Transaction:
sig_list = ''.join( map( lambda x: push_script(x), sig_list))
if not p2sh:
script = sig_list
- script += push_script(pubkeys[0])
+ x_pubkey = pubkeys[0]
+ if x_pubkey is None:
+ addrtype, h160 = bc_address_to_hash_160(txin['address'])
+ x_pubkey = 'fd' + (chr(addrtype) + h160).encode('hex')
+ script += push_script(x_pubkey)
else:
script = '00' # op_0
script += sig_list
@@ -673,16 +684,6 @@ class Transaction:
def hash(self):
return Hash(self.raw.decode('hex') )[::-1].encode('hex')
- def add_signature(self, i, pubkey, sig):
- print_error("adding signature for", pubkey)
- txin = self.inputs[i]
- pubkeys = txin['pubkeys']
- ii = pubkeys.index(pubkey)
- txin['signatures'][ii] = sig
- txin['x_pubkeys'][ii] = pubkey
- self.inputs[i] = txin
- self.raw = self.serialize()
-
def add_input(self, input):
self.inputs.append(input)
self.raw = None
@@ -707,66 +708,56 @@ class Transaction:
r += txin['num_sig']
return s, r
-
def is_complete(self):
s, r = self.signature_count()
return r == s
-
def inputs_to_sign(self):
- from account import BIP32_Account, OldAccount
- xpub_list = []
- addr_list = set()
+ out = set()
for txin in self.inputs:
x_signatures = txin['signatures']
signatures = filter(lambda x: x is not None, x_signatures)
-
if len(signatures) == txin['num_sig']:
# input is complete
continue
-
for k, x_pubkey in enumerate(txin['x_pubkeys']):
-
if x_signatures[k] is not None:
# this pubkey already signed
continue
-
- if x_pubkey[0:2] == 'ff':
- xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey)
- xpub_list.append((xpub,sequence))
- elif x_pubkey[0:2] == 'fe':
- xpub, sequence = OldAccount.parse_xpubkey(x_pubkey)
- xpub_list.append((xpub,sequence))
- else:
- addr_list.add(txin['address'])
-
- return addr_list, xpub_list
-
+ out.add(x_pubkey)
+ return out
def sign(self, keypairs):
print_error("tx.sign(), keypairs:", keypairs)
-
for i, txin in enumerate(self.inputs):
-
- # continue if this txin is complete
signatures = filter(lambda x: x is not None, txin['signatures'])
num = txin['num_sig']
if len(signatures) == num:
+ # continue if this txin is complete
continue
- redeem_pubkeys = txin['pubkeys']
- for_sig = Hash(self.tx_for_sig(i).decode('hex'))
- for pubkey in redeem_pubkeys:
- if pubkey in keypairs.keys():
+ for x_pubkey in txin['x_pubkeys']:
+ if x_pubkey in keypairs.keys():
+ print_error("adding signature for", x_pubkey)
+ # add pubkey to txin
+ txin = self.inputs[i]
+ x_pubkeys = txin['x_pubkeys']
+ ii = x_pubkeys.index(x_pubkey)
+ sec = keypairs[x_pubkey]
+ pubkey = public_key_from_private_key(sec)
+ txin['x_pubkeys'][ii] = pubkey
+ txin['pubkeys'][ii] = pubkey
+ self.inputs[i] = txin
# add signature
- sec = keypairs[pubkey]
+ for_sig = Hash(self.tx_for_sig(i).decode('hex'))
pkey = regenerate_key(sec)
secexp = pkey.secret
private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
public_key = private_key.get_verifying_key()
sig = private_key.sign_digest_deterministic( for_sig, hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_der )
assert public_key.verify_digest( sig, for_sig, sigdecode = ecdsa.util.sigdecode_der)
- self.add_signature(i, pubkey, sig.encode('hex'))
+ txin['signatures'][ii] = sig.encode('hex')
+ self.inputs[i] = txin
print_error("is_complete", self.is_complete())
self.raw = self.serialize()
diff --git a/lib/wallet.py b/lib/wallet.py
@@ -766,35 +766,15 @@ class Abstract_Wallet(object):
return
# check that the password is correct. This will raise if it's not.
self.check_password(password)
-
-
keypairs = {}
-
- # tx.inputs_to_sign() : return list of addresses or derivations
- # this list should be enriched by add_keypairs
- addr_list, xpub_list = tx.inputs_to_sign()
- for addr in addr_list:
- if self.is_mine(addr):
- private_keys = self.get_private_key(addr, password)
- for sec in private_keys:
- pubkey = public_key_from_private_key(sec)
- keypairs[ pubkey ] = sec
-
- for xpub, sequence in xpub_list:
- # look for account that can sign
- for k, account in self.accounts.items():
- if xpub in account.get_master_pubkeys():
- break
- else:
- continue
- pk = account.get_private_key(sequence, self, password)
- for sec in pk:
- pubkey = public_key_from_private_key(sec)
- keypairs[pubkey] = sec
-
+ x_pubkeys = tx.inputs_to_sign()
+ for x in x_pubkeys:
+ sec = self.get_private_key_from_xpubkey(x, password)
+ print "sec", sec
+ if sec:
+ keypairs[ x ] = sec
if keypairs:
tx.sign(keypairs)
-
run_hook('sign_transaction', tx, password)
def sendtx(self, tx):
@@ -1013,7 +993,59 @@ class Abstract_Wallet(object):
return age > age_limit
def can_sign(self, tx):
- pass
+ if self.is_watching_only():
+ return False
+ if tx.is_complete():
+ return False
+ for x in tx.inputs_to_sign():
+ if self.can_sign_xpubkey(x):
+ return True
+ return False
+
+
+ def get_private_key_from_xpubkey(self, x_pubkey, password):
+ if x_pubkey[0:2] in ['02','03','04']:
+ addr = bitcoin.public_key_to_bc_address(x_pubkey.decode('hex'))
+ if self.is_mine(addr):
+ return self.get_private_key(addr, password)[0]
+ elif x_pubkey[0:2] == 'ff':
+ xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey)
+ for k, account in self.accounts.items():
+ if xpub in account.get_master_pubkeys():
+ pk = account.get_private_key(sequence, self, password)
+ return pk[0]
+ elif x_pubkey[0:2] == 'fe':
+ xpub, sequence = OldAccount.parse_xpubkey(x_pubkey)
+ for k, account in self.accounts.items():
+ if xpub in account.get_master_pubkeys():
+ pk = account.get_private_key(sequence, self, password)
+ return pk[0]
+ elif x_pubkey[0:2] == 'fd':
+ addrtype = ord(x_pubkey[2:4].decode('hex'))
+ addr = hash_160_to_bc_address(x_pubkey[4:].decode('hex'), addrtype)
+ if self.is_mine(addr):
+ return self.get_private_key(addr, password)[0]
+ else:
+ raise BaseException("z")
+
+
+ def can_sign_xpubkey(self, x_pubkey):
+ if x_pubkey[0:2] in ['02','03','04']:
+ addr = bitcoin.public_key_to_bc_address(x_pubkey.decode('hex'))
+ return self.is_mine(addr)
+ elif x_pubkey[0:2] == 'ff':
+ xpub, sequence = BIP32_Account.parse_xpubkey(x_pubkey)
+ return xpub in [ self.master_public_keys[k] for k in self.master_private_keys.keys() ]
+ elif x_pubkey[0:2] == 'fe':
+ xpub, sequence = OldAccount.parse_xpubkey(x_pubkey)
+ return xpub == self.get_master_public_key()
+ elif x_pubkey[0:2] == 'fd':
+ addrtype = ord(x_pubkey[2:4].decode('hex'))
+ addr = hash_160_to_bc_address(x_pubkey[4:].decode('hex'), addrtype)
+ return self.is_mine(addr)
+ else:
+ raise BaseException("z")
+
def is_watching_only(self):
False
@@ -1255,21 +1287,6 @@ class BIP32_Wallet(Deterministic_Wallet):
xprv, xpub = bip32_private_derivation(root_xprv, root, derivation)
return xpub, xprv
- def can_sign(self, tx):
- if self.is_watching_only():
- return False
- if tx.is_complete():
- return False
- addr_list, xpub_list = tx.inputs_to_sign()
- for addr in addr_list:
- if self.is_mine(addr):
- return True
- mpk = [ self.master_public_keys[k] for k in self.master_private_keys.keys() ]
- for xpub, sequence in xpub_list:
- if xpub in mpk:
- return True
- return False
-
def create_master_keys(self, password):
seed = self.get_seed(password)
self.add_cosigner_seed(seed, self.root_name, password)
@@ -1564,19 +1581,6 @@ class OldWallet(Deterministic_Wallet):
s = self.get_seed(password)
return ' '.join(old_mnemonic.mn_encode(s))
- def can_sign(self, tx):
- if self.is_watching_only():
- return False
- if tx.is_complete():
- return False
- addr_list, xpub_list = tx.inputs_to_sign()
- for addr in addr_list:
- if self.is_mine(addr):
- return True
- for xpub, sequence in xpub_list:
- if xpub == self.get_master_public_key():
- return True
- return False