electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

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:
Mlib/transaction.py | 77++++++++++++++++++++++++++++++++++-------------------------------------------
Mlib/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