commit 40b397dc0f0df7ee6f0dbaf7371ea743e0b85e85
parent c400583443aa49bc88cafec240dbe51a8b26a8a2
Author: thomasv <thomasv@gitorious>
Date: Fri, 22 Feb 2013 13:40:42 +0100
signrawtransaction: parse redeemScript
Diffstat:
4 files changed, 148 insertions(+), 60 deletions(-)
diff --git a/electrum b/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']
diff --git a/lib/bitcoin.py b/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)
diff --git a/lib/deserialize.py b/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) ]
diff --git a/lib/wallet.py b/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)