From 419c778fa3325b24966eae41e55fa22003efd7b3 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 16 Aug 2013 12:17:29 +0200 Subject: [PATCH] fix tx signing --- lib/account.py | 6 ----- lib/bitcoin.py | 62 +++++++++++++++++++++++++++---------------------------- lib/wallet.py | 53 ++++++++++++++++++++++++----------------------- 3 files changed, 57 insertions(+), 64 deletions(-) diff --git a/lib/account.py b/lib/account.py index cee9c00..e31a706 100644 --- a/lib/account.py +++ b/lib/account.py @@ -171,12 +171,6 @@ class BIP32_Account(Account): K, K_compressed, chain = CKD_prime(K, chain, i) return K_compressed.encode('hex') - def get_private_key(self, sequence, master_k): - chain = self.c - k = master_k - for i in sequence: - k, chain = CKD(k, chain, i) - return SecretToASecret(k, True) def get_private_keys(self, sequence_list, seed): return [ self.get_private_key( sequence, seed) for sequence in sequence_list] diff --git a/lib/bitcoin.py b/lib/bitcoin.py index d0c34d8..ae3a3ad 100644 --- a/lib/bitcoin.py +++ b/lib/bitcoin.py @@ -448,6 +448,11 @@ def bip32_public_derivation(c, K, branch, sequence): return c.encode('hex'), K.encode('hex'), cK.encode('hex') +def bip32_private_key(sequence, k, chain): + for i in sequence: + k, chain = CKD(k, chain, i) + return SecretToASecret(k, True) + @@ -588,61 +593,53 @@ class Transaction: def sign(self, private_keys): import deserialize + is_complete = True for i in range(len(self.inputs)): txin = self.inputs[i] tx_for_sig = self.serialize( self.inputs, self.outputs, for_sig = i ) + txin_pk = private_keys.get( txin.get('address') ) + if not txin_pk: + continue + redeem_script = txin.get('redeemScript') if redeem_script: # 1 parse the redeem script num, redeem_pubkeys = deserialize.parse_redeemScript(redeem_script) - self.inputs[i]["pubkeys"] = redeem_pubkeys + txin["pubkeys"] = redeem_pubkeys # build list of public/private keys keypairs = {} - for sec in private_keys.values(): + for sec in txin_pk: compressed = is_compressed(sec) pkey = regenerate_key(sec) pubkey = GetPubKey(pkey.pubkey, compressed) keypairs[ pubkey.encode('hex') ] = sec - print "keypairs", keypairs - print redeem_script, redeem_pubkeys - # list of already existing signatures signatures = txin.get("signatures",[]) print_error("signatures",signatures) for pubkey in redeem_pubkeys: - # here we have compressed key.. it won't work - #public_key = ecdsa.VerifyingKey.from_string(pubkey[2:].decode('hex'), curve = SECP256k1) - #for s in signatures: - # try: - # public_key.verify_digest( s.decode('hex')[:-1], Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der) - # break - # except ecdsa.keys.BadSignatureError: - # continue - #else: - if 1: - # check if we have a key corresponding to the redeem script - if pubkey in keypairs.keys(): - # add signature - sec = keypairs[pubkey] - compressed = is_compressed(sec) - 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( Hash( tx_for_sig.decode('hex') ), sigencode = ecdsa.util.sigencode_der ) - assert public_key.verify_digest( sig, Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der) - signatures.append( sig.encode('hex') ) + # check if we have a key corresponding to the redeem script + if pubkey in keypairs.keys(): + # add signature + sec = keypairs[pubkey] + compressed = is_compressed(sec) + 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( Hash( tx_for_sig.decode('hex') ), sigencode = ecdsa.util.sigencode_der ) + assert public_key.verify_digest( sig, Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der) + signatures.append( sig.encode('hex') ) # for p2sh, pubkeysig is a tuple (may be incomplete) - self.inputs[i]["signatures"] = signatures - print_error("signatures",signatures) - self.is_complete = len(signatures) == num + txin["signatures"] = signatures + print_error("signatures", signatures) + is_complete = is_complete and (len(signatures) == num) else: sec = private_keys[txin['address']] @@ -656,9 +653,10 @@ class Transaction: sig = private_key.sign_digest( Hash( tx_for_sig.decode('hex') ), sigencode = ecdsa.util.sigencode_der ) assert public_key.verify_digest( sig, Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der) - self.inputs[i]["pubkeysig"] = [(pubkey, sig)] - self.is_complete = True + txin["pubkeysig"] = [(pubkey, sig)] + is_complete = is_complete = True + self.is_complete = is_complete self.raw = self.serialize( self.inputs, self.outputs ) diff --git a/lib/wallet.py b/lib/wallet.py index ca0490b..b180aec 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -325,26 +325,26 @@ class Wallet: def get_private_key(self, address, password): + out = [] if address in self.imported_keys.keys(): - return pw_decode( self.imported_keys[address], password ) + out.append( pw_decode( self.imported_keys[address], password ) ) else: account, sequence = self.get_address_index(address) - m = re.match("m/0'/(\d+)'", account) - if m: - num = int(m.group(1)) - master_k = self.get_master_private_key("m/0'/", password) - master_c, _, _ = self.master_public_keys["m/0'/"] - master_k, master_c = CKD(master_k, master_c, num + BIP32_PRIME) - return self.accounts[account].get_private_key(sequence, master_k) - - m2 = re.match("m/1'/(\d+) & m/2'/(\d+)", account) - if m2: - num = int(m2.group(1)) - master_k = self.get_master_private_key("m/1'/", password) - master_c, master_K, _ = self.master_public_keys["m/1'/"] - master_k, master_c = CKD(master_k.decode('hex'), master_c.decode('hex'), num) - return self.accounts[account].get_private_key(sequence, master_k) - return + # assert address == self.accounts[account].get_address(*sequence) + l = account.split("&") + for s in l: + s = s.strip() + m = re.match("(m/\d+'/)(\d+)", s) + if m: + root = m.group(1) + if root not in self.master_private_keys.keys(): continue + num = int(m.group(2)) + master_k = self.get_master_private_key(root, password) + master_c, _, _ = self.master_public_keys[root] + pk = bip32_private_key( (num,) + sequence, master_k.decode('hex'), master_c.decode('hex')) + out.append(pk) + + return out def get_private_keys(self, addresses, password): @@ -915,7 +915,7 @@ class Wallet: tx = Transaction.from_io(inputs, outputs) - pk_addresses = [] + private_keys = {} for i in range(len(tx.inputs)): txin = tx.inputs[i] address = txin['address'] @@ -924,15 +924,16 @@ class Wallet: continue account, sequence = self.get_address_index(address) txin['KeyID'] = (account, 'BIP32', sequence) # used by the server to find the key + redeemScript = self.accounts[account].redeem_script(sequence) - if redeemScript: txin['redeemScript'] = redeemScript - pk_addresses.append(address) - - # get all private keys at once. - if self.seed: - private_keys = self.get_private_keys(pk_addresses, password) - print "private keys", private_keys - tx.sign(private_keys) + if redeemScript: + txin['redeemScript'] = redeemScript + assert address == self.accounts[account].get_address(*sequence) + + private_keys[address] = self.get_private_key(address, password) + + print_error( "private keys", private_keys ) + tx.sign(private_keys) for address, x in outputs: if address not in self.addressbook and not self.is_mine(address): -- 1.7.1