def test_step_script(self): # dict of {script: [stackAtStep0, stackAtStep1, ...], ...} step_tests = { self.script_simple_addition: [["\x02"], ["\x02", "\x03"], ["\x05"]], self.script_push_dup: [["\x80"], ["\x80", "\x80"]], } my_stack = Stack() for my_script, expected_states in step_tests.items(): my_stack.set_script(my_script) iterator = my_stack.step() for i in expected_states: stack_state, _ = iterator.next() self.assertEqual(i, stack_state)
def test_step_script(self): # dict of {script: [stackAtStep0, stackAtStep1, ...], ...} step_tests = { self.script_simple_addition: [['\x02'], ['\x02', '\x03'], ['\x05']], self.script_push_dup: [['\x80'], ['\x80', '\x80']] } my_stack = Stack() for my_script, expected_states in step_tests.items(): my_stack.set_script(my_script) iterator = my_stack.step() for i in expected_states: stack_state, _ = iterator.next() self.assertEqual(i, stack_state)
def test_evaluate_script(self): my_stack = Stack() my_stack.set_script(self.script_simple_addition) stack = my_stack.evaluate() self.assertEqual(1, len(stack)) self.assertEqual('\x05', stack[0]) my_stack.set_script(self.script_push_dup) stack = my_stack.evaluate() self.assertEqual(2, len(stack)) self.assertEqual('\x80', stack[0]) self.assertEqual('\x80', stack[1])
def test_evaluate_script(self): my_stack = Stack() my_stack.set_script(self.script_simple_addition) stack = my_stack.evaluate() self.assertEqual(1, len(stack)) self.assertEqual("\x05", stack[0]) my_stack.set_script(self.script_push_dup) stack = my_stack.evaluate() self.assertEqual(2, len(stack)) self.assertEqual("\x80", stack[0]) self.assertEqual("\x80", stack[1])
def init_data(self): self.stack = Stack() self.step_counter = -1 self.tx = None self.inIdx = 0
class StackEval(BaseDock): def __init__(self, handler): super(StackEval, self).__init__(handler) self.widget().setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.has_spending_tx.setChecked(False) def init_metadata(self): self.tool_name = 'Stack Evaluator' self.description = '\n'.join([ 'Stack Evaluator steps through scripts, showing you what\'s happening as it happens.', '<b>Please read this warning from the source of python-bitcoinlib, which Stack Evaluator uses to evaluate scripts:</b>', '"Be warned that there are highly likely to be consensus bugs in this code; it is unlikely to match Satoshi Bitcoin exactly. Think carefully before using this module."' ]) def init_data(self): self.stack = Stack() self.step_counter = -1 self.tx = None self.inIdx = 0 def reset_step_counter(self): self.step_counter = -1 def reset(self): self.reset_step_counter() self.stack_view.clear() self.stack_log.clear() self.stack_result.clear() self.stack_result.setProperty('hasError', False) self.style().polish(self.stack_result) # Clear selected step. cursor = QTextCursor(self.tx_script.document()) cursor.setPosition(0) self.tx_script.setTextCursor(cursor) def create_layout(self): form = QFormLayout() form.setRowWrapPolicy(QFormLayout.WrapAllRows) # Whether there is a spending transaction. self.has_spending_tx = QCheckBox('Specify transaction being spent.') self.has_spending_tx.stateChanged.connect(self.set_show_tx) # Spending transaction self.tx_edit = QPlainTextEdit() self.tx_edit.setFont(monospace_font) self.tx_edit.textChanged.connect(self.set_tx) # Input with scriptSig to include self.input_idx = QSpinBox() self.input_idx.setEnabled(False) self.input_idx.valueChanged.connect(self.set_input_index) self.input_idx.setToolTip('Input in the spending transaction with the relevant scriptSig.') in_idx_box = QHBoxLayout() in_idx_box.addWidget(QLabel('Input containing scriptSig:')) in_idx_box.addWidget(self.input_idx) in_idx_box.addStretch(1) self.tx_frame = QFrame() tx_layout = QVBoxLayout() tx_layout.addWidget(self.tx_edit) tx_layout.addLayout(in_idx_box) self.tx_frame.setLayout(tx_layout) self.tx_frame.setVisible(False) # Raw script input. self.tx_script = QPlainTextEdit() self.tx_script.textChanged.connect(self.reset_step_counter) self.tx_script.setFont(monospace_font) # Result of the latest script op. self.stack_result = QLineEdit() self.stack_result.setReadOnly(True) # Visualization of stack. self.stack_view = QListWidget() # Log of script ops. self.stack_log = QTreeWidget() self.stack_log.setColumnCount(3) self.stack_log.setHeaderLabels(['Step', 'Op', 'Result']) self.stack_log.header().setDefaultSectionSize(50) self.stack_log.header().setResizeMode(0, QHeaderView.Fixed) self.stack_log.header().setResizeMode(1, QHeaderView.ResizeToContents) self.stack_log.header().setResizeMode(2, QHeaderView.Stretch) self.stack_log.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) # Controls self.step_button = QPushButton('Step') self.step_button.setToolTip('Evaluate the next operation') self.step_button.clicked.connect(self.do_step) self.reset_button = QPushButton('Reset') self.reset_button.setToolTip('Reset the current evaluation') self.reset_button.clicked.connect(self.reset) self.do_button = QPushButton('Evaluate') self.do_button.setToolTip('Evaluate the entire script') self.do_button.clicked.connect(self.do_evaluate) form.addRow(self.has_spending_tx) form.addRow('Spending Transaction:', self.tx_frame) form.addRow('Script:', self.tx_script) form.addRow('Stack:', self.stack_view) form.addRow('Stack log:', self.stack_log) form.addRow(self.stack_result) btn_hbox = floated_buttons([self.step_button, self.reset_button, self.do_button], left=True) form.addRow(btn_hbox) return form def set_show_tx(self, do_show): do_show = True if do_show else False if do_show: self.tx_frame.setVisible(True) else: self.tx_frame.setVisible(False) def set_spending_tx(self, txt): """Called from other tools to set the spending transaction.""" if not txt: return self.has_spending_tx.setChecked(True) self.tx_edit.setPlainText(txt) def set_tx(self): """Set the spending transaction and (en|dis)able the input index box.""" txt = str(self.tx_edit.toPlainText()) if not txt: self.tx = None self.input_idx.setEnabled(False) self.tx_edit.setToolTip('') return self.tx = CTransaction.deserialize(txt.decode('hex')) self.tx_edit.setToolTip(''.join(['Tx ID: ', bitcoin.core.b2lx(self.tx.GetHash())])) self.input_idx.setRange(0, len(self.tx.vin) - 1) self.input_idx.setEnabled(True) def set_input_index(self, idx): self.inIdx = idx def highlight_step(self, op): """Highlights the relevant text in the input widget.""" opcode, data, byte_index = op if data is None: data = '' pos = byte_index * 2 length = 2 + 2 * len(data) cursor = QTextCursor(self.tx_script.document()) cursor.setPosition(pos) cursor.setPosition(pos + length, QTextCursor.KeepAnchor) self.tx_script.setTextCursor(cursor) def do_evaluate(self): while 1: if not self.do_step(): break cursor = QTextCursor(self.tx_script.document()) cursor.setPosition(0) self.tx_script.setTextCursor(cursor) def do_step(self): """Returns whether another step can be done.""" if self.step_counter == -1: txt = str(self.tx_script.toPlainText()) scr = CScript(txt.decode('hex')) # So we can show the opcode in the stack log self.script_ops = [i for i in scr.raw_iter()] self.stack.set_script(scr, self.tx, self.inIdx) self.stack_iterator = self.stack.step() self.stack_log.clear() self.step_counter += 1 step_again = False try: self.step_counter += 1 stack_state, action = self.stack_iterator.next() new_stack = [i.encode('hex') for i in reversed(stack_state)] self.stack_view.clear() self.stack_view.addItems(new_stack) op_name = OPCODE_NAMES.get(self.script_ops[self.step_counter - 1][0], 'PUSHDATA') self.highlight_step(self.script_ops[self.step_counter - 1]) item = QTreeWidgetItem(map(lambda i: str(i), [self.step_counter, op_name, action])) item.setTextAlignment(0, Qt.AlignLeft) item.setToolTip(1, 'Step {} operation'.format(self.step_counter)) item.setToolTip(2, 'Result of step {}'.format(self.step_counter)) self.stack_log.insertTopLevelItem(0, item) self.stack_result.setText(action) self.stack_result.setProperty('hasError', False) step_again = True except StopIteration: self.stack_result.setText('End of script.') except Exception as e: self.stack_result.setText(str(e)) self.stack_result.setProperty('hasError', True) finally: self.style().polish(self.stack_result) return step_again
def init_data(self): self.stack = Stack() self.step_counter = -1
class StackEval(BaseDock): def __init__(self, handler): super(StackEval, self).__init__(handler) self.widget().setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) def init_metadata(self): self.tool_name = 'Stack View' self.description = '\n'.join([ 'Stack View steps through scripts, showing you what\'s happening as it happens.', '<b>Please read this warning from the source of python-bitcoinlib, which Stack View uses to evaluate scripts:</b>', '"Be warned that there are highly likely to be consensus bugs in this code; it is unlikely to match Satoshi Bitcoin exactly. Think carefully before using this module."' ]) def init_data(self): self.stack = Stack() self.step_counter = -1 def reset_step_counter(self): self.step_counter = -1 def create_layout(self): vbox = QVBoxLayout() self.tx_script = QPlainTextEdit() self.tx_script.textChanged.connect(self.reset_step_counter) self.tx_script.setFont(monospace_font) self.stack_result = QLineEdit() self.stack_result.setReadOnly(True) self.stack_view = QListWidget() self.stack_log = QTreeWidget() self.stack_log.setColumnCount(3) self.stack_log.setHeaderLabels(['Step', 'Op', 'Result']) self.stack_log.header().setDefaultSectionSize(50) self.stack_log.header().setResizeMode(0, QHeaderView.Fixed) self.stack_log.header().setResizeMode(1, QHeaderView.ResizeToContents) self.stack_log.header().setResizeMode(2, QHeaderView.Stretch) self.stack_log.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.MinimumExpanding) def reset(): self.reset_step_counter() self.stack_view.clear() self.stack_log.clear() self.stack_result.clear() self.stack_result.setProperty('hasError', False) self.style().polish(self.stack_result) self.step_button = QPushButton('Step') self.step_button.setToolTip('Evaluate the next operation') self.step_button.clicked.connect(self.do_step) self.reset_button = QPushButton('Reset') self.reset_button.setToolTip('Reset the current evaluation') self.reset_button.clicked.connect(reset) self.do_button = QPushButton('Evaluate') self.do_button.setToolTip('Evaluate the entire script') self.do_button.clicked.connect(self.do_evaluate) vbox.addWidget(QLabel('Script:')) vbox.addWidget(self.tx_script) vbox.addWidget(QLabel('Stack:')) vbox.addWidget(self.stack_view) vbox.addWidget(QLabel('Stack log:')) vbox.addWidget(self.stack_log, stretch=1) vbox.addWidget(self.stack_result) btn_hbox = QHBoxLayout() btn_hbox.addWidget(self.step_button) btn_hbox.addWidget(self.reset_button) btn_hbox.addWidget(self.do_button) btn_hbox.addStretch(1) vbox.addLayout(btn_hbox) return vbox def do_evaluate(self): while 1: if not self.do_step(): break def do_step(self): """Returns whether another step can be done.""" if self.step_counter == -1: txt = str(self.tx_script.toPlainText()) scr = CScript(txt.decode('hex')) # So we can show the opcode in the stack log self.script_ops = [i[0] for i in scr.raw_iter()] self.stack.set_script(scr) self.stack_iterator = self.stack.step() self.stack_log.clear() self.step_counter += 1 step_again = False try: self.step_counter += 1 stack_state, action = self.stack_iterator.next() new_stack = [i.encode('hex') for i in reversed(stack_state)] self.stack_view.clear() self.stack_view.addItems(new_stack) op_name = OPCODE_NAMES.get(self.script_ops[self.step_counter - 1], 'PUSHDATA') item = QTreeWidgetItem(map(lambda i: str(i), [self.step_counter, op_name, action])) item.setTextAlignment(0, Qt.AlignLeft) item.setToolTip(1, 'Step {} operation'.format(self.step_counter)) item.setToolTip(2, 'Result of step {}'.format(self.step_counter)) self.stack_log.insertTopLevelItem(0, item) self.stack_result.setText(action) self.stack_result.setProperty('hasError', False) step_again = True except StopIteration: self.stack_result.setText('End of script.') except Exception as e: self.stack_result.setText(str(e)) self.stack_result.setProperty('hasError', True) finally: self.style().polish(self.stack_result) return step_again
class StackEval(BaseDock): tool_name = 'Stack Evaluator' description = '\n'.join([ 'Stack Evaluator steps through scripts, showing you what\'s happening as it happens.', '<b>Please read this warning from the source of python-bitcoinlib, which Stack Evaluator uses to evaluate scripts:</b>', '"Be warned that there are highly likely to be consensus bugs in this code; it is unlikely to match Satoshi Bitcoin exactly. Think carefully before using this module."' ]) is_large = True category = Category.Script def __init__(self, handler): super(StackEval, self).__init__(handler) self.widget().setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) def init_data(self): self.stack = Stack() self.step_counter = -1 self.tx = None self.inIdx = 0 def init_actions(self): set_as_spending = ('Set as spending transaction', self.set_spending_tx) self.advertised_actions['raw_transaction'] = [set_as_spending] def reset_step_counter(self): self.step_counter = -1 def reset(self): self.reset_step_counter() self.stack_view.clear() self.stack_log.clear() self.stack_result.clear() self.stack_result.setProperty('hasError', False) self.style().polish(self.stack_result) # Clear selected step. cursor = QTextCursor(self.tx_script.document()) cursor.setPosition(0) self.tx_script.setTextCursor(cursor) def create_layout(self): form = QFormLayout() tabs = QTabWidget() tabs.addTab(self.create_main_tab(), 'Stack') tabs.addTab(self.create_tx_tab(), 'Transaction') form.addRow(tabs) return form def create_main_tab(self): form = QFormLayout() form.setRowWrapPolicy(QFormLayout.WrapAllRows) # Raw script input. self.tx_script = QPlainTextEdit() self.tx_script.textChanged.connect(self.reset_step_counter) self.tx_script.setFont(monospace_font) # Result of the latest script op. self.stack_result = QLineEdit() self.stack_result.setReadOnly(True) # Visualization of stack. self.stack_view = QListWidget() # Log of script ops. self.stack_log = QTreeWidget() self.stack_log.setColumnCount(3) self.stack_log.setHeaderLabels(['Step', 'Op', 'Result']) self.stack_log.header().setDefaultSectionSize(50) self.stack_log.header().setResizeMode(0, QHeaderView.Fixed) self.stack_log.header().setResizeMode(1, QHeaderView.ResizeToContents) self.stack_log.header().setResizeMode(2, QHeaderView.Stretch) self.stack_log.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) # Controls self.step_button = QPushButton('Step') self.step_button.setToolTip('Evaluate the next operation') self.step_button.clicked.connect(self.do_step) self.reset_button = QPushButton('Reset') self.reset_button.setToolTip('Reset the current evaluation') self.reset_button.clicked.connect(self.reset) self.do_button = QPushButton('Evaluate') self.do_button.setToolTip('Evaluate the entire script') self.do_button.clicked.connect(self.do_evaluate) form.addRow('Script:', self.tx_script) form.addRow('Stack:', self.stack_view) form.addRow('Stack log:', self.stack_log) form.addRow(self.stack_result) btn_hbox = floated_buttons( [self.step_button, self.reset_button, self.do_button], left=True) form.addRow(btn_hbox) w = QWidget() w.setLayout(form) return w def create_tx_tab(self): form = QFormLayout() # Spending transaction self.tx_edit = QPlainTextEdit() self.tx_edit.setFont(monospace_font) self.tx_edit.textChanged.connect(self.set_tx) # Input with scriptSig to include self.input_idx = QSpinBox() self.input_idx.setEnabled(False) self.input_idx.valueChanged.connect(self.set_input_index) self.input_idx.setToolTip( 'Input in the containing transaction with the relevant scriptSig.') in_idx_box = QHBoxLayout() in_idx_box.addWidget(QLabel('Input containing scriptSig:')) in_idx_box.addWidget(self.input_idx) in_idx_box.addStretch(1) desc = QLabel(' '.join([ 'You can specify the transaction that contains the script you\'re testing.', 'This allows you to evaluate whether an input is spent successfully.' ])) desc.setWordWrap(True) form.addRow(desc) form.addRow('Raw Transaction:', self.tx_edit) form.addRow('Input to spend:', self.input_idx) w = QWidget() w.setLayout(form) return w def set_spending_tx(self, txt): """Called from other tools to set the spending transaction.""" if not txt: return self.needsFocus.emit() self.tx_edit.setPlainText(txt) def set_tx(self): """Set the spending transaction and (en|dis)able the input index box.""" txt = str(self.tx_edit.toPlainText()) try: assert txt self.tx = Transaction.deserialize(txt.decode('hex')) self.tx_edit.setToolTip(''.join( ['Tx ID: ', bitcoin.core.b2lx(self.tx.GetHash())])) self.input_idx.setRange(0, len(self.tx.vin) - 1) self.input_idx.setEnabled(True) except Exception: self.tx = None self.tx_edit.setToolTip('') self.input_idx.setEnabled(False) def set_input_index(self, idx): self.inIdx = idx def highlight_step(self, op): """Highlights the relevant text in the input widget.""" opcode, data, byte_index = op if data is None: data = '' pos = byte_index * 2 length = 2 + 2 * len(data) cursor = QTextCursor(self.tx_script.document()) cursor.setPosition(pos) cursor.setPosition(pos + length, QTextCursor.KeepAnchor) self.tx_script.setTextCursor(cursor) def do_evaluate(self): while 1: if not self.do_step(): break cursor = QTextCursor(self.tx_script.document()) cursor.setPosition(0) self.tx_script.setTextCursor(cursor) def do_step(self): """Returns whether another step can be done.""" if self.step_counter == -1: txt = str(self.tx_script.toPlainText()) scr = CScript(txt.decode('hex')) # So we can show the opcode in the stack log self.script_ops = [i for i in scr.raw_iter()] self.stack.set_script(scr, self.tx, self.inIdx) self.stack_iterator = self.stack.step() self.stack_log.clear() self.step_counter += 1 step_again = False try: self.step_counter += 1 stack_state, action = self.stack_iterator.next() new_stack = [i.encode('hex') for i in reversed(stack_state)] self.stack_view.clear() self.stack_view.addItems(new_stack) op_name = OPCODE_NAMES.get( self.script_ops[self.step_counter - 1][0], 'PUSHDATA') self.highlight_step(self.script_ops[self.step_counter - 1]) item = QTreeWidgetItem( map(lambda i: str(i), [self.step_counter, op_name, action])) item.setTextAlignment(0, Qt.AlignLeft) item.setToolTip(1, 'Step {} operation'.format(self.step_counter)) item.setToolTip(2, 'Result of step {}'.format(self.step_counter)) self.stack_log.insertTopLevelItem(0, item) self.stack_result.setText(action) self.stack_result.setProperty('hasError', False) step_again = True except StopIteration: self.stack_result.setText('End of script.') except Exception as e: self.stack_result.setText(str(e)) self.stack_result.setProperty('hasError', True) finally: self.style().polish(self.stack_result) return step_again
class StackEval(BaseDock): tool_name = "Stack Evaluator" description = "\n".join( [ "Stack Evaluator steps through scripts, showing you what's happening as it happens.", "<b>Please read this warning from the source of python-bitcoinlib, which Stack Evaluator uses to evaluate scripts:</b>", '"Be warned that there are highly likely to be consensus bugs in this code; it is unlikely to match Satoshi Bitcoin exactly. Think carefully before using this module."', ] ) is_large = True category = Category.Script def __init__(self, handler): super(StackEval, self).__init__(handler) self.widget().setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) def init_data(self): self.stack = Stack() self.step_counter = -1 self.tx = None self.inIdx = 0 def init_actions(self): set_as_spending = ("Set as spending transaction", self.set_spending_tx) self.advertised_actions[RAW_TX] = [set_as_spending] def reset_step_counter(self): self.step_counter = -1 def reset(self): self.reset_step_counter() self.stack_view.clear() self.stack_log.clear() self.stack_result.clear() self.stack_result.setProperty("hasError", False) self.style().polish(self.stack_result) # Clear selected step. cursor = QTextCursor(self.tx_script.document()) cursor.setPosition(0) self.tx_script.setTextCursor(cursor) def create_layout(self): form = QFormLayout() tabs = QTabWidget() tabs.addTab(self.create_main_tab(), "Stack") tabs.addTab(self.create_tx_tab(), "Transaction") self.setFocusProxy(tabs) form.addRow(tabs) return form def create_main_tab(self): form = QFormLayout() form.setRowWrapPolicy(QFormLayout.WrapAllRows) # Raw script input. self.tx_script = QPlainTextEdit() self.tx_script.textChanged.connect(self.reset_step_counter) self.tx_script.setFont(monospace_font) # Result of the latest script op. self.stack_result = QLineEdit() self.stack_result.setReadOnly(True) # Visualization of stack. self.stack_view = QListWidget() # Log of script ops. self.stack_log = QTreeWidget() self.stack_log.setColumnCount(3) self.stack_log.setHeaderLabels(["Step", "Op", "Result"]) self.stack_log.header().setDefaultSectionSize(50) self.stack_log.header().setResizeMode(0, QHeaderView.Fixed) self.stack_log.header().setResizeMode(1, QHeaderView.ResizeToContents) self.stack_log.header().setResizeMode(2, QHeaderView.Stretch) self.stack_log.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) # Controls self.step_button = QPushButton("Step") self.step_button.setToolTip("Evaluate the next operation") self.step_button.clicked.connect(self.do_step) self.reset_button = QPushButton("Reset") self.reset_button.setToolTip("Reset the current evaluation") self.reset_button.clicked.connect(self.reset) self.do_button = QPushButton("Evaluate") self.do_button.setToolTip("Evaluate the entire script") self.do_button.clicked.connect(self.do_evaluate) form.addRow("Script:", self.tx_script) form.addRow("Stack:", self.stack_view) form.addRow("Stack log:", self.stack_log) form.addRow(self.stack_result) btn_hbox = floated_buttons([self.step_button, self.reset_button, self.do_button], left=True) form.addRow(btn_hbox) w = QWidget() w.setLayout(form) return w def create_tx_tab(self): form = QFormLayout() # Spending transaction self.tx_edit = QPlainTextEdit() self.tx_edit.setFont(monospace_font) self.tx_edit.textChanged.connect(self.set_tx) # Input with scriptSig to include self.input_idx = QSpinBox() self.input_idx.setEnabled(False) self.input_idx.valueChanged.connect(self.set_input_index) self.input_idx.setToolTip("Input in the containing transaction with the relevant scriptSig.") in_idx_box = QHBoxLayout() in_idx_box.addWidget(QLabel("Input containing scriptSig:")) in_idx_box.addWidget(self.input_idx) in_idx_box.addStretch(1) desc = QLabel( " ".join( [ "You can specify the transaction that contains the script you're testing.", "This allows you to evaluate whether an input is spent successfully.", ] ) ) desc.setWordWrap(True) form.addRow(desc) form.addRow("Raw Transaction:", self.tx_edit) form.addRow("Input to spend:", self.input_idx) w = QWidget() w.setLayout(form) return w def set_spending_tx(self, txt): """Called from other tools to set the spending transaction.""" if not txt: return self.needsFocus.emit() self.tx_edit.setPlainText(txt) def set_tx(self): """Set the spending transaction and (en|dis)able the input index box.""" txt = str(self.tx_edit.toPlainText()) try: assert txt self.tx = Transaction.deserialize(txt.decode("hex")) self.tx_edit.setToolTip("".join(["Tx ID: ", bitcoin.core.b2lx(self.tx.GetHash())])) self.input_idx.setRange(0, len(self.tx.vin) - 1) self.input_idx.setEnabled(True) except Exception: self.tx = None self.tx_edit.setToolTip("") self.input_idx.setEnabled(False) def set_input_index(self, idx): self.inIdx = idx def highlight_step(self, op): """Highlights the relevant text in the input widget.""" opcode, data, byte_index = op if data is None: data = "" pos = byte_index * 2 length = 2 + 2 * len(data) cursor = QTextCursor(self.tx_script.document()) cursor.setPosition(pos) cursor.setPosition(pos + length, QTextCursor.KeepAnchor) self.tx_script.setTextCursor(cursor) def do_evaluate(self): while 1: if not self.do_step(): break cursor = QTextCursor(self.tx_script.document()) cursor.setPosition(0) self.tx_script.setTextCursor(cursor) def do_step(self): """Returns whether another step can be done.""" if self.step_counter == -1: txt = str(self.tx_script.toPlainText()) scr = CScript(txt.decode("hex")) # So we can show the opcode in the stack log self.script_ops = [i for i in scr.raw_iter()] self.stack.set_script(scr, self.tx, self.inIdx) self.stack_iterator = self.stack.step() self.stack_log.clear() self.step_counter += 1 step_again = False try: self.step_counter += 1 stack_state, action = self.stack_iterator.next() new_stack = [i.encode("hex") for i in reversed(stack_state)] self.stack_view.clear() self.stack_view.addItems(new_stack) op_name = OPCODE_NAMES.get(self.script_ops[self.step_counter - 1][0], "PUSHDATA") self.highlight_step(self.script_ops[self.step_counter - 1]) item = QTreeWidgetItem(map(lambda i: str(i), [self.step_counter, op_name, action])) item.setTextAlignment(0, Qt.AlignLeft) item.setToolTip(1, "Step {} operation".format(self.step_counter)) item.setToolTip(2, "Result of step {}".format(self.step_counter)) self.stack_log.insertTopLevelItem(0, item) self.stack_result.setText(action) self.stack_result.setProperty("hasError", False) step_again = True except StopIteration: self.stack_result.setText("End of script.") except Exception as e: self.stack_result.setText(str(e)) self.stack_result.setProperty("hasError", True) finally: self.style().polish(self.stack_result) return step_again