tsignrawtransaction: parse redeemScript - electrum - Electrum Bitcoin wallet
 (HTM) git clone https://git.parazyd.org/electrum
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
       ---
 (DIR) commit 40b397dc0f0df7ee6f0dbaf7371ea743e0b85e85
 (DIR) parent c400583443aa49bc88cafec240dbe51a8b26a8a2
 (HTM) Author: thomasv <thomasv@gitorious>
       Date:   Fri, 22 Feb 2013 13:40:42 +0100
       
       signrawtransaction: parse redeemScript
       
       Diffstat:
         M electrum                            |      26 +++++++++++++++++++++-----
         M lib/bitcoin.py                      |     129 +++++++++++++++++++++-----------
         M lib/deserialize.py                  |      49 ++++++++++++++++++++++++-------
         M lib/wallet.py                       |       4 ++--
       
       4 files changed, 148 insertions(+), 60 deletions(-)
       ---
 (DIR) diff --git a/electrum b/electrum
       t@@ -729,22 +729,38 @@ if __name__ == '__main__':
        
        
            elif cmd == 'signrawtransaction':
       +        from lib.bitcoin import *
                tx = Transaction(args[1])
                txouts = ast.literal_eval(args[2]) if len(args)>2 else []
                private_keys = ast.literal_eval(args[3]) if len(args)>3 else {}
        
       -        # lookup addresses
                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']
        
       +            for txout in txouts:
       +                if txout.get('txid') == txid and txout.get('vout') == index:
       +                    # compute addr from redeemScript
       +                    addr = hash_160_to_bc_address(hash_160(txout['redeemScript'].decode('hex')),5)
       +                    txin['address'] = addr
       +                    txin['raw_output_script'] = txout['scriptPubKey']
       +                    txin['redeemScript'] = txout['redeemScript']
       +                    break
       +                
       +            else:
       +                if wallet.transactions.get(txid):
       +                    # lookup in my own list of transactions
       +                    txout = wallet.transactions[txid]['outputs'][index]
       +                    txin['address'] = txout['address']
       +                    txin['raw_output_script'] = txout['raw_output_script']
       +
       +                else:
       +                    # if neither, we might want to get it from the server..
       +                    raise
       +
                if not private_keys:
                    for txin in tx.inputs:
                        addr = txin['address']
 (DIR) diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -39,6 +39,17 @@ def var_int(i):
            else:
                return "ff"+int_to_hex(i,8)
        
       +def op_push(i):
       +    if i<0x4c:
       +        return int_to_hex(i)
       +    elif i<0xff:
       +        return '4c' + int_to_hex(i)
       +    elif i<0xffff:
       +        return '4d' + int_to_hex(i,2)
       +    else:
       +        return '4e' + int_to_hex(i,4)
       +    
       +
        
        Hash = lambda x: hashlib.sha256(hashlib.sha256(x).digest()).digest()
        hash_encode = lambda x: x[::-1].encode('hex')
       t@@ -326,11 +337,6 @@ def CKD_prime(K, c, n):
        ################################## transactions
        
        
       -def tx_filter(s): 
       -    out = re.sub('( [^\n]*|)\n','',s)
       -    out = out.replace(' ','')
       -    out = out.replace('\n','')
       -    return out
        
        def raw_tx( inputs, outputs, for_sig = None ):
        
       t@@ -338,41 +344,39 @@ def raw_tx( inputs, outputs, for_sig = None ):
            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['tx_hash'].decode('hex')[::-1].encode('hex')   # prev hash
       +        s += int_to_hex(txin['index'],4)                         # prev index
        
                if for_sig is None:
       -            pubkeysig = txin['pubkeysig']
       -            if len(pubkeysig) == 1:
       +            pubkeysig = txin.get('pubkeysig')
       +            if pubkeysig:
                        pubkey, sig = pubkeysig[0]
                        sig = sig + chr(1)                               # hashtype
       -                script  = int_to_hex( len(sig))
       +                script  = op_push( len(sig))
                        script += sig.encode('hex')
       -                script += int_to_hex( len(pubkey))
       +                script += op_push( len(pubkey))
                        script += pubkey.encode('hex')
                    else:
       -                n = txin['multisig_num']
       -                pubkeys = map(lambda x:x[0], pubkeysig)
       +                signatures = txin['signatures']
       +                pubkeys = txin['pubkeys']
                        script = '00'                                    # op_0
       -                for item in pubkeysig:
       -                    pubkey, sig = item
       -                    sig = sig + chr(1)
       -                    script += int_to_hex(len(sig))
       -                    script += sig.encode('hex')
       -                inner_script = multisig_script(pubkeys)
       -                script += var_int(len(inner_script)/2)
       -                script += inner_script
       +                for sig in signatures:
       +                    sig = sig + '01'
       +                    script += op_push(len(sig)/2)
       +                    script += sig
       +
       +                redeem_script = multisig_script(pubkeys,2)
       +                script += op_push(len(redeem_script)/2)
       +                script += redeem_script
        
                elif for_sig==i:
       -            pubkeys = txin.get('pubkeys')
       -            if pubkeys:
       -                num = txin['p2sh_num']
       -                script = multisig_script(pubkeys, num)           # p2sh uses the inner script
       +            if txin.get('redeemScript'):
       +                script = txin['redeemScript']                    # p2sh uses the inner script
                    else:
                        script = txin['raw_output_script']               # scriptsig
                else:
                    script=''
       -        s += var_int( len(tx_filter(script))/2 )                 # script length
       +        s += var_int( len(script)/2 )                            # script length
                s += script
                s += "ffffffff"                                          # sequence
        
       t@@ -394,11 +398,12 @@ def raw_tx( inputs, outputs, for_sig = None ):
                else:
                    raise
                    
       -        s += var_int( len(tx_filter(script))/2 )                #  script length
       +        s += var_int( len(script)/2 )                           #  script length
                s += script                                             #  script
            s += int_to_hex(0,4)                                        #  lock time
       -    if for_sig is not None and for_sig != -1: s += int_to_hex(1, 4)               #  hash type
       -    return tx_filter(s)
       +    if for_sig is not None and for_sig != -1:
       +        s += int_to_hex(1, 4)                                   #  hash type
       +    return s
        
        
        
       t@@ -460,22 +465,60 @@ class Transaction:
                return Hash(self.raw.decode('hex') )[::-1].encode('hex')
        
            def sign(self, private_keys):
       +        import deserialize
        
                for i in range(len(self.inputs)):
                    txin = self.inputs[i]
       -            sec = private_keys[txin['address']]
       -            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()
       -            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)]
       +
       +            if txin.get('redeemScript'):
       +                # 1 parse the redeem script
       +                num, redeem_pubkeys = deserialize.parse_redeemScript(txin.get('redeemScript'))
       +                self.inputs[i]["pubkeys"] = redeem_pubkeys
       +
       +                # build list of public/private keys
       +                keypairs = {}
       +                for sec in private_keys.values():
       +                    compressed = is_compressed(sec)
       +                    pkey = regenerate_key(sec)
       +                    pubkey = GetPubKey(pkey.pubkey, compressed)
       +                    keypairs[ pubkey.encode('hex') ] = sec
       +
       +                # list of signatures
       +                signatures = txin.get("signatures",[])
       +                
       +                # check if we have a key corresponding to the redeem script
       +                for pubkey, privkey in keypairs.items():
       +                    if pubkey in redeem_pubkeys:
       +                        # add signature
       +                        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()
       +
       +                        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)
       +                        signatures.append( sig.encode('hex') )
       +
       +                # for p2sh, pubkeysig is a tuple (may be incomplete)
       +                self.inputs[i]["signatures"] = signatures
       +
       +            else:
       +                sec = private_keys[txin['address']]
       +                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()
       +                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 )
        
       t@@ -542,7 +585,7 @@ def test_p2sh():
        
            tx = Transaction.from_io(
                [{'tx_hash':'3c9018e8d5615c306d72397f8f5eef44308c98fb576a88e030c25456b4f3a7ac', 'index':0,
       -          'raw_output_script':'a914f815b036d9bbbce5e9f2a00abd1bf3dc91e9551087', 'pubkeys':pubkeys, 'p2sh_num':2}],
       +          'raw_output_script':'a914f815b036d9bbbce5e9f2a00abd1bf3dc91e9551087', 'redeemScript':multisig_script(pubkeys, 2)}],
                [('1GtpSrGhRGY5kkrNz4RykoqRQoJuG2L6DS',1000000)])
        
            tx_for_sig = tx.for_sig(0)
 (DIR) diff --git a/lib/deserialize.py b/lib/deserialize.py
       t@@ -186,7 +186,17 @@ def parse_TxIn(vds):
          d['prevout_n'] = vds.read_uint32()
          scriptSig = vds.read_bytes(vds.read_compact_size())
          d['sequence'] = vds.read_uint32()
       -  d['address'] = get_address_from_input_script(scriptSig)
       +
       +  if scriptSig:
       +    pubkeys, signatures, address = get_address_from_input_script(scriptSig)
       +  else:
       +    pubkeys = []
       +    signatures = []
       +    address = None
       +    
       +  d['address'] = address
       +  d['signatures'] = signatures
       +
          return d
        
        
       t@@ -215,6 +225,20 @@ def parse_Transaction(vds):
          d['lockTime'] = vds.read_uint32()
          return d
        
       +def parse_redeemScript(bytes):
       +  dec = [ x for x in script_GetOp(bytes.decode('hex')) ]
       +
       +  # 2 of 2
       +  match = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_2, opcodes.OP_CHECKMULTISIG ]
       +  if match_decoded(dec, match):
       +    pubkeys = [ dec[1][1].encode('hex'), dec[2][1].encode('hex') ]
       +    return 2, pubkeys
       +
       +  # 2 of 3
       +  match = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_3, opcodes.OP_CHECKMULTISIG ]
       +  if match_decoded(dec, match):
       +    pubkeys = [ dec[1][1].encode('hex'), dec[2][1].encode('hex'), dec[3][1].encode('hex') ]
       +    return 3, pubkeys
        
        
        
       t@@ -299,33 +323,38 @@ def get_address_from_input_script(bytes):
          # (65 bytes) onto the stack:
          match = [ opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4 ]
          if match_decoded(decoded, match):
       -    return public_key_to_bc_address(decoded[1][1])
       +    return None, None, public_key_to_bc_address(decoded[1][1])
        
          # p2sh transaction, 2 of n
       -  match = [ opcodes.OP_0, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4 ] 
       +  match = [ opcodes.OP_0 ]
       +  while len(match) < len(decoded):
       +    match.append(opcodes.OP_PUSHDATA4)
       +
          if match_decoded(decoded, match):
       -    bytes = decoded[3][1]
       -    dec2 = [ x for x in script_GetOp(bytes) ]
       +
       +    redeemScript = decoded[-1][1]
       +    num = len(match) - 2
       +    signatures = map(lambda x:x[1].encode('hex'), decoded[1:-1])
       +  
       +    dec2 = [ x for x in script_GetOp(redeemScript) ]
        
            # 2 of 2
            match2 = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_2, opcodes.OP_CHECKMULTISIG ]
            if match_decoded(dec2, match2):
              pubkeys = [ dec2[1][1].encode('hex'), dec2[2][1].encode('hex') ]
              s = multisig_script(pubkeys)
       -      return hash_160_to_bc_address(hash_160(s.decode('hex')), 5)
       +      return pubkeys, signatures, hash_160_to_bc_address(hash_160(s.decode('hex')), 5)
         
            # 2 of 3
            match2 = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_3, opcodes.OP_CHECKMULTISIG ]
            if match_decoded(dec2, match2):
              pubkeys = [ dec2[1][1].encode('hex'), dec2[2][1].encode('hex'), dec2[3][1].encode('hex') ]
              s = multisig_script(pubkeys)
       -      return hash_160_to_bc_address(hash_160(s.decode('hex')), 5)
       +      return pubkeys, signatures, hash_160_to_bc_address(hash_160(s.decode('hex')), 5)
        
       -    return "p2sh, unknown"
       +  raise BaseException("no match for scriptsig")
        
        
       -  return "(None)"
       -
        
        def get_address_from_output_script(bytes):
          decoded = [ x for x in script_GetOp(bytes) ]
 (DIR) diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -821,8 +821,8 @@ class Wallet:
                private_keys = {}
                for txin in tx.inputs:
                    addr = txin['address']
       -            secexp, compressed = self.get_private_key(addr, password)
       -            private_keys[addr] = (secexp,compressed)
       +            sec = self.get_private_key_base58(addr, password)
       +            private_keys[addr] = sec
                tx.sign(private_keys)
                return str(tx)