URI: 
       tcreate Transaction class - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit ea7718fc59fa7111d235ede8a48543d2a7dbfb10
   DIR parent b4bb3c74499520d66a98271e9e011558abf5be83
  HTML Author: thomasv <thomasv@gitorious>
       Date:   Thu, 21 Feb 2013 14:18:12 +0100
       
       create Transaction class
       
       Diffstat:
         M electrum                            |      50 +++++++++++++++++--------------
         M lib/__init__.py                     |       1 +
         M lib/bitcoin.py                      |      65 ++++++++++++++++++++++++++-----
         M lib/wallet.py                       |      51 +++++++++++---------------------
       
       4 files changed, 102 insertions(+), 65 deletions(-)
       ---
   DIR diff --git a/electrum b/electrum
       t@@ -711,38 +711,44 @@ if __name__ == '__main__':
                import ast
                inputs = ast.literal_eval(args[1])
                outputs = ast.literal_eval(args[2])
       -        inputs = map(lambda x: (None, None, x["txid"], x["vout"], None, None), inputs)
       -        outputs = map(lambda x: (x[0],int(x[1]*1e8)), outputs.items())
       -        tx = raw_tx(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
       +        # convert to own format
       +        for i in inputs:
       +            i['tx_hash'] = i['txid']
       +            i['index'] = i['vout']
       +        outputs = map(lambda x: (x[0],int(1e8*x[1])), outputs.items())
       +        tx = Transaction.from_io(inputs, outputs)
                print_msg( tx )
        
        
            elif cmd == 'decoderawtransaction':
       -        print_json( bitcoin.deserialize(args[1]) )
       +        tx = Transaction(args[1])
       +        print_json( tx.deserialize() )
        
        
            elif cmd == 'signrawtransaction':
       -        d = bitcoin.deserialize(args[1])
       +        tx = Transaction(args[1])
                txouts = args[2] if len(args)>2 else []
       -        private_keys = args[3] if len(args)>3 else []
       -        
       -        inputs = []
       -        for x in d['inputs']:
       -            txid = x["prevout_hash"]
       -            nout = x["prevout_n"]
       -            tx = wallet.transactions.get(txid)
       -            txout = tx['outputs'][nout]
       -            addr = txout['address']
       -            v = txout['value']
       -            inputs.append( (addr, v, txid, nout, txout['raw_output_script'], [(None,None)] ) )
       -
       -        outputs = map(lambda x: (x['address'],x['value']), d['outputs'])
       -        print_error("inputs", inputs)
       -        print_error("outputs", outputs)
       -
       -        tx = wallet.signed_tx( inputs, outputs, password )
       +        private_keys = args[3] if len(args)>3 else {}
       +
       +        if not private_keys:
       +            for txin in tx.inputs:
       +                txid = txin["prevout_hash"]
       +                index = txin["prevout_n"]
       +                utx = wallet.transactions.get(txid)
       +                txout = utx['outputs'][index]
       +                txin['address'] = txout['address']
       +                txin['raw_output_script'] = txout['raw_output_script']
       +                # convert to own format
       +                txin['tx_hash'] = txin['prevout_hash']
       +                txin['index'] = txin['prevout_n']
       +                secexp, compressed = wallet.get_private_key(txin['address'], password)
       +                private_keys[addr] = (secexp,compressed)
       +            
       +        tx.sign( private_keys )
                print_msg(tx)
                
        
            if cmd not in offline_commands and not options.offline:
                synchronizer.stop()
       +
       +
   DIR diff --git a/lib/__init__.py b/lib/__init__.py
       t@@ -7,5 +7,6 @@ from verifier import WalletVerifier
        from interface import Interface, pick_random_server, DEFAULT_SERVERS
        from simple_config import SimpleConfig
        import bitcoin
       +from bitcoin import Transaction
        from mnemonic import mn_encode as mnemonic_encode
        from mnemonic import mn_decode as mnemonic_decode
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -317,12 +317,14 @@ def tx_filter(s):
            return out
        
        def raw_tx( inputs, outputs, for_sig = None ):
       +
            s  = int_to_hex(1,4)                                         # version
            s += var_int( len(inputs) )                                  # number of inputs
            for i in range(len(inputs)):
       -        _, _, p_hash, p_index, p_script, pubkeysig = inputs[i]
       -        s += p_hash.decode('hex')[::-1].encode('hex')            # prev hash
       -        s += int_to_hex(p_index,4)                               # prev index
       +        txin = inputs[i]
       +        pubkeysig = txin.get('pubkeysig',[])
       +        s += txin['tx_hash'].decode('hex')[::-1].encode('hex')            # prev hash
       +        s += int_to_hex(txin['index'],4)                               # prev index
        
                if for_sig is None:
                    if len(pubkeysig) == 1:
       t@@ -350,7 +352,7 @@ def raw_tx( inputs, outputs, for_sig = None ):
                    if len(pubkeysig) > 1:
                        script = multisig_script(pubkeysig)              # p2sh uses the inner script
                    else:
       -                script = p_script                                # scriptsig
       +                script = txin['raw_output_script']               # scriptsig
                else:
                    script=''
                s += var_int( len(tx_filter(script))/2 )                 # script length
       t@@ -382,11 +384,6 @@ def raw_tx( inputs, outputs, for_sig = None ):
            return tx_filter(s)
        
        
       -def deserialize(raw_tx):
       -    import deserialize
       -    vds = deserialize.BCDataStream()
       -    vds.write(raw_tx.decode('hex'))
       -    return deserialize.parse_Transaction(vds)
        
        
        def multisig_script(public_keys, num=None):
       t@@ -419,6 +416,56 @@ def multisig_script(public_keys, num=None):
        
        
        
       +class Transaction:
       +    
       +    def __init__(self, raw):
       +        self.raw = raw
       +        self.deserialize()
       +        self.inputs = self.d['inputs']
       +        self.outputs = self.d['outputs']
       +        self.outputs = map(lambda x: (x['address'],x['value']), self.outputs)
       +        
       +    @classmethod
       +    def from_io(klass, inputs, outputs):
       +        raw = raw_tx(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
       +        self = klass(raw)
       +        self.inputs = inputs
       +        self.outputs = outputs
       +        return self
       +
       +    def __str__(self):
       +        return self.raw
       +
       +    def hash(self):
       +        return Hash(self.raw.decode('hex') )[::-1].encode('hex')
       +
       +    def sign(self, private_keys):
       +
       +        for i in range(len(self.inputs)):
       +            txin = self.inputs[i]
       +            secexp, compressed = private_keys[txin['address']]
       +            private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
       +            public_key = private_key.get_verifying_key()
       +            pkey = EC_KEY(secexp)
       +            pubkey = GetPubKey(pkey.pubkey, compressed)
       +            tx = raw_tx( self.inputs, self.outputs, for_sig = i )
       +            sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
       +            assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
       +            self.inputs[i]["pubkeysig"] = [(pubkey, sig)]
       +
       +        self.raw = raw_tx( self.inputs, self.outputs )
       +
       +
       +    def deserialize(self):
       +        import deserialize
       +        vds = deserialize.BCDataStream()
       +        vds.write(self.raw.decode('hex'))
       +        self.d = deserialize.parse_Transaction(vds)
       +        return self.d
       +    
       +
       +
       +
        def test_bip32():
            seed = "ff000000000000000000000000000000".decode('hex')
            master_secret, master_chain, master_public_key, master_public_key_compressed = bip32_init(seed)
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -210,7 +210,7 @@ class Wallet:
                        n = self.change_addresses.index(address)
                        for_change = True
                    else:
       -                raise BaseException("unknown address")
       +                raise BaseException("unknown address", address)
        
                    seed = self.pw_decode( self.seed, password)
                    if not seed: return None
       t@@ -609,7 +609,8 @@ class Wallet:
                    addr = item.get('address')
                    v = item.get('value')
                    total += v
       -            inputs.append((addr, v, item['tx_hash'], item['index'], item['raw_output_script'], [(None,None)] ))
       +            item['pubkeysig'] = [(None, None)]
       +            inputs.append( item )
                    fee = self.fee*len(inputs) if fixed_fee is None else fixed_fee
                    if total >= amount + fee: break
                else:
       t@@ -628,22 +629,6 @@ class Wallet:
                    outputs[posn:posn] = [( change_addr,  change_amount)]
                return outputs
        
       -    def sign_inputs( self, inputs, outputs, password ):
       -        s_inputs = []
       -        for i in range(len(inputs)):
       -            addr, v, p_hash, p_pos, p_scriptPubKey, _ = inputs[i]
       -            secexp, compressed = self.get_private_key(addr, password)
       -            private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
       -            public_key = private_key.get_verifying_key()
       -
       -            pkey = EC_KEY(secexp)
       -            pubkey = GetPubKey(pkey.pubkey, compressed)
       -
       -            tx = raw_tx( inputs, outputs, for_sig = i )
       -            sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
       -            assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
       -            s_inputs.append( (addr, v, p_hash, p_pos, p_scriptPubKey, [(pubkey, sig)] ) )
       -        return s_inputs
        
            def pw_encode(self, s, password):
                if password:
       t@@ -823,7 +808,7 @@ class Wallet:
                    raise ValueError("Not enough funds")
        
                if not self.use_change and not change_addr:
       -            change_addr = inputs[-1][0]
       +            change_addr = inputs[-1]['address']
                    print_error( "Sending change to", change_addr )
                outputs = self.add_tx_change(outputs, amount, fee, total, change_addr)
        
       t@@ -843,9 +828,14 @@ class Wallet:
                return tx
        
            def signed_tx(self, inputs, outputs, password):
       -        s_inputs = self.sign_inputs( inputs, outputs, password )
       -        tx = raw_tx( s_inputs, outputs )
       -        return tx
       +        tx = Transaction.from_io(inputs, outputs)
       +        private_keys = {}
       +        for txin in tx.inputs:
       +            addr = txin['address']
       +            secexp, compressed = self.get_private_key(addr, password)
       +            private_keys[addr] = (secexp,compressed)
       +        tx.sign(private_keys)
       +        return str(tx)
        
            def sendtx(self, tx):
                # synchronous
       t@@ -1344,7 +1334,11 @@ class WalletSynchronizer(threading.Thread):
                    elif method == 'blockchain.transaction.get':
                        tx_hash = params[0]
                        tx_height = params[1]
       -                d = self.deserialize_tx(tx_hash, tx_height, result)
       +                assert tx_hash == hash_encode(Hash(result.decode('hex')))
       +                tx = Transaction(result)
       +                d = tx.deserialize()
       +                d['height'] = tx_height
       +                d['tx_hash'] = tx_hash
                        self.wallet.receive_tx_callback(tx_hash, d)
                        self.was_updated = True
                        requested_tx.remove( (tx_hash, tx_height) )
       t@@ -1365,14 +1359,3 @@ class WalletSynchronizer(threading.Thread):
                        self.was_updated = False
        
        
       -    def deserialize_tx(self, tx_hash, tx_height, raw_tx):
       -
       -        assert tx_hash == hash_encode(Hash(raw_tx.decode('hex')))
       -        import deserialize
       -        vds = deserialize.BCDataStream()
       -        vds.write(raw_tx.decode('hex'))
       -        d = deserialize.parse_Transaction(vds)
       -        d['height'] = tx_height
       -        d['tx_hash'] = tx_hash
       -        return d
       -