def test_invalid_hash256(self): for script in [ [types.Five(), types.Hash256(), types.Push(b'\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01'), types.EqualVerify()], [types.Five(), types.Sha256(), types.Push(b'\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01'), types.EqualVerify()], ]: script = LInstructions(script) self.assertRaises(IRError, self._do_context, script)
def replace_shortcut_ops(instructions): """Replace opcodes with a corresponding shortcut form.""" optimizations = [] # Replace division by 2. # OP_2 OP_DIV -> OP_2DIV optimizations.append(([types.Two(), types.Div()], lambda values: [types.Div2()])) # Replace subtraction by 1. # OP_1 OP_SUB -> OP_1SUB optimizations.append(([types.One(), types.Sub()], lambda values: [types.Sub1()])) # Replace 1 * -1 with -1. # OP_1 OP_NEGATE -> OP_1NEGATE optimizations.append(([types.One(), types.Negate()], lambda values: [types.NegativeOne()])) # Replace addition by 1. # OP_1 OP_ADD -> OP_1ADD for permutation in permutations([types.Push(), types.One()]) + permutations([types.SmallIntOpCode(), types.One()]): idx = 0 if not isinstance(permutation[0], types.One) else 1 optimizations.append((permutation + [types.Add()], lambda values, idx=idx: [values[idx], types.Add1()])) optimizations.append(([types.Assumption(), types.One(), types.Add()], lambda values: [values[0], types.Add1()])) # Replace multiplication by 2. # OP_2 OP_MUL -> OP_2MUL for permutation in permutations([types.Push(), types.Two()]) + permutations([types.SmallIntOpCode(), types.Two()]): idx = 0 if not isinstance(permutation[0], types.Two) else 1 optimizations.append((permutation + [types.Mul()], lambda values, idx=idx: [values[idx], types.Mul2()])) optimizations.append(([types.Assumption(), types.Two(), types.Mul()], lambda values: [values[0], types.Mul2()])) for template, callback in optimizations: instructions.replace_template(template, callback, strict=False)
def test_valid_hash256(self): for script in [ [types.Five(), types.Hash256(), types.Push(b'\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01'), types.EqualVerify()], [types.Five(), types.Sha256(), types.Push(b'\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01'), types.EqualVerify()], ]: script = LInstructions(script) self._do_context(script)
def test_arithmetic_ops(self): script = [types.Five(), types.Five(), types.Equal(), types.Not()] self._do_test('OP_5 OP_5 OP_NUMNOTEQUAL', script) script = [types.Five(), types.Push(b'\x01\x02\x03\x04'), types.Equal(), types.Not()] self._do_test('OP_5 01020304 OP_NUMNOTEQUAL', script) # A value longer than 4 bytes won't be optimized this way. script = [types.Five(), types.Push(b'\x01\x02\x03\x04\x05'), types.Equal(), types.Not()] self._do_test('OP_5 0102030405 OP_EQUAL OP_NOT', script)
def test_multisig(self): sigs = [types.Push(formats.int_to_bytearray(i)) for i in [100]] pubs = [types.Push(formats.int_to_bytearray(i)) for i in [300, 400]] script = [types.Zero()] # Dummy value. script.extend(sigs + [types.One()]) # 1 signature. script.extend(pubs + [types.Two()]) # 2 public keys. script.append(types.CheckMultiSig()) script = LInstructions(script) self._do_context(script) checkmultisig = script[6] self.assertIsInstance(checkmultisig, types.CheckMultiSig) self.assertEqual(2, checkmultisig.num_pubkeys) self.assertEqual(1, checkmultisig.num_sigs)
def op_for_int(self, value): """Get a small int or push operation for value.""" cls = types.opcode_by_name('OP_%d' % value) if cls: return cls() value = formats.int_to_bytearray(value) return types.Push(data=value)
def state_append(self, op): """Append op to the stack state.""" if isinstance(op, StackItem): op = copy.deepcopy(op) elif isinstance(op, int): smallint = types.small_int_opcode(op) if smallint: op = smallint() else: op = types.Push(data=op) op = StackItem(op) elif isinstance(op, str): op = StackItem(types.Push(data=op)) self.state.append(op)
def use_small_int_opcodes(instructions): """Convert data pushes to equivalent small integer opcodes.""" def convert_push(push): push = push[0] try: i = formats.bytearray_to_int(push.data) return [types.small_int_opcode(i)()] except TypeError: pass return [push] instructions.replace_template([types.Push()], convert_push, strict=False)
def process_value(self, value): # Encode integer. if isinstance(value, (int, long)): push = formats.int_to_bytearray(value) self.add_instruction(types.Push(data=push)) else: try: opcode = types.small_int_opcode(int(value))() except (TypeError, ValueError): opcode = types.opcode_by_name('OP_%s' % value)() self.add_instruction(opcode)
def visit_InnerScript(self, node): s = [] for op in node.ops: result = self.visit(op) if isinstance(result, list): result = ''.join(result).replace('0x', '') s.append(result) s = ''.join(s) data = x(s) if self.little_endian: data = lx(s) return self.visit(types.Push(data=data))
def use_arithmetic_ops(instructions): """Replace ops with more convenient arithmetic ops.""" optimizations = [] _two_values = permutations([types.SmallIntOpCode(), types.Push()]) _two_values.append([types.SmallIntOpCode()] * 2) _two_values.append([types.Push()] * 2) def all_strict_nums(numbers): """Evaluate whether instances represent strict numbers.""" return all(formats.is_strict_num(i) for i in map(LInstructions.instruction_to_int, numbers)) # Use NUMNOTEQUAL if both values are numbers. def numnotequal_callback(values): if not all_strict_nums(values[0:2]): return values return values[0:2] + [types.NumNotEqual()] for permutation in _two_values: optimizations.append((permutation + [types.Equal(), types.Not()], numnotequal_callback)) for template, callback in optimizations: instructions.replace_template(template, callback, strict=False)
def transform(self, source): if isinstance(source, list): source = ''.join(source) if source.startswith('0x'): source = source[2:] src = script.CScript(x(source)) iterator = iter(src) for value in iterator: op = None s = str(value) if s.startswith('OP_'): op = types.opcode_by_name(s)() elif isinstance(value, int): op = types.small_int_opcode(value)() else: op = types.Push(data=value) if op is not None: self.add_instruction(op) return self.instructions
def visit_Bytes(self, node): try: return types.small_int_opcode(int(node.data, 16))() except (TypeError, ValueError): return types.Push(formats.hex_to_bytearray(node.data))
def visit_Int(self, node): smallint = types.small_int_opcode(int(node)) if smallint: return smallint() else: return types.Push(formats.int_to_bytearray(node.value))
def test_push(self): script = LInstructions([types.One(), types.Push(formats.int_to_bytearray(20)), types.IfDup()]) self._do_context(script) ifdup = script[2] self.assertIsInstance(ifdup, types.IfDup) self.assertEqual(1, ifdup.delta)
def test_convert_to_small_int(self): script = [types.Push(b'\x05')] self._do_test('OP_5', script)