示例#1
0
    def create_deserialize_tab(self):
        form = QFormLayout()

        self.deserialize_button = QPushButton('Deserialize')
        self.deserialize_button.clicked.connect(self.deserialize)
        btn_hbox = floated_buttons([self.deserialize_button])

        self.tx_widget = TxWidget()
        self.tx_widget.inputs_tree.view.customContextMenuRequested.disconnect(
            self.tx_widget.inputs_tree.customContextMenu)
        self.tx_widget.inputs_tree.view.customContextMenuRequested.connect(
            self.inputs_context_menu)

        form.addRow(self.tx_widget)

        w = QWidget()
        w.setLayout(form)
        return w
示例#2
0
    def create_review_tab(self):
        form = QFormLayout()

        self.raw_tx = QTextEdit()
        self.raw_tx.setReadOnly(True)

        self.tx_widget = TxWidget()

        build_button = QPushButton('Build transaction')
        build_button.setToolTip('Build a tx from the data in the previous tabs')
        build_button.clicked.connect(self.build_transaction)

        form.addRow('Raw Tx:', self.raw_tx)
        form.addRow(self.tx_widget)
        form.addRow(floated_buttons([build_button]))

        w = QWidget()
        w.setLayout(form)
        return w
示例#3
0
    def create_deserialize_tab(self):
        form = QFormLayout()

        self.deserialize_button = QPushButton('Deserialize')
        self.deserialize_button.clicked.connect(self.deserialize)
        btn_hbox = floated_buttons([self.deserialize_button])

        self.tx_widget = TxWidget()
        self.tx_widget.inputs_tree.view.customContextMenuRequested.disconnect(self.tx_widget.inputs_tree.customContextMenu)
        self.tx_widget.inputs_tree.view.customContextMenuRequested.connect(self.inputs_context_menu)

        form.addRow(self.tx_widget)

        w = QWidget()
        w.setLayout(form)
        return w
示例#4
0
    def create_layout(self):
        form = QFormLayout()
        form.setRowWrapPolicy(QFormLayout.WrapLongRows)


        self.raw_tx_edit = QPlainTextEdit()
        self.raw_tx_edit.setFont(monospace_font)

        self.deserialize_button = QPushButton('Deserialize')
        self.deserialize_button.clicked.connect(self.deserialize)
        btn_hbox = floated_buttons([self.deserialize_button])

        self.tx_widget = TxWidget()

        form.addRow('Raw Tx:', self.raw_tx_edit)
        form.addRow(btn_hbox)
        form.addRow(Separator())
        form.addRow(self.tx_widget)

        return form
示例#5
0
class TxDeserializer(BaseDock):
    def __init__(self, handler):
        super(TxDeserializer, self).__init__(handler)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.context_menu)

    def init_metadata(self):
        self.tool_name = 'Transaction Deserializer'
        self.description = 'Deserializes transactions.'

    def init_data(self):
        self.tx = None

    def create_layout(self):
        form = QFormLayout()
        form.setRowWrapPolicy(QFormLayout.WrapLongRows)


        self.raw_tx_edit = QPlainTextEdit()
        self.raw_tx_edit.setFont(monospace_font)

        self.deserialize_button = QPushButton('Deserialize')
        self.deserialize_button.clicked.connect(self.deserialize)
        btn_hbox = floated_buttons([self.deserialize_button])

        self.tx_widget = TxWidget()

        form.addRow('Raw Tx:', self.raw_tx_edit)
        form.addRow(btn_hbox)
        form.addRow(Separator())
        form.addRow(self.tx_widget)

        return form

    def context_menu(self, position):
        menu = QMenu()
        menu.addAction('Clear Fields', self.clear)
        set_spend = menu.addAction('Set as spending transaction in Stack Evaluator', self.set_as_spending_tx)
        set_spend.setEnabled(True if self.tx else False)

        menu.exec_(self.mapToGlobal(position))

    def set_as_spending_tx(self):
        txt = str(self.raw_tx_edit.toPlainText())
        self.handler.set_stack_spending_tx(txt)

    def clear(self):
        self.tx_widget.clear()

    def deserialize(self):
        self.clear()
        txt = str(self.raw_tx_edit.toPlainText())
        try:
            txt = txt.decode('hex')
        except Exception:
            self.status_message('Raw transaction must be hex.', True)
            return
        try:
            self.tx = tx = CTransaction.deserialize(txt)
        except Exception:
            self.status_message('Cannot deserialize transaction.', True)
            return

        self.tx_widget.set_tx(tx)

        self.status_message('Deserialized transaction {}'.format(bitcoin.core.b2lx(tx.GetHash())))

    def on_option_changed(self, key):
        if key == 'amount_format':
            self.needsUpdate.emit()

    def refresh_data(self):
        self.deserialize()
