signrawtransaction: parse redeemScript
This commit is contained in:
parent
c400583443
commit
40b397dc0f
26
electrum
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']
|
||||
|
|
127
lib/bitcoin.py
127
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)
|
||||
|
|
|
@ -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) ]
|
||||
|
|
|
@ -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…
Reference in New Issue