URI:
       ttransaction.py - electrum-personal-server - Maximally lightweight electrum server for a single user
  HTML git clone https://git.parazyd.org/electrum-personal-server
   DIR Log
   DIR Files
   DIR Refs
   DIR README
       ---
       ttransaction.py (15760B)
       ---
            1 #!/usr/bin/python
            2 import binascii, re, json, copy, sys
            3 from electrumpersonalserver.bitcoin.main import *
            4 from _functools import reduce
            5 
            6 ### Hex to bin converter and vice versa for objects
            7 
            8 
            9 def 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 
           33 def 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 
           45 def 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(
           49             deserialize(binascii.unhexlify(tx)), 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 
           96 
           97 def serialize(txobj):
           98     #if isinstance(txobj, bytes):
           99     #    txobj = bytes_to_hex_string(txobj)
          100     o = []
          101     if json_is_base(txobj, 16):
          102         json_changedbase = json_changebase(txobj,
          103                                            lambda x: binascii.unhexlify(x))
          104         hexlified = safe_hexlify(serialize(json_changedbase))
          105         return hexlified
          106     o.append(encode(txobj["version"], 256, 4)[::-1])
          107     o.append(num_to_var_int(len(txobj["ins"])))
          108     for inp in txobj["ins"]:
          109         o.append(inp["outpoint"]["hash"][::-1])
          110         o.append(encode(inp["outpoint"]["index"], 256, 4)[::-1])
          111         o.append(num_to_var_int(len(inp["script"])) + (inp["script"] if inp[
          112             "script"] or is_python2 else bytes()))
          113         o.append(encode(inp["sequence"], 256, 4)[::-1])
          114     o.append(num_to_var_int(len(txobj["outs"])))
          115     for out in txobj["outs"]:
          116         o.append(encode(out["value"], 256, 8)[::-1])
          117         o.append(num_to_var_int(len(out["script"])) + out["script"])
          118     o.append(encode(txobj["locktime"], 256, 4)[::-1])
          119 
          120     return ''.join(o) if is_python2 else reduce(lambda x, y: x + y, o, bytes())
          121 
          122 # Hashing transactions for signing
          123 
          124 SIGHASH_ALL = 1
          125 SIGHASH_NONE = 2
          126 SIGHASH_SINGLE = 3
          127 # this works like SIGHASH_ANYONECANPAY | SIGHASH_ALL, might as well make it explicit while
          128 # we fix the constant
          129 SIGHASH_ANYONECANPAY = 0x81
          130 
          131 
          132 def signature_form(tx, i, script, hashcode=SIGHASH_ALL):
          133     i, hashcode = int(i), int(hashcode)
          134     if isinstance(tx, string_or_bytes_types):
          135         return serialize(signature_form(deserialize(tx), i, script, hashcode))
          136     newtx = copy.deepcopy(tx)
          137     for inp in newtx["ins"]:
          138         inp["script"] = ""
          139     newtx["ins"][i]["script"] = script
          140     if hashcode == SIGHASH_NONE:
          141         newtx["outs"] = []
          142     elif hashcode == SIGHASH_SINGLE:
          143         newtx["outs"] = newtx["outs"][:len(newtx["ins"])]
          144         for out in range(len(newtx["ins"]) - 1):
          145             out.value = 2**64 - 1
          146             out.script = ""
          147     elif hashcode == SIGHASH_ANYONECANPAY:
          148         newtx["ins"] = [newtx["ins"][i]]
          149     else:
          150         pass
          151     return newtx
          152 
          153 # Making the actual signatures
          154 
          155 
          156 def der_encode_sig(v, r, s):
          157     """Takes (vbyte, r, s) as ints and returns hex der encode sig"""
          158     #See https://github.com/vbuterin/pybitcointools/issues/89
          159     #See https://github.com/simcity4242/pybitcointools/
          160     s = N - s if s > N // 2 else s  # BIP62 low s
          161     b1, b2 = encode(r, 256), encode(s, 256)
          162     if bytearray(b1)[
          163             0] & 0x80:  # add null bytes if leading byte interpreted as negative
          164         b1 = b'\x00' + b1
          165     if bytearray(b2)[0] & 0x80:
          166         b2 = b'\x00' + b2
          167     left = b'\x02' + encode(len(b1), 256, 1) + b1
          168     right = b'\x02' + encode(len(b2), 256, 1) + b2
          169     return safe_hexlify(b'\x30' + encode(
          170         len(left + right), 256, 1) + left + right)
          171 
          172 
          173 def der_decode_sig(sig):
          174     leftlen = decode(sig[6:8], 16) * 2
          175     left = sig[8:8 + leftlen]
          176     rightlen = decode(sig[10 + leftlen:12 + leftlen], 16) * 2
          177     right = sig[12 + leftlen:12 + leftlen + rightlen]
          178     return (None, decode(left, 16), decode(right, 16))
          179 
          180 
          181 def txhash(tx, hashcode=None):
          182     if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
          183         tx = changebase(tx, 16, 256)
          184     if hashcode:
          185         return dbl_sha256(from_string_to_bytes(tx) + encode(
          186             int(hashcode), 256, 4)[::-1])
          187     else:
          188         return safe_hexlify(bin_dbl_sha256(tx)[::-1])
          189 
          190 
          191 def bin_txhash(tx, hashcode=None):
          192     return binascii.unhexlify(txhash(tx, hashcode))
          193 
          194 
          195 def ecdsa_tx_sign(tx, priv, hashcode=SIGHASH_ALL):
          196     rawsig = ecdsa_raw_sign(bin_txhash(tx, hashcode), priv)
          197     return der_encode_sig(*rawsig) + encode(hashcode, 16, 2)
          198 
          199 
          200 def ecdsa_tx_verify(tx, sig, pub, hashcode=SIGHASH_ALL):
          201     return ecdsa_raw_verify(bin_txhash(tx, hashcode), der_decode_sig(sig), pub)
          202 
          203 # Scripts
          204 
          205 def mk_pubkey_script(addr):
          206     # Keep the auxiliary functions around for altcoins' sake
          207     return '76a914' + b58check_to_hex(addr) + '88ac'
          208 
          209 
          210 def mk_scripthash_script(addr):
          211     return 'a914' + b58check_to_hex(addr) + '87'
          212 
          213 # Address representation to output script
          214 
          215 
          216 def address_to_script(addr):
          217     if addr[0] == '3' or addr[0] == '2':
          218         return mk_scripthash_script(addr)
          219     else:
          220         return mk_pubkey_script(addr)
          221 
          222 # Output script to address representation
          223 
          224 
          225 def script_to_address(script, vbyte=0):
          226     if re.match('^[0-9a-fA-F]*$', script):
          227         script = binascii.unhexlify(script)
          228     if script[:3] == b'\x76\xa9\x14' and script[-2:] == b'\x88\xac' and len(
          229             script) == 25:
          230         return bin_to_b58check(script[3:-2], vbyte)  # pubkey hash addresses
          231     else:
          232         if vbyte in [111, 196]:
          233             # Testnet
          234             scripthash_byte = 196
          235         else:
          236             scripthash_byte = 5
          237         # BIP0016 scripthash addresses
          238         return bin_to_b58check(script[2:-1], scripthash_byte)
          239 
          240 
          241 def p2sh_scriptaddr(script, magicbyte=5):
          242     if re.match('^[0-9a-fA-F]*$', script):
          243         script = binascii.unhexlify(script)
          244     return hex_to_b58check(hash160(script), magicbyte)
          245 
          246 
          247 scriptaddr = p2sh_scriptaddr
          248 
          249 
          250 def deserialize_script(script):
          251     if isinstance(script, str) and re.match('^[0-9a-fA-F]*$', script):
          252         return json_changebase(
          253             deserialize_script(binascii.unhexlify(script)),
          254             lambda x: safe_hexlify(x))
          255     out, pos = [], 0
          256     while pos < len(script):
          257         code = from_byte_to_int(script[pos])
          258         if code == 0:
          259             out.append(None)
          260             pos += 1
          261         elif code <= 75:
          262             out.append(script[pos + 1:pos + 1 + code])
          263             pos += 1 + code
          264         elif code <= 78:
          265             szsz = pow(2, code - 76)
          266             sz = decode(script[pos + szsz:pos:-1], 256)
          267             out.append(script[pos + 1 + szsz:pos + 1 + szsz + sz])
          268             pos += 1 + szsz + sz
          269         elif code <= 96:
          270             out.append(code - 80)
          271             pos += 1
          272         else:
          273             out.append(code)
          274             pos += 1
          275     return out
          276 
          277 
          278 def serialize_script_unit(unit):
          279     if isinstance(unit, int):
          280         if unit < 16:
          281             return from_int_to_byte(unit + 80)
          282         else:
          283             return bytes([unit])
          284     elif unit is None:
          285         return b'\x00'
          286     else:
          287         if len(unit) <= 75:
          288             return from_int_to_byte(len(unit)) + unit
          289         elif len(unit) < 256:
          290             return from_int_to_byte(76) + from_int_to_byte(len(unit)) + unit
          291         elif len(unit) < 65536:
          292             return from_int_to_byte(77) + encode(len(unit), 256, 2)[::-1] + unit
          293         else:
          294             return from_int_to_byte(78) + encode(len(unit), 256, 4)[::-1] + unit
          295 
          296 
          297 if is_python2:
          298 
          299     def serialize_script(script):
          300         if json_is_base(script, 16):
          301             return binascii.hexlify(serialize_script(json_changebase(
          302                 script, lambda x: binascii.unhexlify(x))))
          303         return ''.join(map(serialize_script_unit, script))
          304 else:
          305 
          306     def serialize_script(script):
          307         if json_is_base(script, 16):
          308             return safe_hexlify(serialize_script(json_changebase(
          309                 script, lambda x: binascii.unhexlify(x))))
          310 
          311         result = bytes()
          312         for b in map(serialize_script_unit, script):
          313             result += b if isinstance(b, bytes) else bytes(b, 'utf-8')
          314         return result
          315 
          316 
          317 def mk_multisig_script(*args):  # [pubs],k or pub1,pub2...pub[n],k
          318     if isinstance(args[0], list):
          319         pubs, k = args[0], int(args[1])
          320     else:
          321         pubs = list(filter(lambda x: len(str(x)) >= 32, args))
          322         k = int(args[len(pubs)])
          323     return serialize_script([k] + pubs + [len(pubs)]) + 'ae'
          324 
          325 # Signing and verifying
          326 
          327 
          328 def verify_tx_input(tx, i, script, sig, pub):
          329     if re.match('^[0-9a-fA-F]*$', tx):
          330         tx = binascii.unhexlify(tx)
          331     if re.match('^[0-9a-fA-F]*$', script):
          332         script = binascii.unhexlify(script)
          333     if not re.match('^[0-9a-fA-F]*$', sig):
          334         sig = safe_hexlify(sig)
          335     hashcode = decode(sig[-2:], 16)
          336     modtx = signature_form(tx, int(i), script, hashcode)
          337     return ecdsa_tx_verify(modtx, sig, pub, hashcode)
          338 
          339 
          340 def sign(tx, i, priv, hashcode=SIGHASH_ALL):
          341     i = int(i)
          342     if (not is_python2 and isinstance(re, bytes)) or not re.match(
          343             '^[0-9a-fA-F]*$', tx):
          344         return binascii.unhexlify(sign(safe_hexlify(tx), i, priv))
          345     if len(priv) <= 33:
          346         priv = safe_hexlify(priv)
          347     pub = privkey_to_pubkey(priv)
          348     address = pubkey_to_address(pub)
          349     signing_tx = signature_form(tx, i, mk_pubkey_script(address), hashcode)
          350     sig = ecdsa_tx_sign(signing_tx, priv, hashcode)
          351     txobj = deserialize(tx)
          352     txobj["ins"][i]["script"] = serialize_script([sig, pub])
          353     return serialize(txobj)
          354 
          355 
          356 def signall(tx, priv):
          357     # if priv is a dictionary, assume format is
          358     # { 'txinhash:txinidx' : privkey }
          359     if isinstance(priv, dict):
          360         for e, i in enumerate(deserialize(tx)["ins"]):
          361             k = priv["%s:%d" % (i["outpoint"]["hash"], i["outpoint"]["index"])]
          362             tx = sign(tx, e, k)
          363     else:
          364         for i in range(len(deserialize(tx)["ins"])):
          365             tx = sign(tx, i, priv)
          366     return tx
          367 
          368 
          369 def multisign(tx, i, script, pk, hashcode=SIGHASH_ALL):
          370     if re.match('^[0-9a-fA-F]*$', tx):
          371         tx = binascii.unhexlify(tx)
          372     if re.match('^[0-9a-fA-F]*$', script):
          373         script = binascii.unhexlify(script)
          374     modtx = signature_form(tx, i, script, hashcode)
          375     return ecdsa_tx_sign(modtx, pk, hashcode)
          376 
          377 
          378 def apply_multisignatures(*args):
          379     # tx,i,script,sigs OR tx,i,script,sig1,sig2...,sig[n]
          380     tx, i, script = args[0], int(args[1]), args[2]
          381     sigs = args[3] if isinstance(args[3], list) else list(args[3:])
          382 
          383     if isinstance(script, str) and re.match('^[0-9a-fA-F]*$', script):
          384         script = binascii.unhexlify(script)
          385     sigs = [binascii.unhexlify(x) if x[:2] == '30' else x for x in sigs]
          386     if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
          387         return safe_hexlify(apply_multisignatures(
          388             binascii.unhexlify(tx), i, script, sigs))
          389 
          390     txobj = deserialize(tx)
          391     txobj["ins"][i]["script"] = serialize_script([None] + sigs + [script])
          392     return serialize(txobj)
          393 
          394 
          395 def is_inp(arg):
          396     return len(arg) > 64 or "output" in arg or "outpoint" in arg
          397 
          398 
          399 def mktx(*args):
          400     # [in0, in1...],[out0, out1...] or in0, in1 ... out0 out1 ...
          401     ins, outs = [], []
          402     for arg in args:
          403         if isinstance(arg, list):
          404             for a in arg:
          405                 (ins if is_inp(a) else outs).append(a)
          406         else:
          407             (ins if is_inp(arg) else outs).append(arg)
          408 
          409     txobj = {"locktime": 0, "version": 1, "ins": [], "outs": []}
          410     for i in ins:
          411         if isinstance(i, dict) and "outpoint" in i:
          412             txobj["ins"].append(i)
          413         else:
          414             if isinstance(i, dict) and "output" in i:
          415                 i = i["output"]
          416             txobj["ins"].append({
          417                 "outpoint": {"hash": i[:64],
          418                              "index": int(i[65:])},
          419                 "script": "",
          420                 "sequence": 4294967295
          421             })
          422     for o in outs:
          423         if isinstance(o, string_or_bytes_types):
          424             addr = o[:o.find(':')]
          425             val = int(o[o.find(':') + 1:])
          426             o = {}
          427             if re.match('^[0-9a-fA-F]*$', addr):
          428                 o["script"] = addr
          429             else:
          430                 o["address"] = addr
          431             o["value"] = val
          432 
          433         outobj = {}
          434         if "address" in o:
          435             outobj["script"] = address_to_script(o["address"])
          436         elif "script" in o:
          437             outobj["script"] = o["script"]
          438         else:
          439             raise Exception("Could not find 'address' or 'script' in output.")
          440         outobj["value"] = o["value"]
          441         txobj["outs"].append(outobj)
          442 
          443     return serialize(txobj)
          444 
          445 
          446 def select(unspent, value):
          447     value = int(value)
          448     high = [u for u in unspent if u["value"] >= value]
          449     high.sort(key=lambda u: u["value"])
          450     low = [u for u in unspent if u["value"] < value]
          451     low.sort(key=lambda u: -u["value"])
          452     if len(high):
          453         return [high[0]]
          454     i, tv = 0, 0
          455     while tv < value and i < len(low):
          456         tv += low[i]["value"]
          457         i += 1
          458     if tv < value:
          459         raise Exception("Not enough funds")
          460     return low[:i]
          461 
          462 # Only takes inputs of the form { "output": blah, "value": foo }
          463 
          464 
          465 def mksend(*args):
          466     argz, change, fee = args[:-2], args[-2], int(args[-1])
          467     ins, outs = [], []
          468     for arg in argz:
          469         if isinstance(arg, list):
          470             for a in arg:
          471                 (ins if is_inp(a) else outs).append(a)
          472         else:
          473             (ins if is_inp(arg) else outs).append(arg)
          474 
          475     isum = sum([i["value"] for i in ins])
          476     osum, outputs2 = 0, []
          477     for o in outs:
          478         if isinstance(o, string_types):
          479             o2 = {"address": o[:o.find(':')], "value": int(o[o.find(':') + 1:])}
          480         else:
          481             o2 = o
          482         outputs2.append(o2)
          483         osum += o2["value"]
          484 
          485     if isum < osum + fee:
          486         raise Exception("Not enough money")
          487     elif isum > osum + fee + 5430:
          488         outputs2 += [{"address": change, "value": isum - osum - fee}]
          489 
          490     return mktx(ins, outputs2)