import hashlib from logging import getLogger from unittest import TestCase from ecc import ( S256Point, Signature, ) from helper import ( hash160, hash256, ) LOGGER = getLogger(__name__) def encode_num(num): if num == 0: return b'' abs_num = abs(num) negative = num < 0 result = bytearray() while abs_num: result.append(abs_num & 0xff) abs_num >>= 8 # if the top bit is set, # for negative numbers we ensure that the top bit is set # for positive numbers we ensure that the top bit is not set if result[-1] & 0x80: if negative: result.append(0x80) else: result.append(0) elif negative: result[-1] |= 0x80 return bytes(result) def decode_num(element): if element == b'': return 0 # reverse for big endian big_endian = element[::-1] # top bit being 1 means it's negative if big_endian[0] & 0x80: negative = True result = big_endian[0] & 0x7f else: negative = False result = big_endian[0] for c in big_endian[1:]: result <<= 8 result += c if negative: return -result else: return result def op_0(stack): stack.append(encode_num(0)) return True def op_1negate(stack): stack.append(encode_num(-1)) return True def op_1(stack): stack.append(encode_num(1)) return True def op_2(stack): stack.append(encode_num(2)) return True def op_3(stack): stack.append(encode_num(3)) return True def op_4(stack): stack.append(encode_num(4)) return True def op_5(stack): stack.append(encode_num(5)) return True def op_6(stack): stack.append(encode_num(6)) return True def op_7(stack): stack.append(encode_num(7)) return True def op_8(stack): stack.append(encode_num(8)) return True def op_9(stack): stack.append(encode_num(9)) return True def op_10(stack): stack.append(encode_num(10)) return True def op_11(stack): stack.append(encode_num(11)) return True def op_12(stack): stack.append(encode_num(12)) return True def op_13(stack): stack.append(encode_num(13)) return True def op_14(stack): stack.append(encode_num(14)) return True def op_15(stack): stack.append(encode_num(15)) return True def op_16(stack): stack.append(encode_num(16)) return True def op_nop(stack): return True def op_if(stack, items): if len(stack) < 1: return False # go through and re-make the items array based on the top stack element true_items = [] false_items = [] current_array = true_items found = False num_endifs_needed = 1 while len(items) > 0: item = items.pop(0) if item in (99, 100): # nested if, we have to go another endif num_endifs_needed += 1 current_array.append(item) elif num_endifs_needed == 1 and item == 103: current_array = false_items elif item == 104: if num_endifs_needed == 1: found = True break else: num_endifs_needed -= 1 current_array.append(item) else: current_array.append(item) if not found: return False element = stack.pop() if decode_num(element) == 0: items[:0] = false_items else: items[:0] = true_items return True def op_notif(stack, items): if len(stack) < 1: return False # go through and re-make the items array based on the top stack element true_items = [] false_items = [] current_array = true_items found = False num_endifs_needed = 1 while len(items) > 0: item = items.pop(0) if item in (99, 100): # nested if, we have to go another endif num_endifs_needed += 1 current_array.append(item) elif num_endifs_needed == 1 and item == 103: current_array = false_items elif item == 104: if num_endifs_needed == 1: found = True break else: num_endifs_needed -= 1 current_array.append(item) else: current_array.append(item) if not found: return False element = stack.pop() if decode_num(element) == 0: items[:0] = true_items else: items[:0] = false_items return True def op_verify(stack): if len(stack) < 1: return False element = stack.pop() if decode_num(element) == 0: return False return True def op_return(stack): return False def op_toaltstack(stack, altstack): if len(stack) < 1: return False altstack.append(stack.pop()) return True def op_fromaltstack(stack, altstack): if len(altstack) < 1: return False stack.append(altstack.pop()) return True def op_2drop(stack): if len(stack) < 2: return False stack.pop() stack.pop() return True def op_2dup(stack): if len(stack) < 2: return False stack.extend(stack[-2:]) return True def op_3dup(stack): if len(stack) < 3: return False stack.extend(stack[-3:]) return True def op_2over(stack): if len(stack) < 4: return False stack.extend(stack[-4:-2]) return True def op_2rot(stack): if len(stack) < 6: return False stack.extend(stack[-6:-4]) return True def op_2swap(stack): if len(stack) < 4: return False stack[-4:] = stack[-2:] + stack[-4:-2] return True def op_ifdup(stack): if len(stack) < 1: return False if decode_num(stack[-1]) != 0: stack.append(stack[-1]) return True def op_depth(stack): stack.append(encode_num(len(stack))) return True def op_drop(stack): if len(stack) < 1: return False stack.pop() return True def op_dup(stack): if len(stack) < 1: return False stack.append(stack[-1]) return True def op_nip(stack): if len(stack) < 2: return False stack[-2:] = stack[-1:] return True def op_over(stack): if len(stack) < 2: return False stack.append(stack[-2]) return True def op_pick(stack): if len(stack) < 1: return False n = decode_num(stack.pop()) if len(stack) < n + 1: return False stack.append(stack[-n - 1]) return True def op_roll(stack): if len(stack) < 1: return False n = decode_num(stack.pop()) if len(stack) < n + 1: return False if n == 0: return True stack.append(stack.pop(-n - 1)) return True def op_rot(stack): if len(stack) < 3: return False stack.append(stack.pop(-3)) return True def op_swap(stack): if len(stack) < 2: return False stack.append(stack.pop(-2)) return True def op_tuck(stack): if len(stack) < 2: return False stack.insert(-2, stack[-1]) return True def op_size(stack): if len(stack) < 1: return False stack.append(encode_num(len(stack[-1]))) return True def op_equal(stack): if len(stack) < 2: return False element1 = stack.pop() element2 = stack.pop() if element1 == element2: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_equalverify(stack): return op_equal(stack) and op_verify(stack) def op_1add(stack): if len(stack) < 1: return False element = decode_num(stack.pop()) stack.append(encode_num(element + 1)) return True def op_1sub(stack): if len(stack) < 1: return False element = decode_num(stack.pop()) stack.append(encode_num(element - 1)) return True def op_negate(stack): if len(stack) < 1: return False element = decode_num(stack.pop()) stack.append(encode_num(-element)) return True def op_abs(stack): if len(stack) < 1: return False element = decode_num(stack.pop()) if element < 0: stack.append(encode_num(-element)) else: stack.append(encode_num(element)) return True def op_not(stack): if len(stack) < 1: return False element = stack.pop() if decode_num(element) == 0: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_0notequal(stack): if len(stack) < 1: return False element = stack.pop() if decode_num(element) == 0: stack.append(encode_num(0)) else: stack.append(encode_num(1)) return True def op_add(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) stack.append(encode_num(element1 + element2)) return True def op_sub(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) stack.append(encode_num(element2 - element1)) return True def op_booland(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 and element2: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_boolor(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 or element2: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_numequal(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 == element2: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_numequalverify(stack): return op_numequal(stack) and op_verify(stack) def op_numnotequal(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 == element2: stack.append(encode_num(0)) else: stack.append(encode_num(1)) return True def op_lessthan(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element2 < element1: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_greaterthan(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element2 > element1: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_lessthanorequal(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element2 <= element1: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_greaterthanorequal(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element2 >= element1: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_min(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 < element2: stack.append(encode_num(element1)) else: stack.append(encode_num(element2)) return True def op_max(stack): if len(stack) < 2: return False element1 = decode_num(stack.pop()) element2 = decode_num(stack.pop()) if element1 > element2: stack.append(encode_num(element1)) else: stack.append(encode_num(element2)) return True def op_within(stack): if len(stack) < 3: return False maximum = decode_num(stack.pop()) minimum = decode_num(stack.pop()) element = decode_num(stack.pop()) if element >= minimum and element < maximum: stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_ripemd160(stack): if len(stack) < 1: return False element = stack.pop() stack.append(hashlib.new('ripemd160', element).digest()) return True def op_sha1(stack): if len(stack) < 1: return False element = stack.pop() stack.append(hashlib.sha1(element).digest()) return True def op_sha256(stack): if len(stack) < 1: return False element = stack.pop() stack.append(hashlib.sha256(element).digest()) return True def op_hash160(stack): # check that there's at least 1 element on the stack if len(stack) < 1: return False # pop off the top element from the stack element = stack.pop() # push a hash160 of the popped off element to the stack h160 = hash160(element) stack.append(h160) return True def op_hash256(stack): if len(stack) < 1: return False element = stack.pop() stack.append(hash256(element)) return True def op_checksig(stack, z): # check that there are at least 2 elements on the stack if len(stack) < 2: return False # the top element of the stack is the SEC pubkey sec_pubkey = stack.pop() # the next element of the stack is the DER signature # take off the last byte of the signature as that's the hash_type der_signature = stack.pop()[:-1] # parse the serialized pubkey and signature into objects try: point = S256Point.parse(sec_pubkey) sig = Signature.parse(der_signature) except (ValueError, SyntaxError) as e: LOGGER.info(e) return False # verify the signature using S256Point.verify() # push an encoded 1 or 0 depending on whether the signature verified if point.verify(z, sig): stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True def op_checksigverify(stack, z): return op_checksig(stack, z) and op_verify(stack) def op_checkmultisig(stack, z): if len(stack) < 1: return False n = decode_num(stack.pop()) if len(stack) < n + 1: return False sec_pubkeys = [] for _ in range(n): sec_pubkeys.append(stack.pop()) m = decode_num(stack.pop()) if len(stack) < m + 1: return False der_signatures = [] for _ in range(m): # signature is assumed to be using SIGHASH_ALL der_signatures.append(stack.pop()[:-1]) # OP_CHECKMULTISIG bug stack.pop() try: # parse all the points points = [S256Point.parse(sec) for sec in sec_pubkeys] # parse all the signatures sigs = [Signature.parse(der) for der in der_signatures] # loop through the signatures for sig in sigs: # if we have no more points, signatures are no good if len(points) == 0: LOGGER.info("signatures no good or not in right order") return False # we loop until we find the point which works with this signature while points: # get the current point from the list of points point = points.pop(0) # we check if this signature goes with the current point if point.verify(z, sig): break # the signatures are valid, so push a 1 to the stack stack.append(encode_num(1)) except (ValueError, SyntaxError): return False return True def op_checkmultisigverify(stack, z): return op_checkmultisig(stack, z) and op_verify(stack) def op_checklocktimeverify(stack, locktime, sequence): if sequence == 0xffffffff: return False if len(stack) < 1: return False element = decode_num(stack[-1]) if element < 0: return False if element < 500000000 and locktime > 500000000: return False if locktime < element: return False return True def op_checksequenceverify(stack, version, sequence): if sequence & (1 << 31) == (1 << 31): return False if len(stack) < 1: return False element = decode_num(stack[-1]) if element < 0: return False if element & (1 << 31) == (1 << 31): if version < 2: return False elif sequence & (1 << 31) == (1 << 31): return False elif element & (1 << 22) != sequence & (1 << 22): return False elif element & 0xffff > sequence & 0xffff: return False return True class OpTest(TestCase): def test_op_hash160(self): stack = [b'hello world'] self.assertTrue(op_hash160(stack)) self.assertEqual( stack[0].hex(), 'd7d5ee7824ff93f94c3055af9382c86c68b5ca92') def test_op_checksig(self): z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d sec = bytes.fromhex('04887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34') sig = bytes.fromhex('3045022000eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c022100c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab601') stack = [sig, sec] self.assertTrue(op_checksig(stack, z)) self.assertEqual(decode_num(stack[0]), 1) def test_op_checkmultisig(self): z = 0xe71bfa115715d6fd33796948126f40a8cdd39f187e4afb03896795189fe1423c sig1 = bytes.fromhex('3045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a8fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4ee942a8993701') sig2 = bytes.fromhex('3045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e75402201') sec1 = bytes.fromhex('022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb70') sec2 = bytes.fromhex('03b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb71') stack = [b'', sig1, sig2, b'\x02', sec1, sec2, b'\x02'] self.assertTrue(op_checkmultisig(stack, z)) self.assertEqual(decode_num(stack[0]), 1) OP_CODE_FUNCTIONS = { 0: op_0, 79: op_1negate, 81: op_1, 82: op_2, 83: op_3, 84: op_4, 85: op_5, 86: op_6, 87: op_7, 88: op_8, 89: op_9, 90: op_10, 91: op_11, 92: op_12, 93: op_13, 94: op_14, 95: op_15, 96: op_16, 97: op_nop, 99: op_if, 100: op_notif, 105: op_verify, 106: op_return, 107: op_toaltstack, 108: op_fromaltstack, 109: op_2drop, 110: op_2dup, 111: op_3dup, 112: op_2over, 113: op_2rot, 114: op_2swap, 115: op_ifdup, 116: op_depth, 117: op_drop, 118: op_dup, 119: op_nip, 120: op_over, 121: op_pick, 122: op_roll, 123: op_rot, 124: op_swap, 125: op_tuck, 130: op_size, 135: op_equal, 136: op_equalverify, 139: op_1add, 140: op_1sub, 143: op_negate, 144: op_abs, 145: op_not, 146: op_0notequal, 147: op_add, 148: op_sub, 154: op_booland, 155: op_boolor, 156: op_numequal, 157: op_numequalverify, 158: op_numnotequal, 159: op_lessthan, 160: op_greaterthan, 161: op_lessthanorequal, 162: op_greaterthanorequal, 163: op_min, 164: op_max, 165: op_within, 166: op_ripemd160, 167: op_sha1, 168: op_sha256, 169: op_hash160, 170: op_hash256, 172: op_checksig, 173: op_checksigverify, 174: op_checkmultisig, 175: op_checkmultisigverify, 176: op_nop, 177: op_checklocktimeverify, 178: op_checksequenceverify, 179: op_nop, 180: op_nop, 181: op_nop, 182: op_nop, 183: op_nop, 184: op_nop, 185: op_nop, } OP_CODE_NAMES = { 0: 'OP_0', 76: 'OP_PUSHDATA1', 77: 'OP_PUSHDATA2', 78: 'OP_PUSHDATA4', 79: 'OP_1NEGATE', 81: 'OP_1', 82: 'OP_2', 83: 'OP_3', 84: 'OP_4', 85: 'OP_5', 86: 'OP_6', 87: 'OP_7', 88: 'OP_8', 89: 'OP_9', 90: 'OP_10', 91: 'OP_11', 92: 'OP_12', 93: 'OP_13', 94: 'OP_14', 95: 'OP_15', 96: 'OP_16', 97: 'OP_NOP', 99: 'OP_IF', 100: 'OP_NOTIF', 103: 'OP_ELSE', 104: 'OP_ENDIF', 105: 'OP_VERIFY', 106: 'OP_RETURN', 107: 'OP_TOALTSTACK', 108: 'OP_FROMALTSTACK', 109: 'OP_2DROP', 110: 'OP_2DUP', 111: 'OP_3DUP', 112: 'OP_2OVER', 113: 'OP_2ROT', 114: 'OP_2SWAP', 115: 'OP_IFDUP', 116: 'OP_DEPTH', 117: 'OP_DROP', 118: 'OP_DUP', 119: 'OP_NIP', 120: 'OP_OVER', 121: 'OP_PICK', 122: 'OP_ROLL', 123: 'OP_ROT', 124: 'OP_SWAP', 125: 'OP_TUCK', 130: 'OP_SIZE', 135: 'OP_EQUAL', 136: 'OP_EQUALVERIFY', 139: 'OP_1ADD', 140: 'OP_1SUB', 143: 'OP_NEGATE', 144: 'OP_ABS', 145: 'OP_NOT', 146: 'OP_0NOTEQUAL', 147: 'OP_ADD', 148: 'OP_SUB', 154: 'OP_BOOLAND', 155: 'OP_BOOLOR', 156: 'OP_NUMEQUAL', 157: 'OP_NUMEQUALVERIFY', 158: 'OP_NUMNOTEQUAL', 159: 'OP_LESSTHAN', 160: 'OP_GREATERTHAN', 161: 'OP_LESSTHANOREQUAL', 162: 'OP_GREATERTHANOREQUAL', 163: 'OP_MIN', 164: 'OP_MAX', 165: 'OP_WITHIN', 166: 'OP_RIPEMD160', 167: 'OP_SHA1', 168: 'OP_SHA256', 169: 'OP_HASH160', 170: 'OP_HASH256', 171: 'OP_CODESEPARATOR', 172: 'OP_CHECKSIG', 173: 'OP_CHECKSIGVERIFY', 174: 'OP_CHECKMULTISIG', 175: 'OP_CHECKMULTISIGVERIFY', 176: 'OP_NOP1', 177: 'OP_CHECKLOCKTIMEVERIFY', 178: 'OP_CHECKSEQUENCEVERIFY', 179: 'OP_NOP4', 180: 'OP_NOP5', 181: 'OP_NOP6', 182: 'OP_NOP7', 183: 'OP_NOP8', 184: 'OP_NOP9', 185: 'OP_NOP10', }