示例#6
0
class TxAnalyzer(BaseDock):

    tool_name = 'Transaction Analyzer'
    description = 'Deserializes transactions and verifies their inputs.'
    is_large = True
    category = Category.Tx

    def __init__(self, handler):
        super(TxAnalyzer, self).__init__(handler)
        self.raw_tx_edit.textChanged.emit()

    def init_data(self):
        self.tx = None

    def init_actions(self):
        deserialize = ('Deserialize', self.deserialize_raw)
        verify = ('Verify inputs', self.do_verify_inputs)
        self.advertised_actions['raw_transaction'] = [deserialize, verify]

    def create_layout(self):
        form = QFormLayout()

        self.raw_tx_edit = QPlainTextEdit()
        self.raw_tx_edit.setFont(monospace_font)
        self.raw_tx_edit.setContextMenuPolicy(Qt.CustomContextMenu)
        self.raw_tx_edit.customContextMenuRequested.connect(self.context_menu)
        self.raw_tx_edit.textChanged.connect(self.check_raw_tx)

        self.raw_tx_invalid = QLabel('Cannot parse transaction.')
        self.raw_tx_invalid.setProperty('hasError', True)

        self.tabs = tabs = QTabWidget()
        tabs.addTab(self.create_deserialize_tab(), 'Deserialize')
        tabs.addTab(self.create_verify_tab(), 'Verify')

        tabs.setTabToolTip(0, 'View the transaction in human-readable form')
        tabs.setTabToolTip(1,
                           'Download previous transactions and verify inputs')

        form.addRow('Raw Tx:', self.raw_tx_edit)
        form.addRow(self.raw_tx_invalid)
        form.addRow(tabs)
        return form

    def create_deserialize_tab(self):
        form = QFormLayout()

        self.deserialize_button = QPushButton('Deserialize')
        self.deserialize_button.clicked.connect(self.deserialize)
        btn_hbox = floated_buttons([self.deserialize_button])

        self.tx_widget = TxWidget()
        self.tx_widget.inputs_tree.view.customContextMenuRequested.disconnect(
            self.tx_widget.inputs_tree.customContextMenu)
        self.tx_widget.inputs_tree.view.customContextMenuRequested.connect(
            self.inputs_context_menu)

        form.addRow(self.tx_widget)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_verify_tab(self):
        form = QFormLayout()
        form.setRowWrapPolicy(QFormLayout.WrapLongRows)

        self.inputs_box = QSpinBox()

        self.verify_button = QPushButton('Verify')
        self.verify_button.clicked.connect(self.verify_input)

        self.verify_all_button = QPushButton('Verify All')
        self.verify_all_button.clicked.connect(self.verify_inputs)

        self.result_edit = QLineEdit()
        self.result_edit.setReadOnly(True)

        self.inputs_table = InputStatusTable()

        form.addRow('Verify Input:',
                    floated_buttons([self.inputs_box, self.verify_button]))
        form.addRow(floated_buttons([self.verify_all_button]))
        form.addRow('Result:', self.result_edit)
        form.addRow(self.inputs_table)

        w = QWidget()
        w.setLayout(form)
        return w

    def context_menu(self, position):
        menu = QMenu()
        if self.tx:
            txt = str(self.raw_tx_edit.toPlainText())
            self.handler.add_plugin_actions(self, menu, 'raw_transaction', txt)

        menu.exec_(self.mapToGlobal(position))

    def inputs_context_menu(self, position):
        inputs = self.tx_widget.inputs_tree

        def inputs_context_verify():
            self.tabs.setCurrentIndex(1)
            item = inputs.model.itemFromIndex(inputs.view.selectedIndexes()[0])
            row = item.row()
            self.do_verify_input(self.tx, row)

        menu = QMenu()
        if self.tx:
            menu = inputs.context_menu()
            menu.addAction('Verify script', inputs_context_verify)

        menu.exec_(inputs.view.viewport().mapToGlobal(position))

    def clear(self):
        self.result_edit.clear()
        self.tx_widget.clear()
        self.inputs_table.clear()

    def check_raw_tx(self):
        txt = str(self.raw_tx_edit.toPlainText())
        tx = None
        valid = True
        try:
            tx = Transaction.deserialize(txt.decode('hex'))
        except Exception:
            valid = False

        self.tx = tx
        self.deserialize_button.setEnabled(valid)
        self.inputs_box.setEnabled(valid)
        self.verify_button.setEnabled(valid)
        self.verify_all_button.setEnabled(valid)
        self.tx_widget.setEnabled(valid)
        self.clear()
        if valid:
            self.raw_tx_invalid.hide()
            self.inputs_box.setRange(0, len(tx.vin) - 1)
            self.deserialize()
        elif txt:
            self.raw_tx_invalid.show()
        else:
            self.raw_tx_invalid.hide()

    def deserialize_raw(self, txt):
        """Deserialize a raw transaction."""
        self.needsFocus.emit()
        self.raw_tx_edit.setPlainText(txt)
        self.deserialize()

    def deserialize(self):
        self.clear()
        self.tx_widget.set_tx(self.tx)
        self.inputs_table.set_tx(self.tx)
        self.status_message('Deserialized transaction {}'.format(
            bitcoin.core.b2lx(self.tx.GetHash())))

    def do_verify_input(self, tx, in_idx):
        raw_prev_tx = None
        tx_in = tx.vin[in_idx]
        txid = b2lx(tx_in.prevout.hash)
        prev_out_n = tx_in.prevout.n

        try:
            raw_prev_tx = self.handler.download_blockchain_data(
                'raw_transaction', txid)
        except Exception as e:
            self.status_message(str(e), True)
            return False

        try:
            prev_tx = Transaction.deserialize(raw_prev_tx.decode('hex'))
            result = bitcoin.core.scripteval.VerifyScript(
                tx_in.scriptSig, prev_tx.vout[prev_out_n].scriptPubKey, tx,
                in_idx)
            self.result_edit.setText(
                'Successfully verified input {}'.format(in_idx))
            self.inputs_table.set_verified(in_idx, True)
        except Exception as e:
            self.result_edit.setText(str(e))
            self.inputs_table.set_verified(in_idx, False)
            self.status_message(str(e), True)
            return False

        return True

    def do_verify_inputs(self, txt):
        self.needsFocus.emit()
        self.raw_tx_edit.setPlainText(txt)
        tx = Transaction.deserialize(txt.decode('hex'))
        failed_inputs = []
        self.result_edit.setText('Verifying...')
        for i in range(len(tx.vin)):
            if not self.do_verify_input(tx, i):
                failed_inputs.append(i)

        result = 'Successfully verified all inputs.'
        ret_val = True
        if failed_inputs:
            result = 'Failed to verify inputs: {}'.format(failed_inputs)
            ret_val = False
        if len(tx.vin) == 0:
            result = 'Transaction has no inputs.'
        self.result_edit.setText(result)
        return ret_val

    def verify_input(self):
        tx = None
        try:
            txt = str(self.raw_tx_edit.toPlainText())
            tx = Transaction.deserialize(txt.decode('hex'))
        except Exception:
            self.status_message('Could not deserialize transaction.', True)
            return
        in_idx = self.inputs_box.value()
        if in_idx >= len(tx.vin):
            self.status_message('Input {} does not exist.'.format(in_idx),
                                True)
            return

        self.do_verify_input(tx, in_idx)

    def verify_inputs(self):
        txt = str(self.raw_tx_edit.toPlainText())
        self.do_verify_inputs(txt)

    def on_option_changed(self, key):
        if key == 'amount_format':
            self.needsUpdate.emit()

    def refresh_data(self):
        if self.tx:
            self.deserialize()
