Browse Source

signrawtransaction: parse redeemScript

283
thomasv 12 years ago
parent
commit
40b397dc0f
  1. 26
      electrum
  2. 129
      lib/bitcoin.py
  3. 49
      lib/deserialize.py
  4. 4
      lib/wallet.py

26
electrum

@ -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']

129
lib/bitcoin.py

@ -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')
@ -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 ):
@ -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
@ -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
@ -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 )
@ -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)

49
lib/deserialize.py

@ -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
@ -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
@ -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) ]

4
lib/wallet.py

@ -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)

Loading…
Cancel
Save