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_shortcut_ops(self): for script in [ [types.Five(), types.One(), types.Add()], [types.One(), types.Five(), types.Add()], ]: self._do_test('OP_5 OP_1ADD', script) self._do_test('OP_1ADD', [types.Assumption('testItem'), types.One(), types.Add()]) script = [types.Five(), types.One(), types.Sub()] self._do_test('OP_5 OP_1SUB', script) for script in [ [types.Five(), types.Two(), types.Mul()], [types.Two(), types.Five(), types.Mul()], ]: self._do_test('OP_5 OP_2MUL', script) self._do_test('OP_2MUL', [types.Assumption('testItem'), types.Two(), types.Mul()]) script = [types.Five(), types.Two(), types.Div()] self._do_test('OP_5 OP_2DIV', script) script = [types.Five(), types.One(), types.Negate()] self._do_test('OP_5 OP_1NEGATE', script)
def test_small_int(self): items = [ (LInstructions([types.One(), types.Two(), types.IfDup()]), 1), (LInstructions([types.One(), types.Zero(), types.IfDup()]), 0), ] for script, expected_delta in items: self._do_context(script) ifdup = script[2] self.assertIsInstance(ifdup, types.IfDup) self.assertEqual(expected_delta, ifdup.delta)
def test_optimize_stack_ops(self): script = [types.Five(), types.One(), types.Pick()] self._do_test('OP_5 OP_OVER', script) script = [types.Five(), types.One(), types.Roll(), types.Drop()] self._do_test('OP_5 OP_NIP', script) script = [types.Zero(), types.Pick()] self._do_test('OP_DUP', script) script = [types.Five(), types.Zero(), types.Roll()] self._do_test('OP_5', script) script = [types.Five(), types.Six(), types.One(), types.Roll(), types.One(), types.Roll()] self._do_test('OP_5 OP_6', script)
def optimize_stack_ops(instructions): """Optimize stack operations.""" for template, replacement in [ # OP_1 OP_PICK -> OP_OVER ([types.One(), types.Pick()], [types.Over()]), # OP_1 OP_ROLL OP_DROP -> OP_NIP ([types.One(), types.Roll(), types.Drop()], [types.Nip()]), # OP_0 OP_PICK -> OP_DUP ([types.Zero(), types.Pick()], [types.Dup()]), # OP_0 OP_ROLL -> _ ([types.Zero(), types.Roll()], []), # OP_1 OP_ROLL OP_1 OP_ROLL -> _ ([types.One(), types.Roll(), types.One(), types.Roll()], []), # OP_1 OP_ROLL -> OP_SWAP ([types.One(), types.Roll()], [types.Swap()]), # OP_NIP OP_DROP -> OP_2DROP ([types.Nip(), types.Drop()], [types.TwoDrop()]), # OP_OVER OP_OVER -> OP_2DUP ([types.Over(), types.Over()], [types.TwoDup()]), ]: callback = lambda values, replacement=replacement: replacement instructions.replace_template(template, callback)
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 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)