示例#7
0
class TxAnalyzer(BaseDock):

    tool_name = 'Transaction Analyzer'
    description = 'Deserializes transactions and verifies their inputs.'
    is_large = True
    category = Category.Tx

    def __init__(self, handler):
        super(TxAnalyzer, self).__init__(handler)
        self.raw_tx_edit.textChanged.emit()

    def init_data(self):
        self.tx = None

    def init_actions(self):
        deserialize = ('Deserialize', self.deserialize_raw)
        verify = ('Verify inputs', self.do_verify_inputs)
        self.advertised_actions['raw_transaction'] = [deserialize, verify]

    def create_layout(self):
        form = QFormLayout()

        self.raw_tx_edit = QPlainTextEdit()
        self.raw_tx_edit.setFont(monospace_font)
        self.raw_tx_edit.setContextMenuPolicy(Qt.CustomContextMenu)
        self.raw_tx_edit.customContextMenuRequested.connect(self.context_menu)
        self.raw_tx_edit.textChanged.connect(self.check_raw_tx)

        self.raw_tx_invalid = QLabel('Cannot parse transaction.')
        self.raw_tx_invalid.setProperty('hasError', True)

        self.tabs = tabs = QTabWidget()
        tabs.addTab(self.create_deserialize_tab(), 'Deserialize')
        tabs.addTab(self.create_verify_tab(), 'Verify')

        tabs.setTabToolTip(0, 'View the transaction in human-readable form')
        tabs.setTabToolTip(1, 'Download previous transactions and verify inputs')

        form.addRow('Raw Tx:', self.raw_tx_edit)
        form.addRow(self.raw_tx_invalid)
        form.addRow(tabs)
        return form

    def create_deserialize_tab(self):
        form = QFormLayout()

        self.deserialize_button = QPushButton('Deserialize')
        self.deserialize_button.clicked.connect(self.deserialize)
        btn_hbox = floated_buttons([self.deserialize_button])

        self.tx_widget = TxWidget()
        self.tx_widget.inputs_tree.view.customContextMenuRequested.disconnect(self.tx_widget.inputs_tree.customContextMenu)
        self.tx_widget.inputs_tree.view.customContextMenuRequested.connect(self.inputs_context_menu)

        form.addRow(self.tx_widget)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_verify_tab(self):
        form = QFormLayout()
        form.setRowWrapPolicy(QFormLayout.WrapLongRows)

        self.inputs_box = QSpinBox()

        self.verify_button = QPushButton('Verify')
        self.verify_button.clicked.connect(self.verify_input)

        self.verify_all_button = QPushButton('Verify All')
        self.verify_all_button.clicked.connect(self.verify_inputs)

        self.result_edit = QLineEdit()
        self.result_edit.setReadOnly(True)

        self.inputs_table = InputStatusTable()

        form.addRow('Verify Input:', floated_buttons([self.inputs_box, self.verify_button]))
        form.addRow(floated_buttons([self.verify_all_button]))
        form.addRow('Result:', self.result_edit)
        form.addRow(self.inputs_table)

        w = QWidget()
        w.setLayout(form)
        return w

    def context_menu(self, position):
        menu = QMenu()
        if self.tx:
            txt = str(self.raw_tx_edit.toPlainText())
            self.handler.add_plugin_actions(self, menu, 'raw_transaction', txt)

        menu.exec_(self.mapToGlobal(position))

    def inputs_context_menu(self, position):
        inputs = self.tx_widget.inputs_tree
        def inputs_context_verify():
            self.tabs.setCurrentIndex(1)
            item = inputs.model.itemFromIndex(inputs.view.selectedIndexes()[0])
            row = item.row()
            self.do_verify_input(self.tx, row)

        menu = QMenu()
        if self.tx:
            menu = inputs.context_menu()
            menu.addAction('Verify script', inputs_context_verify)

        menu.exec_(inputs.view.viewport().mapToGlobal(position))

    def clear(self):
        self.result_edit.clear()
        self.tx_widget.clear()
        self.inputs_table.clear()

    def check_raw_tx(self):
        txt = str(self.raw_tx_edit.toPlainText())
        tx = None
        valid = True
        try:
            tx = Transaction.deserialize(txt.decode('hex'))
        except Exception:
            valid = False

        self.tx = tx
        self.deserialize_button.setEnabled(valid)
        self.inputs_box.setEnabled(valid)
        self.verify_button.setEnabled(valid)
        self.verify_all_button.setEnabled(valid)
        self.tx_widget.setEnabled(valid)
        self.clear()
        if valid:
            self.raw_tx_invalid.hide()
            self.inputs_box.setRange(0, len(tx.vin) - 1)
            self.deserialize()
        elif txt:
            self.raw_tx_invalid.show()
        else:
            self.raw_tx_invalid.hide()

    def deserialize_raw(self, txt):
        """Deserialize a raw transaction."""
        self.needsFocus.emit()
        self.raw_tx_edit.setPlainText(txt)
        self.deserialize()

    def deserialize(self):
        self.clear()
        self.tx_widget.set_tx(self.tx)
        self.inputs_table.set_tx(self.tx)
        self.status_message('Deserialized transaction {}'.format(bitcoin.core.b2lx(self.tx.GetHash())))

    def do_verify_input(self, tx, in_idx):
        raw_prev_tx = None
        tx_in = tx.vin[in_idx]
        txid = b2lx(tx_in.prevout.hash)
        prev_out_n = tx_in.prevout.n

        try:
            raw_prev_tx = self.handler.download_blockchain_data('raw_transaction', txid)
        except Exception as e:
            self.status_message(str(e), True)
            return False

        try:
            prev_tx = Transaction.deserialize(raw_prev_tx.decode('hex'))
            result = bitcoin.core.scripteval.VerifyScript(tx_in.scriptSig, prev_tx.vout[prev_out_n].scriptPubKey, tx, in_idx)
            self.result_edit.setText('Successfully verified input {}'.format(in_idx))
            self.inputs_table.set_verified(in_idx, True)
        except Exception as e:
            self.result_edit.setText(str(e))
            self.inputs_table.set_verified(in_idx, False)
            self.status_message(str(e), True)
            return False

        return True

    def do_verify_inputs(self, txt):
        self.needsFocus.emit()
        self.raw_tx_edit.setPlainText(txt)
        tx = Transaction.deserialize(txt.decode('hex'))
        failed_inputs = []
        self.result_edit.setText('Verifying...')
        for i in range(len(tx.vin)):
            if not self.do_verify_input(tx, i):
                failed_inputs.append(i)

        result = 'Successfully verified all inputs.'
        ret_val = True
        if failed_inputs:
            result = 'Failed to verify inputs: {}'.format(failed_inputs)
            ret_val = False
        if len(tx.vin) == 0:
            result = 'Transaction has no inputs.'
        self.result_edit.setText(result)
        return ret_val

    def verify_input(self):
        tx = None
        try:
            txt = str(self.raw_tx_edit.toPlainText())
            tx = Transaction.deserialize(txt.decode('hex'))
        except Exception:
            self.status_message('Could not deserialize transaction.', True)
            return
        in_idx = self.inputs_box.value()
        if in_idx >= len(tx.vin):
            self.status_message('Input {} does not exist.'.format(in_idx), True)
            return

        self.do_verify_input(tx, in_idx)

    def verify_inputs(self):
        txt = str(self.raw_tx_edit.toPlainText())
        self.do_verify_inputs(txt)

    def on_option_changed(self, key):
        if key == 'amount_format':
            self.needsUpdate.emit()

    def refresh_data(self):
        if self.tx:
            self.deserialize()
