cleanup signrawtrasaction and input_info
authorThomasV <thomasv@gitorious>
Sun, 29 Sep 2013 13:16:22 +0000 (15:16 +0200)
committerThomasV <thomasv@gitorious>
Sun, 29 Sep 2013 13:16:22 +0000 (15:16 +0200)
gui/qt/main_window.py
lib/transaction.py
lib/wallet.py

index d1fe9ae..511c032 100644 (file)
@@ -1658,9 +1658,11 @@ class ElectrumWindow(QMainWindow):
             tx_dict = json.loads(str(txt))
             assert "hex" in tx_dict.keys()
             assert "complete" in tx_dict.keys()
+            tx = Transaction(tx_dict["hex"])
             if not tx_dict["complete"]:
                 assert "input_info" in tx_dict.keys()
-            tx = Transaction(tx_dict["hex"])
+                input_info = json.loads(tx_dict['input_info'])
+                tx.add_input_info(input_info)
             return tx
         except:
             pass
index 6f2460a..7f16ac1 100644 (file)
@@ -376,8 +376,7 @@ class Transaction:
         self.inputs = self.d['inputs']
         self.outputs = self.d['outputs']
         self.outputs = map(lambda x: (x['address'],x['value']), self.outputs)
-        self.input_info = None
-        self.is_complete = True
+        self.is_complete = False
         
     def __str__(self):
         return self.raw
@@ -389,22 +388,6 @@ class Transaction:
         self.is_complete = False
         self.inputs = inputs
         self.outputs = outputs
-        extras = []
-        for i in self.inputs:
-            
-            e = { 'txid':i['tx_hash'],
-                  'vout':i['index'],
-                  'scriptPubKey':i.get('raw_output_script'),
-                  'KeyID':i['KeyID'],
-                  'redeemScript':i.get('redeemScript'),
-                  'redeemPubkey':i.get('redeemPubkey')
-                  }
-            extras.append(e)
-            # fixme: simplify this
-            i['prevout_hash'] = i['tx_hash']
-            i['prevout_n'] = i['index']
-
-        self.input_info = extras
         return self
 
     @classmethod
@@ -441,8 +424,8 @@ class Transaction:
         s += var_int( len(inputs) )                                  # number of inputs
         for i in range(len(inputs)):
             txin = inputs[i]
-            s += txin['tx_hash'].decode('hex')[::-1].encode('hex')   # prev hash
-            s += int_to_hex(txin['index'],4)                         # prev index
+            s += txin['prevout_hash'].decode('hex')[::-1].encode('hex')   # prev hash
+            s += int_to_hex(txin['prevout_n'],4)                          # prev index
 
             if for_sig is None:
                 signatures = txin['signatures']
@@ -470,7 +453,7 @@ class Transaction:
                 if txin.get('redeemScript'):
                     script = txin['redeemScript']                    # p2sh uses the inner script
                 else:
-                    script = txin['raw_output_script']               # scriptsig
+                    script = txin['scriptPubKey']                    # scriptsig
             else:
                 script=''
             s += var_int( len(script)/2 )                            # script length
@@ -598,8 +581,8 @@ class Transaction:
         is_pubkey, address = get_address_from_output_script(scriptPubKey)
         d['is_pubkey'] = is_pubkey
         d['address'] = address
-        d['raw_output_script'] = scriptPubKey.encode('hex')
-        d['index'] = i
+        d['scriptPubKey'] = scriptPubKey.encode('hex')
+        d['prevout_n'] = i
         return d
 
 
@@ -679,27 +662,36 @@ class Transaction:
 
         return is_relevant, is_send, v, fee
 
+
+    def get_input_info(self):
+        info = []
+        for i in self.inputs:
+            print len(i)
+            item = { 
+                'prevout_hash':i['prevout_hash'], 
+                'prevout_n':i['prevout_n'],
+                'address':i['address'],
+                'KeyID':i.get('KeyID'),
+                'scriptPubKey':i.get('scriptPubKey'),
+                'redeemScript':i.get('redeemScript'),
+                'redeemPubkey':i.get('redeemPubkey'),
+                'pubkeys':i.get('pubkeys'),
+                'signatures':i.get('signatures'),
+                }
+            info.append(item)
+        return info
+
+
     def as_dict(self):
         import json
         out = {
             "hex":self.raw,
             "complete":self.is_complete
             }
