transaction.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)