示例#8
0
class TxBuilder(BaseDock):

    tool_name = 'Transaction Builder'
    description = 'Transaction Builder helps you create transactions.'
    is_large = True
    category = Category.Tx

    def __init__(self, handler):
        super(TxBuilder, self).__init__(handler)
        self.raw_tx.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.raw_tx.customContextMenuRequested.connect(self.context_menu)

    def init_data(self):
        self.tx = None

    def create_layout(self):
        vbox = QVBoxLayout()
        self.tabs = tabs = QTabWidget()

        tabs.addTab(self.create_version_locktime_tab(), '&Version/Locktime')
        tabs.addTab(self.create_inputs_tab(), '&Inputs')
        tabs.addTab(self.create_outputs_tab(), '&Outputs')
        tabs.addTab(self.create_review_tab(), '&Review')

        self.tx_field_widgets = []
        tabs.insertTab(3, self.create_other_tab(), 'Ot&her')
        self.adjust_tx_fields()

        # Build the tx if the Review tab is selected.
        def maybe_build(i):
            if str(tabs.tabText(i)) == '&Review':
                self.build_transaction()
        tabs.currentChanged.connect(maybe_build)

        vbox.addWidget(tabs)
        return vbox

    def context_menu(self, position):
        menu = self.raw_tx.createStandardContextMenu(position)

        txt = str(self.raw_tx.toPlainText())
        if txt:
            self.handler.add_plugin_actions(self, menu, 'raw_transaction', txt)

        menu.exec_(self.raw_tx.viewport().mapToGlobal(position))

    def create_version_locktime_tab(self):
        form = QFormLayout()
        self.version_edit = AmountEdit()
        self.version_edit.setText('1')

        self.locktime_edit = AmountEdit()
        self.locktime_edit.setText('0')

        version_desc = QLabel('A transaction\'s version determines how it is interpreted.\n\nBitcoin transactions are currently version 1.')
        locktime_desc = QLabel('A transaction\'s locktime defines the earliest time or block that it may be added to the blockchain.\n\nLocktime only applies if it\'s non-zero and at least one input has a Sequence that\'s not the maximum possible value.')
        for i in [version_desc, locktime_desc]:
            i.setWordWrap(True)
        for i in [self.version_edit, self.locktime_edit]:
            i.setFont(monospace_font)

        form.addRow(version_desc)
        form.addRow('Version:', self.version_edit)
        form.addRow(Separator())
        form.addRow(locktime_desc)
        form.addRow('Locktime:', self.locktime_edit)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_inputs_tab(self):
        form = QFormLayout()
        self.inputs_tree = InputsTree()

        input_prev_tx = QLineEdit()
        input_prev_tx.setToolTip('Transaction ID of the tx with the output being spent')

        input_prev_vout = AmountEdit()
        input_prev_vout.setToolTip('Output index of the previous transaction')

        input_script = QTextEdit()
        input_script.setToolTip('Script that will be put on the stack before the previous output\'s script.')

        input_sequence = AmountEdit()
        input_sequence.setText('4294967295')
        maxify_input_sequence = QPushButton('Max')
        maxify_input_sequence.clicked.connect(lambda: input_sequence.setText('0xffffffff'))

        rm_input_edit = QSpinBox()
        rm_input_edit.setRange(0, 0)
        rm_input_button = QPushButton('Remove input')

        def add_input():
            try:
                outpoint = COutPoint(lx(str(input_prev_tx.text())), input_prev_vout.get_amount())
                in_script = Script.from_human(str(input_script.toPlainText()))
                new_input = CTxIn(outpoint, in_script.get_hex().decode('hex'), input_sequence.get_amount())
            except Exception as e:
                self.status_message(str(e), True)
                return
            else:
                self.inputs_tree.add_input(new_input)
                rm_input_edit.setRange(0, len(self.inputs_tree.get_inputs()) - 1)

        def rm_input():
            in_num = rm_input_edit.value()
            self.inputs_tree.model.takeRow(in_num)
            rm_input_edit.setRange(0, len(self.inputs_tree.get_inputs()) - 1)

        add_input_button = QPushButton('Add input')
        add_input_button.setToolTip('Add the above input')
        add_input_button.clicked.connect(add_input)

        rm_input_button.clicked.connect(rm_input)

        for i in [input_prev_tx, input_prev_vout, input_script, input_sequence]:
            i.setFont(monospace_font)

        form.addRow(self.inputs_tree)
        form.addRow(Separator())

        form.addRow('Previous Transaction:', input_prev_tx)
        form.addRow('Previous Tx Output:', input_prev_vout)
        form.addRow('Input script:', input_script)
        seq_desc = QLabel('Sequence is mostly deprecated.\nIf an input has a sequence that\'s not the maximum value, the transaction\'s locktime will apply.')
        seq_desc.setWordWrap(True)
        form.addRow(seq_desc)
        form.addRow('Sequence:', HBox(input_sequence, maxify_input_sequence))

        form.addRow(Separator())
        form.addRow(floated_buttons([add_input_button]))
        form.addRow('Remove input:', HBox(rm_input_edit, rm_input_button))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_outputs_tab(self):
        form = QFormLayout()
        self.outputs_tree = OutputsTree()

        output_value = QLineEdit()

        output_script = QTextEdit()
        output_script.setToolTip('Script that will be put on the stack after the input that spends it.')

        rm_output_edit = QSpinBox()
        rm_output_edit.setRange(0, 0)
        rm_output_button = QPushButton('Remove output')

        def add_output():
            try:
                val_str = str(output_value.text())
                value = 0
                if '.' in val_str:
                    value = int(float(val_str) * pow(10, 8))
                else:
                    value = int(val_str)
                out_script = Script.from_human(str(output_script.toPlainText()))
                new_output = CTxOut(value, out_script.get_hex().decode('hex'))
            except Exception as e:
                self.status_message(str(e), True)
                return
            else:
                self.outputs_tree.add_output(new_output)
                rm_output_edit.setRange(0, len(self.outputs_tree.get_outputs()) - 1)

        def rm_output():
            out_n = rm_output_edit.value()
            self.outputs_tree.model.takeRow(out_n)
            rm_output_edit.setRange(0, len(self.outputs_tree.get_outputs()) - 1)

        add_output_button = QPushButton('Add output')
        add_output_button.setToolTip('Add the above output')
        add_output_button.clicked.connect(add_output)

        rm_output_button.clicked.connect(rm_output)

        value_desc = QLabel('Include a decimal point if this value is not in satoshis.')
        value_desc.setWordWrap(True)

        for i in [output_value, output_script]:
            i.setFont(monospace_font)

        form.addRow(self.outputs_tree)
        form.addRow(Separator())

        form.addRow(value_desc)
        form.addRow('Value:', output_value)
        form.addRow('Output script:', output_script)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_output_button]))
        form.addRow('Remove output:', HBox(rm_output_edit, rm_output_button))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_review_tab(self):
        form = QFormLayout()

        self.raw_tx = QTextEdit()
        self.raw_tx.setReadOnly(True)

        self.tx_widget = TxWidget()

        build_button = QPushButton('Build transaction')
        build_button.setToolTip('Build a tx from the data in the previous tabs')
        build_button.clicked.connect(self.build_transaction)

        form.addRow('Raw Tx:', self.raw_tx)
        form.addRow(self.tx_widget)
        form.addRow(floated_buttons([build_button]))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_other_tab(self):
        self.tx_fields_layout = QFormLayout()

        w = QWidget()
        w.setLayout(self.tx_fields_layout)
        return w

    def build_transaction(self):
        self.tx_widget.clear()
        self.tx = tx = Transaction()
        tx.nVersion = self.version_edit.get_amount()
        for i in self.inputs_tree.get_inputs():
            tx.vin.append(i)
        for o in self.outputs_tree.get_outputs():
            tx.vout.append(o)
        tx.nLockTime = self.locktime_edit.get_amount()

        for name, w in self.tx_field_widgets:
            if not name in [field[0] for field in tx.fields]:
                continue
            value = str(w.text())
            default = getattr(tx, name)
            if isinstance(default, int):
                value = w.get_amount()
            setattr(tx, name, value)

        self.raw_tx.setText(bitcoin.core.b2x(tx.serialize()))

        self.tx_widget.set_tx(tx)

    def on_option_changed(self, key):
        if key in ['amount_format', 'chainparams']:
            self.needsUpdate.emit()

    def adjust_tx_fields(self):
        """Show or hide tx field widgets."""
        tx_fields = chainparams.get_tx_fields()
        for field in tx_fields:
            name = field[0]
            if name in ['nVersion', 'vin', 'vout', 'nLockTime']:
                continue

            default_value = field[3]
            if name not in [j[0] for j in self.tx_field_widgets]:
                widget = QLineEdit()
                if isinstance(default_value, int):
                    # Special case for timestamp fields.
                    if name == 'Timestamp':
                        widget = TimestampWidget()
                        widget.timestamp_raw.setReadOnly(False)
                    else:
                        widget = AmountEdit()
                widget.setText(str(default_value))
                label = QLabel(''.join([name, ':']))
                self.tx_field_widgets.append((name, widget))
                self.tx_fields_layout.addRow(label, widget)

        tx_field_names = [i[0] for i in tx_fields]
        for name, w in self.tx_field_widgets:
            l = self.tx_fields_layout.labelForField(w)
            if name in tx_field_names:
                w.show()
                l.show()
            else:
                w.hide()
                l.hide()

        if tx_field_names == ['nVersion', 'vin', 'vout', 'nLockTime']:
            self.tabs.setTabEnabled(3, False)
        else:
            self.tabs.setTabEnabled(3, True)

    def refresh_data(self):
        self.adjust_tx_fields()
        self.build_transaction()
        self.outputs_tree.amount_format_changed()
