Example #1
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()
Example #2
0
    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
Example #3
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()