1#!/usr/bin/python
2import binascii, re, json, copy, sys
3from .main import *
4from _functools import reduce
5
6### Hex to bin converter and vice versa for objects
7
8
9def json_is_base(obj, base):
10    if not is_python2 and isinstance(obj, bytes):
11        return False
12
13    alpha = get_code_string(base)
14    if isinstance(obj, string_types):
15        for i in range(len(obj)):
16            if alpha.find(obj[i]) == -1:
17                return False
18        return True
19    elif isinstance(obj, int_types) or obj is None:
20        return True
21    elif isinstance(obj, list):
22        for i in range(len(obj)):
23            if not json_is_base(obj[i], base):
24                return False
25        return True
26    else:
27        for x in obj:
28            if not json_is_base(obj[x], base):
29                return False
30        return True
31
32
33def json_changebase(obj, changer):
34    if isinstance(obj, string_or_bytes_types):
35        return changer(obj)
36    elif isinstance(obj, int_types) or obj is None:
37        return obj
38    elif isinstance(obj, list):
39        return [json_changebase(x, changer) for x in obj]
40    return dict((x, json_changebase(obj[x], changer)) for x in obj)
41
42# Transaction serialization and deserialization
43
44
45def deserialize(tx):
46    if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
47        #tx = bytes(bytearray.fromhex(tx))
48        return json_changebase(deserialize(binascii.unhexlify(tx)),
49                              lambda x: safe_hexlify(x))
50    # http://stackoverflow.com/questions/4851463/python-closure-write-to-variable-in-parent-scope
51    # Python's scoping rules are demented, requiring me to make pos an object
52    # so that it is call-by-reference
53    pos = [0]
54
55    def read_as_int(bytez):
56        pos[0] += bytez
57        return decode(tx[pos[0]-bytez:pos[0]][::-1], 256)
58
59    def read_var_int():
60        pos[0] += 1
61
62        val = from_byte_to_int(tx[pos[0]-1])
63        if val < 253:
64            return val
65        return read_as_int(pow(2, val - 252))
66
67    def read_bytes(bytez):
68        pos[0] += bytez
69        return tx[pos[0]-bytez:pos[0]]
70
71    def read_var_string():
72        size = read_var_int()
73        return read_bytes(size)
74
75    obj = {"ins": [], "outs": []}
76    obj["version"] = read_as_int(4)
77    ins = read_var_int()
78    for i in range(ins):
79        obj["ins"].append({
80            "outpoint": {
81                "hash": read_bytes(32)[::-1],
82                "index": read_as_int(4)
83            },
84            "script": read_var_string(),
85            "sequence": read_as_int(4)
86        })
87    outs = read_var_int()
88    for i in range(outs):
89        obj["outs"].append({
90            "value": read_as_int(8),
91            "script": read_var_string()
92        })
93    obj["locktime"] = read_as_int(4)
94    return obj
95
96def serialize(txobj):
97    #if isinstance(txobj, bytes):
98    #    txobj = bytes_to_hex_string(txobj)
99    o = []
100    if json_is_base(txobj, 16):
101        json_changedbase = json_changebase(txobj, lambda x: binascii.unhexlify(x))
102        hexlified = safe_hexlify(serialize(json_changedbase))
103        return hexlified
104    o.append(encode(txobj["version"], 256, 4)[::-1])
105    o.append(num_to_var_int(len(txobj["ins"])))
106    for inp in txobj["ins"]:
107        o.append(inp["outpoint"]["hash"][::-1])
108        o.append(encode(inp["outpoint"]["index"], 256, 4)[::-1])
109        o.append(num_to_var_int(len(inp["script"]))+(inp["script"] if inp["script"] or is_python2 else bytes()))
110        o.append(encode(inp["sequence"], 256, 4)[::-1])
111    o.append(num_to_var_int(len(txobj["outs"])))
112    for out in txobj["outs"]:
113        o.append(encode(out["value"], 256, 8)[::-1])
114        o.append(num_to_var_int(len(out["script"]))+out["script"])
115    o.append(encode(txobj["locktime"], 256, 4)[::-1])
116
117    return ''.join(o) if is_python2 else reduce(lambda x,y: x+y, o, bytes())
118
119# Hashing transactions for signing
120
121SIGHASH_ALL = 1
122SIGHASH_NONE = 2
123SIGHASH_SINGLE = 3
124# this works like SIGHASH_ANYONECANPAY | SIGHASH_ALL, might as well make it explicit while
125# we fix the constant
126SIGHASH_ANYONECANPAY = 0x81
127
128
129def signature_form(tx, i, script, hashcode=SIGHASH_ALL):
130    i, hashcode = int(i), int(hashcode)
131    if isinstance(tx, string_or_bytes_types):
132        return serialize(signature_form(deserialize(tx), i, script, hashcode))
133    newtx = copy.deepcopy(tx)
134    for inp in newtx["ins"]:
135        inp["script"] = ""
136    newtx["ins"][i]["script"] = script
137    if hashcode == SIGHASH_NONE:
138        newtx["outs"] = []
139    elif hashcode == SIGHASH_SINGLE:
140        newtx["outs"] = newtx["outs"][:len(newtx["ins"])]
141        for out in newtx["outs"][:len(newtx["ins"]) - 1]:
142            out['value'] = 2**64 - 1
143            out['script'] = ""
144    elif hashcode == SIGHASH_ANYONECANPAY:
145        newtx["ins"] = [newtx["ins"][i]]
146    else:
147        pass
148    return newtx
149
150# Making the actual signatures
151
152
153def der_encode_sig(v, r, s):
154    b1, b2 = safe_hexlify(encode(r, 256)), safe_hexlify(encode(s, 256))
155    if len(b1) and b1[0] in '89abcdef':
156        b1 = '00' + b1
157    if len(b2) and b2[0] in '89abcdef':
158        b2 = '00' + b2
159    left = '02'+encode(len(b1)//2, 16, 2)+b1
160    right = '02'+encode(len(b2)//2, 16, 2)+b2
161    return '30'+encode(len(left+right)//2, 16, 2)+left+right
162
163def der_decode_sig(sig):
164    leftlen = decode(sig[6:8], 16)*2
165    left = sig[8:8+leftlen]
166    rightlen = decode(sig[10+leftlen:12+leftlen], 16)*2
167    right = sig[12+leftlen:12+leftlen+rightlen]
168    return (None, decode(left, 16), decode(right, 16))
169
170def is_bip66(sig):
171    """Checks hex DER sig for BIP66 consistency"""
172    #https://raw.githubusercontent.com/bitcoin/bips/master/bip-0066.mediawiki
173    #0x30  [total-len]  0x02  [R-len]  [R]  0x02  [S-len]  [S]  [sighash]
174    sig = bytearray.fromhex(sig) if re.match('^[0-9a-fA-F]*$', sig) else bytearray(sig)
175    if (sig[0] == 0x30) and (sig[1] == len(sig)-2):     # check if sighash is missing
176            sig.extend(b"\1")		                   	# add SIGHASH_ALL for testing
177    #assert (sig[-1] & 124 == 0) and (not not sig[-1]), "Bad SIGHASH value"
178
179    if len(sig) < 9 or len(sig) > 73: return False
180    if (sig[0] != 0x30): return False
181    if (sig[1] != len(sig)-3): return False
182    rlen = sig[3]
183    if (5+rlen >= len(sig)): return False
184    slen = sig[5+rlen]
185    if (rlen + slen + 7 != len(sig)): return False
186    if (sig[2] != 0x02): return False
187    if (rlen == 0): return False
188    if (sig[4] & 0x80): return False
189    if (rlen > 1 and (sig[4] == 0x00) and not (sig[5] & 0x80)): return False
190    if (sig[4+rlen] != 0x02): return False
191    if (slen == 0): return False
192    if (sig[rlen+6] & 0x80): return False
193    if (slen > 1 and (sig[6+rlen] == 0x00) and not (sig[7+rlen] & 0x80)):
194        return False
195    return True
196
197def txhash(tx, hashcode=None):
198    if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
199        tx = changebase(tx, 16, 256)
200    if hashcode:
201        return dbl_sha256(from_string_to_bytes(tx) + encode(int(hashcode), 256, 4)[::-1])
202    else:
203        return safe_hexlify(bin_dbl_sha256(tx)[::-1])
204
205
206def bin_txhash(tx, hashcode=None):
207    return binascii.unhexlify(txhash(tx, hashcode))
208
209
210def ecdsa_tx_sign(tx, priv, hashcode=SIGHASH_ALL):
211    rawsig = ecdsa_raw_sign(bin_txhash(tx, hashcode), priv)
212    return der_encode_sig(*rawsig)+encode(hashcode, 16, 2)
213
214
215def ecdsa_tx_verify(tx, sig, pub, hashcode=SIGHASH_ALL):
216    return ecdsa_raw_verify(bin_txhash(tx, hashcode), der_decode_sig(sig), pub)
217
218
219def ecdsa_tx_recover(tx, sig, hashcode=SIGHASH_ALL):
220    z = bin_txhash(tx, hashcode)
221    _, r, s = der_decode_sig(sig)
222    left = ecdsa_raw_recover(z, (0, r, s))
223    right = ecdsa_raw_recover(z, (1, r, s))
224    return (encode_pubkey(left, 'hex'), encode_pubkey(right, 'hex'))
225
226# Scripts
227
228
229def mk_pubkey_script(addr):
230    # Keep the auxiliary functions around for altcoins' sake
231    return '76a914' + b58check_to_hex(addr) + '88ac'
232
233
234def mk_scripthash_script(addr):
235    return 'a914' + b58check_to_hex(addr) + '87'
236
237# Address representation to output script
238
239
240def address_to_script(addr):
241    if addr[0] == '3' or addr[0] == '2':
242        return mk_scripthash_script(addr)
243    else:
244        return mk_pubkey_script(addr)
245
246# Output script to address representation
247
248
249def script_to_address(script, vbyte=0):
250    if re.match('^[0-9a-fA-F]*$', script):
251        script = binascii.unhexlify(script)
252    if script[:3] == b'\x76\xa9\x14' and script[-2:] == b'\x88\xac' and len(script) == 25:
253        return bin_to_b58check(script[3:-2], vbyte)  # pubkey hash addresses
254    else:
255        if vbyte in [111, 196]:
256            # Testnet
257            scripthash_byte = 196
258        elif vbyte == 0:
259            # Mainnet
260            scripthash_byte = 5
261        else:
262            scripthash_byte = vbyte
263        # BIP0016 scripthash addresses
264        return bin_to_b58check(script[2:-1], scripthash_byte)
265
266
267def p2sh_scriptaddr(script, magicbyte=5):
268    if re.match('^[0-9a-fA-F]*$', script):
269        script = binascii.unhexlify(script)
270    return hex_to_b58check(hash160(script), magicbyte)
271scriptaddr = p2sh_scriptaddr
272
273
274def deserialize_script(script):
275    if isinstance(script, str) and re.match('^[0-9a-fA-F]*$', script):
276       return json_changebase(deserialize_script(binascii.unhexlify(script)),
277                              lambda x: safe_hexlify(x))
278    out, pos = [], 0
279    while pos < len(script):
280        code = from_byte_to_int(script[pos])
281        if code == 0:
282            out.append(None)
283            pos += 1
284        elif code <= 75:
285            out.append(script[pos+1:pos+1+code])
286            pos += 1 + code
287        elif code <= 78:
288            szsz = pow(2, code - 76)
289            sz = decode(script[pos+szsz: pos:-1], 256)
290            out.append(script[pos + 1 + szsz:pos + 1 + szsz + sz])
291            pos += 1 + szsz + sz
292        elif code <= 96:
293            out.append(code - 80)
294            pos += 1
295        else:
296            out.append(code)
297            pos += 1
298    return out
299
300
301def serialize_script_unit(unit):
302    if isinstance(unit, int):
303        if unit < 16:
304            return from_int_to_byte(unit + 80)
305        else:
306            return from_int_to_byte(unit)
307    elif unit is None:
308        return b'\x00'
309    else:
310        if len(unit) <= 75:
311            return from_int_to_byte(len(unit))+unit
312        elif len(unit) < 256:
313            return from_int_to_byte(76)+from_int_to_byte(len(unit))+unit
314        elif len(unit) < 65536:
315            return from_int_to_byte(77)+encode(len(unit), 256, 2)[::-1]+unit
316        else:
317            return from_int_to_byte(78)+encode(len(unit), 256, 4)[::-1]+unit
318
319
320if is_python2:
321    def serialize_script(script):
322        if json_is_base(script, 16):
323            return binascii.hexlify(serialize_script(json_changebase(script,
324                                    lambda x: binascii.unhexlify(x))))
325        return ''.join(map(serialize_script_unit, script))
326else:
327    def serialize_script(script):
328        if json_is_base(script, 16):
329            return safe_hexlify(serialize_script(json_changebase(script,
330                                    lambda x: binascii.unhexlify(x))))
331
332        result = bytes()
333        for b in map(serialize_script_unit, script):
334            result += b if isinstance(b, bytes) else bytes(b, 'utf-8')
335        return result
336
337
338def mk_multisig_script(*args):  # [pubs],k or pub1,pub2...pub[n],k
339    if isinstance(args[0], list):
340        pubs, k = args[0], int(args[1])
341    else:
342        pubs = list(filter(lambda x: len(str(x)) >= 32, args))
343        k = int(args[len(pubs)])
344    return serialize_script([k]+pubs+[len(pubs)]+[0xae])
345
346# Signing and verifying
347
348
349def verify_tx_input(tx, i, script, sig, pub):
350    if re.match('^[0-9a-fA-F]*$', tx):
351        tx = binascii.unhexlify(tx)
352    if re.match('^[0-9a-fA-F]*$', script):
353        script = binascii.unhexlify(script)
354    if not re.match('^[0-9a-fA-F]*$', sig):
355        sig = safe_hexlify(sig)
356    hashcode = decode(sig[-2:], 16)
357    modtx = signature_form(tx, int(i), script, hashcode)
358    return ecdsa_tx_verify(modtx, sig, pub, hashcode)
359
360
361def sign(tx, i, priv, hashcode=SIGHASH_ALL):
362    i = int(i)
363    if (not is_python2 and isinstance(re, bytes)) or not re.match('^[0-9a-fA-F]*$', tx):
364        return binascii.unhexlify(sign(safe_hexlify(tx), i, priv))
365    if len(priv) <= 33:
366        priv = safe_hexlify(priv)
367    pub = privkey_to_pubkey(priv)
368    address = pubkey_to_address(pub)
369    signing_tx = signature_form(tx, i, mk_pubkey_script(address), hashcode)
370    sig = ecdsa_tx_sign(signing_tx, priv, hashcode)
371    txobj = deserialize(tx)
372    txobj["ins"][i]["script"] = serialize_script([sig, pub])
373    return serialize(txobj)
374
375
376def signall(tx, priv):
377    # if priv is a dictionary, assume format is
378    # { 'txinhash:txinidx' : privkey }
379    if isinstance(priv, dict):
380        for e, i in enumerate(deserialize(tx)["ins"]):
381            k = priv["%s:%d" % (i["outpoint"]["hash"], i["outpoint"]["index"])]
382            tx = sign(tx, e, k)
383    else:
384        for i in range(len(deserialize(tx)["ins"])):
385            tx = sign(tx, i, priv)
386    return tx
387
388
389def multisign(tx, i, script, pk, hashcode=SIGHASH_ALL):
390    if re.match('^[0-9a-fA-F]*$', tx):
391        tx = binascii.unhexlify(tx)
392    if re.match('^[0-9a-fA-F]*$', script):
393        script = binascii.unhexlify(script)
394    modtx = signature_form(tx, i, script, hashcode)
395    return ecdsa_tx_sign(modtx, pk, hashcode)
396
397
398def apply_multisignatures(*args):
399    # tx,i,script,sigs OR tx,i,script,sig1,sig2...,sig[n]
400    tx, i, script = args[0], int(args[1]), args[2]
401    sigs = args[3] if isinstance(args[3], list) else list(args[3:])
402
403    if isinstance(script, str) and re.match('^[0-9a-fA-F]*$', script):
404        script = binascii.unhexlify(script)
405    sigs = [binascii.unhexlify(x) if x[:2] == '30' else x for x in sigs]
406    if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
407        return safe_hexlify(apply_multisignatures(binascii.unhexlify(tx), i, script, sigs))
408
409    # Not pushing empty elements on the top of the stack if passing no
410    # script (in case of bare multisig inputs there is no script)
411    script_blob = [] if script.__len__() == 0 else [script]
412
413    txobj = deserialize(tx)
414    txobj["ins"][i]["script"] = serialize_script([None]+sigs+script_blob)
415    return serialize(txobj)
416
417
418def is_inp(arg):
419    return len(arg) > 64 or "output" in arg or "outpoint" in arg
420
421
422def mktx(*args):
423    # [in0, in1...],[out0, out1...] or in0, in1 ... out0 out1 ...
424    ins, outs = [], []
425    for arg in args:
426        if isinstance(arg, list):
427            for a in arg: (ins if is_inp(a) else outs).append(a)
428        else:
429            (ins if is_inp(arg) else outs).append(arg)
430
431    txobj = {"locktime": 0, "version": 1, "ins": [], "outs": []}
432    for i in ins:
433        if isinstance(i, dict) and "outpoint" in i:
434            txobj["ins"].append(i)
435        else:
436            if isinstance(i, dict) and "output" in i:
437                i = i["output"]
438            txobj["ins"].append({
439                "outpoint": {"hash": i[:64], "index": int(i[65:])},
440                "script": "",
441                "sequence": 4294967295
442            })
443    for o in outs:
444        if isinstance(o, string_or_bytes_types):
445            addr = o[:o.find(':')]
446            val = int(o[o.find(':')+1:])
447            o = {}
448            if re.match('^[0-9a-fA-F]*$', addr):
449                o["script"] = addr
450            else:
451                o["address"] = addr
452            o["value"] = val
453
454        outobj = {}
455        if "address" in o:
456            outobj["script"] = address_to_script(o["address"])
457        elif "script" in o:
458            outobj["script"] = o["script"]
459        else:
460            raise Exception("Could not find 'address' or 'script' in output.")
461        outobj["value"] = o["value"]
462        txobj["outs"].append(outobj)
463
464    return serialize(txobj)
465
466
467def select(unspent, value):
468    value = int(value)
469    high = [u for u in unspent if u["value"] >= value]
470    high.sort(key=lambda u: u["value"])
471    low = [u for u in unspent if u["value"] < value]
472    low.sort(key=lambda u: -u["value"])
473    if len(high):
474        return [high[0]]
475    i, tv = 0, 0
476    while tv < value and i < len(low):
477        tv += low[i]["value"]
478        i += 1
479    if tv < value:
480        raise Exception("Not enough funds")
481    return low[:i]
482
483# Only takes inputs of the form { "output": blah, "value": foo }
484
485
486def mksend(*args):
487    argz, change, fee = args[:-2], args[-2], int(args[-1])
488    ins, outs = [], []
489    for arg in argz:
490        if isinstance(arg, list):
491            for a in arg:
492                (ins if is_inp(a) else outs).append(a)
493        else:
494            (ins if is_inp(arg) else outs).append(arg)
495
496    isum = sum([i["value"] for i in ins])
497    osum, outputs2 = 0, []
498    for o in outs:
499        if isinstance(o, string_types):
500            o2 = {
501                "address": o[:o.find(':')],
502                "value": int(o[o.find(':')+1:])
503            }
504        else:
505            o2 = o
506        outputs2.append(o2)
507        osum += o2["value"]
508
509    if isum < osum+fee:
510        raise Exception("Not enough money")
511    elif isum > osum+fee+5430:
512        outputs2 += [{"address": change, "value": isum-osum-fee}]
513
514    return mktx(ins, outputs2)
515