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