+
         if not self.is_complete:
-            extras = []
-            for i in self.inputs:
-                e = { 'txid':i['tx_hash'], 'vout':i['index'],
-                      'scriptPubKey':i.get('raw_output_script'),
-                      'KeyID':i.get('KeyID'),
-                      'redeemScript':i.get('redeemScript'),
-                      'signatures':i.get('signatures'),
-                      'pubkeys':i.get('pubkeys'),
-                      }
-                extras.append(e)
-            self.input_info = extras
-
-            if self.input_info:
-                out['input_info'] = json.dumps(self.input_info).replace(' ','')
+            input_info = self.get_input_info()
+            out['input_info'] = json.dumps(input_info).replace(' ','')
 
         return out
 
@@ -724,3 +716,11 @@ class Transaction:
         return priority < threshold 
 
 
+
+    def add_input_info(self, input_info):
+        for i, txin in enumerate(self.inputs):
+            item = input_info[i]
+            txin['address'] = item['address']
+            txin['scriptPubKey'] = item['scriptPubKey']
+            txin['redeemScript'] = item.get('redeemScript')
+            txin['KeyID'] = item.get('KeyID')
index e7457a5..868d383 100644 (file)
@@ -585,44 +585,17 @@ class Wallet:
         return out
 
 
+    def add_keypairs_from_wallet(self, tx, keypairs, password):
+        for txin in tx.inputs:
+            address = txin['address']
+            private_keys = self.get_private_key(address, password)
+            for sec in private_keys:
+                pubkey = public_key_from_private_key(sec)
+                keypairs[ pubkey ] = sec
 
 
-    def signrawtransaction(self, tx, input_info, private_keys, password):
-
-        unspent_coins = self.get_unspent_coins()
-        seed = self.decode_seed(password)
-
-        # build a list of public/private keys
-        keypairs = {}
-        for sec in private_keys:
-            pubkey = public_key_from_private_key(sec)
-            keypairs[ pubkey ] = sec
-
-
+    def add_keypairs_from_KeyID(self, tx, keypairs, password):
         for txin in tx.inputs:
-            # convert to own format
-            txin['tx_hash'] = txin['prevout_hash']
-            txin['index'] = txin['prevout_n']
-
-            for item in input_info:
-                if item.get('txid') == txin['tx_hash'] and item.get('vout') == txin['index']:
-                    txin['raw_output_script'] = item['scriptPubKey']
-                    txin['redeemScript'] = item.get('redeemScript')
-                    txin['KeyID'] = item.get('KeyID')
-                    break
-            else:
-                for item in unspent_coins:
-                    if txin['tx_hash'] == item['tx_hash'] and txin['index'] == item['index']:
-                        print_error( "tx input is in unspent coins" )
-                        txin['raw_output_script'] = item['raw_output_script']
-                        account, sequence = self.get_address_index(item['address'])
-                        if account != -1:
-                            txin['redeemScript'] = self.accounts[account].redeem_script(sequence)
-                        break
-                else:
-                    raise BaseException("Unknown transaction input. Please provide the 'input_info' parameter, or synchronize this wallet")
-
-            # if available, derive private_keys from KeyID
             keyid = txin.get('KeyID')
             if keyid:
                 roots = []
@@ -643,32 +616,51 @@ class Wallet:
                 account = self.accounts.get(account_id)
                 if not account: continue
                 addr = account.get_address(*sequence)
-                txin['address'] = addr
+                txin['address'] = addr # fixme: side effect
                 pk = self.get_private_key(addr, password)
                 for sec in pk:
                     pubkey = public_key_from_private_key(sec)
                     keypairs[pubkey] = sec
 
-            redeem_script = txin.get("redeemScript")
-            print_error( "p2sh:", "yes" if redeem_script else "no")
-            if redeem_script:
-                addr = hash_160_to_bc_address(hash_160(redeem_script.decode('hex')), 5)
-            else:
-                import transaction
-                _, addr = transaction.get_address_from_output_script(txin["raw_output_script"].decode('hex'))
-            txin['address'] = addr
 