示例#9
0
class TxBuilder(BaseDock):

    def init_metadata(self):
        self.tool_name = 'Transaction Builder'
        self.description = 'Transaction Builder helps you create transactions.'

    def init_data(self):
        self.tx = None

    def create_layout(self):
        vbox = QVBoxLayout()
        tabs = QTabWidget()

        tabs.addTab(self.create_version_locktime_tab(), '&Version/Locktime')
        tabs.addTab(self.create_inputs_tab(), '&Inputs')
        tabs.addTab(self.create_outputs_tab(), '&Outputs')
        tabs.addTab(self.create_review_tab(), '&Review')

        vbox.addWidget(tabs)
        return vbox


    def create_version_locktime_tab(self):
        form = QFormLayout()
        self.version_edit = AmountEdit()
        self.version_edit.setText('1')

        self.locktime_edit = AmountEdit()
        self.locktime_edit.setText('0')

        version_desc = QLabel('A transaction\'s version determines how it is interpreted.\n\nBitcoin transactions are currently version 1.')
        locktime_desc = QLabel('A transaction\'s locktime defines the earliest time or block that it may be added to the blockchain.\n\nLocktime only applies if it\'s non-zero and at least one input has a Sequence that\'s not the maximum possible value.')
        for i in [version_desc, locktime_desc]:
            i.setWordWrap(True)
        for i in [self.version_edit, self.locktime_edit]:
            i.setFont(monospace_font)

        form.addRow(version_desc)
        form.addRow('Version:', self.version_edit)
        form.addRow(Separator())
        form.addRow(locktime_desc)
        form.addRow('Locktime:', self.locktime_edit)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_inputs_tab(self):
        form = QFormLayout()
        self.inputs_tree = InputsTree()

        input_prev_tx = QLineEdit()
        input_prev_tx.setToolTip('Transaction ID of the tx with the output being spent')

        input_prev_vout = AmountEdit()
        input_prev_vout.setToolTip('Output index of the previous transaction')

        input_script = QTextEdit()
        input_script.setToolTip('Script that will be put on the stack before the previous output\'s script.')

        input_sequence = AmountEdit()
        input_sequence.setText('4294967295')
        maxify_input_sequence = QPushButton('Max')
        maxify_input_sequence.clicked.connect(lambda: input_sequence.setText('0xffffffff'))

        rm_input_edit = QSpinBox()
        rm_input_edit.setRange(0, 0)
        rm_input_button = QPushButton('Remove input')

        def add_input():
            try:
                outpoint = COutPoint(lx(str(input_prev_tx.text())), input_prev_vout.get_amount())
                in_script = Script.from_human(str(input_script.toPlainText()))
                new_input = CTxIn(outpoint, in_script.get_hex().decode('hex'), input_sequence.get_amount())
            except Exception as e:
                self.status_message(str(e), True)
                return
            else:
                self.inputs_tree.add_input(new_input)
                rm_input_edit.setRange(0, len(self.inputs_tree.get_inputs()) - 1)

        def rm_input():
            in_num = rm_input_edit.value()
            self.inputs_tree.model.takeRow(in_num)
            rm_input_edit.setRange(0, len(self.inputs_tree.get_inputs()) - 1)

        add_input_button = QPushButton('Add input')
        add_input_button.setToolTip('Add the above input')
        add_input_button.clicked.connect(add_input)

        rm_input_button.clicked.connect(rm_input)

        for i in [input_prev_tx, input_prev_vout, input_script, input_sequence]:
            i.setFont(monospace_font)

        form.addRow(self.inputs_tree)
        form.addRow(Separator())

        form.addRow('Previous Transaction:', input_prev_tx)
        form.addRow('Previous Tx Output:', input_prev_vout)
        form.addRow('Input script:', input_script)
        seq_desc = QLabel('Sequence is mostly deprecated.\nIf an input has a sequence that\'s not the maximum value, the transaction\'s locktime will apply.')
        seq_desc.setWordWrap(True)
        form.addRow(seq_desc)
        form.addRow('Sequence:', HBox(input_sequence, maxify_input_sequence))

        form.addRow(Separator())
        form.addRow(floated_buttons([add_input_button]))
        form.addRow('Remove input:', HBox(rm_input_edit, rm_input_button))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_outputs_tab(self):
        form = QFormLayout()
        self.outputs_tree = OutputsTree()

        output_value = QLineEdit()

        output_script = QTextEdit()
        output_script.setToolTip('Script that will be put on the stack after the input that spends it.')

        rm_output_edit = QSpinBox()
        rm_output_edit.setRange(0, 0)
        rm_output_button = QPushButton('Remove output')

        def add_output():
            try:
                val_str = str(output_value.text())
                value = 0
                if '.' in val_str:
                    value = int(float(val_str) * pow(10, 8))
                else:
                    value = int(val_str)
                out_script = Script.from_human(str(output_script.toPlainText()))
                new_output = CTxOut(value, out_script.get_hex().decode('hex'))
            except Exception as e:
                self.status_message(str(e), True)
                return
            else:
                self.outputs_tree.add_output(new_output)
                rm_output_edit.setRange(0, len(self.outputs_tree.get_outputs()) - 1)

        def rm_output():
            out_n = rm_output_edit.value()
            self.outputs_tree.model.takeRow(out_n)
            rm_output_edit.setRange(0, len(self.outputs_tree.get_outputs()) - 1)

        add_output_button = QPushButton('Add output')
        add_output_button.setToolTip('Add the above output')
        add_output_button.clicked.connect(add_output)

        rm_output_button.clicked.connect(rm_output)

        value_desc = QLabel('Include a decimal point if this value is not in satoshis.')
        value_desc.setWordWrap(True)

        for i in [output_value, output_script]:
            i.setFont(monospace_font)

        form.addRow(self.outputs_tree)
        form.addRow(Separator())

        form.addRow(value_desc)
        form.addRow('Value:', output_value)
        form.addRow('Output script:', output_script)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_output_button]))
        form.addRow('Remove output:', HBox(rm_output_edit, rm_output_button))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_review_tab(self):
        form = QFormLayout()

        self.raw_tx = QTextEdit()
        self.raw_tx.setReadOnly(True)

        self.tx_widget = TxWidget()

        build_button = QPushButton('Build transaction')
        build_button.setToolTip('Build a tx from the data in the previous tabs')
        build_button.clicked.connect(self.build_transaction)

        form.addRow('Raw Tx:', self.raw_tx)
        form.addRow(self.tx_widget)
        form.addRow(floated_buttons([build_button]))

        w = QWidget()
        w.setLayout(form)
        return w

    def build_transaction(self):
        self.tx_widget.clear()
        self.tx = tx = CMutableTransaction()
        tx.nVersion = self.version_edit.get_amount()
        for i in self.inputs_tree.get_inputs():
            tx.vin.append(i)
        for o in self.outputs_tree.get_outputs():
            tx.vout.append(o)
        tx.nLockTime = self.locktime_edit.get_amount()

        self.raw_tx.setText(bitcoin.core.b2x(tx.serialize()))

        self.tx_widget.set_tx(tx)

    def on_option_changed(self, key):
        if key == 'amount_format':
            self.needsUpdate.emit()

    def refresh_data(self):
        self.build_transaction()
        self.outputs_tree.amount_format_changed()