fix tx signing
authorThomasV <thomasv@gitorious>
Fri, 16 Aug 2013 10:17:29 +0000 (12:17 +0200)
committerThomasV <thomasv@gitorious>
Fri, 16 Aug 2013 10:17:29 +0000 (12:17 +0200)
lib/account.py
lib/bitcoin.py
lib/wallet.py

index cee9c00..e31a706 100644 (file)
@@ -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]
index d0c34d8..ae3a3ad 100644 (file)
@@ -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 )
 
 
index ca0490b..b180aec 100644 (file)
@@ -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):