-            # add private keys that are in the wallet
-            pk = self.get_private_key(addr, password)
-            for sec in pk:
-                pubkey = public_key_from_private_key(sec)
-                keypairs[pubkey] = sec
-                if not redeem_script:
-                    txin['redeemPubkey'] = pubkey
 
-            print txin
+    def signrawtransaction(self, tx, input_info, private_keys, password):
+
+        # check that the password is correct
+        seed = self.decode_seed(password)
+
+        # add input info
+        tx.add_input_info(input_info)
+
+        # add redeem script for coins that are in the wallet
+        # FIXME: add redeemPubkey too!
+        unspent_coins = self.get_unspent_coins()
+        for txin in tx.inputs:
+            for item in unspent_coins:
+                if txin['prevout_hash'] == item['prevout_hash'] and txin['prevout_n'] == item['prevout_n']:
+                    print_error( "tx input is in unspent coins" )
+                    txin['scriptPubKey'] = item['scriptPubKey']
+                    account, sequence = self.get_address_index(item['address'])
+                    if account != -1:
+                        txin['redeemScript'] = self.accounts[account].redeem_script(sequence)
+                        print_error("added redeemScript", txin['redeemScript'])
+                    break
+
+
+        # build a list of public/private keys
+        keypairs = {}
+
+        # add private keys from parameter
+        for sec in private_keys:
+            pubkey = public_key_from_private_key(sec)
+            keypairs[ pubkey ] = sec
+
+        # add private_keys from KeyID
+        self.add_keypairs_from_KeyID(tx, keypairs, password)
 
-        self.sign_tx(tx, keypairs)
+        # add private keys from wallet
+        self.add_keypairs_from_wallet(tx, keypairs, password)
+        self.sign_transaction(tx, keypairs)
 
 
     def sign_message(self, address, message, password):
@@ -979,9 +971,9 @@ class Wallet:
                 if tx is None: raise BaseException("Wallet not synchronized")
                 for output in tx.d.get('outputs'):
                     if output.get('address') != addr: continue
-                    key = tx_hash + ":%d" % output.get('index')
+                    key = tx_hash + ":%d" % output.get('prevout_n')
                     if key in self.spent_outputs: continue
-                    output['tx_hash'] = tx_hash
+                    output['prevout_hash'] = tx_hash
                     output['height'] = tx_height
                     coins.append((tx_height, output))
 
@@ -1021,7 +1013,7 @@ class Wallet:
             addr = item.get('address')
             v = item.get('value')
             total += v
-            inputs.append( item )
+            inputs.append(item)
             fee = self.estimated_fee(inputs) if fixed_fee is None else fixed_fee
             if total >= amount + fee: break
         else:
@@ -1210,7 +1202,9 @@ class Wallet:
 
     def mktx(self, outputs, password, fee=None, change_addr=None, domain= None ):
         tx = self.make_unsigned_transaction(outputs, fee, change_addr, domain)
-        self.sign_transaction(tx, password)
+        keypairs = {}
+        self.add_keypairs_from_wallet(tx, keypairs, password)
+        self.sign_transaction(tx, keypairs)
         return tx
 
 
@@ -1226,21 +1220,9 @@ class Wallet:
                 txin['redeemPubkey'] = self.accounts[account].get_pubkey(*sequence)
 
 
-    def sign_transaction(self, tx, password):
-        keypairs = {}
-        for i, txin in enumerate(tx.inputs):
-            address = txin['address']
-            private_keys = self.get_private_key(address, password)
-            for sec in private_keys:
-                pubkey = public_key_from_private_key(sec)
-                keypairs[ pubkey ] = sec
-
-        self.sign_tx(tx, keypairs)
-
-
-    def sign_tx(self, tx, keypairs):
+    def sign_transaction(self, tx, keypairs):
         tx.sign(keypairs)
-        run_hook('sign_tx', tx)
+        run_hook('sign_transaction', tx)
 
 
     def sendtx(self, tx):