Exemple #1
0
    def start_upgrade(self, client):
        # ask for a filename (must have already downloaded it)
        mw = get_parent_main_window(self)
        dev = client.dev

        fileName = mw.getOpenFileName("Select upgraded firmware file", "*.dfu")
        if not fileName:
            return

        from ckcc.utils import dfu_parse
        from ckcc.sigheader import FW_HEADER_SIZE, FW_HEADER_OFFSET, FW_HEADER_MAGIC
        from ckcc.protocol import CCProtocolPacker
        from hashlib import sha256
        import struct

        try:
            with open(fileName, 'rb') as fd:

                # unwrap firmware from the DFU
                offset, size, *ignored = dfu_parse(fd)

                fd.seek(offset)
                firmware = fd.read(size)

            hpos = FW_HEADER_OFFSET
            hdr = bytes(firmware[hpos:hpos +
                                 FW_HEADER_SIZE])  # needed later too
            magic = struct.unpack_from("<I", hdr)[0]

            if magic != FW_HEADER_MAGIC:
                raise ValueError("Bad magic")
        except Exception as exc:
            mw.show_error(
                "Does not appear to be a Coldcard firmware file.\n\n%s" % exc)
            return

        # TODO:
        # - detect if they are trying to downgrade; aint gonna work
        # - warn them about the reboot?
        # - length checks
        # - add progress local bar
        mw.show_message(
            "Ready to Upgrade.\n\nBe patient. Unit will reboot itself when complete."
        )

        def doit():
            dlen, _ = dev.upload_file(firmware, verify=True)
            assert dlen == len(firmware)

            # append the firmware header a second time
            result = dev.send_recv(
                CCProtocolPacker.upload(size, size + FW_HEADER_SIZE, hdr))

            # make it reboot into bootlaoder which might install it
            dev.send_recv(CCProtocolPacker.reboot())

        self.thread.add(doit)
        self.close()
Exemple #2
0
    def start_upgrade(self, client):
        # ask for a filename (must have already downloaded it)
        mw = get_parent_main_window(self)
        dev = client.dev

        fileName = mw.getOpenFileName("Select upgraded firmware file", "*.dfu")
        if not fileName:
            return

        from ckcc.utils import dfu_parse
        from ckcc.sigheader import FW_HEADER_SIZE, FW_HEADER_OFFSET, FW_HEADER_MAGIC
        from ckcc.protocol import CCProtocolPacker
        from hashlib import sha256
        import struct

        try:
            with open(fileName, 'rb') as fd:

                # unwrap firmware from the DFU
                offset, size, *ignored = dfu_parse(fd)

                fd.seek(offset)
                firmware = fd.read(size)

            hpos = FW_HEADER_OFFSET
            hdr = bytes(firmware[hpos:hpos + FW_HEADER_SIZE])        # needed later too
            magic = struct.unpack_from("<I", hdr)[0]

            if magic != FW_HEADER_MAGIC:
                raise ValueError("Bad magic")
        except Exception as exc:
            mw.show_error("Does not appear to be a Coldcard firmware file.\n\n%s" % exc)
            return

        # TODO: 
        # - detect if they are trying to downgrade; aint gonna work
        # - warn them about the reboot?
        # - length checks
        # - add progress local bar
        mw.show_message("Ready to Upgrade.\n\nBe patient. Unit will reboot itself when complete.")

        def doit():
            dlen, _ = dev.upload_file(firmware, verify=True)
            assert dlen == len(firmware)

            # append the firmware header a second time
            result = dev.send_recv(CCProtocolPacker.upload(size, size+FW_HEADER_SIZE, hdr))

            # make it reboot into bootlaoder which might install it
            dev.send_recv(CCProtocolPacker.reboot())

        self.thread.add(doit)
        self.close()
Exemple #3
0
    def display_bitpost(self, dialog):
        window = get_parent_main_window(dialog)
        if self.config.get('batch_rbf', False):
            window.show_error(_(
                "Warning: batch rbf transaction is not supported by bitpost plugin"
            ),
                              parent=self.window)

        invoice = window.read_invoice()
        if not invoice:
            window.logger.exception("BitPostPlugin: Invoice is Null")
            return

        window.wallet.save_invoice(invoice)
        window.invoice_list.update()
        window.do_clear()

        inputs = window.get_coins(nonlocal_only=True)

        outputs = invoice.outputs

        output_values = [x.value for x in outputs]
        if output_values.count('!') > 1:
            window.show_error(_("More than one output set to spend max"))
            return
        is_sweep = bool(None)
        output_value = '!' if '!' in output_values else sum(output_values)
        d = ConfirmTxDialog(window=window,
                            inputs=inputs,
                            outputs=outputs,
                            output_value=output_value,
                            is_sweep=is_sweep)

        if d.not_enough_funds:
            # Check if we had enough funds excluding fees, if so, still provide opportunity to set lower fees.
            if not d.have_enough_funds_assuming_zero_fees():
                self.show_message(_('Not Enough Funds'))
                return

        self.send_bitpost_request(d, invoice)
Exemple #4
0
 def receive_list_menu(self, menu, addr):
     window = get_parent_main_window(menu)
     menu.addAction(_("Send via e-mail"), lambda: self.send(window, addr))
Exemple #5
0
    def send_bitpost_request(self, dialog, invoice):
        window = get_parent_main_window(dialog)

        cancelled, is_send, password, txs, target, delay, max_fees, max_size = dialog.run(
        )
        if cancelled or not is_send:
            return

        raw_signed_txs = []
        window.logger.debug("TXS BATCH SIZE", len(txs))
        max_fee = 0
        for tx in txs:
            window.logger.debug("----------------")
            window.logger.debug("FEE", tx.get_fee())
            max_fee = max(max_fee, tx.get_fee())
            window.logger.debug("****************************")
            window.wallet.sign_transaction(tx, password)
            window.logger.debug("SERIALIZED", tx.serialize_to_network())
            raw_signed_txs.append(tx.serialize_to_network())
            window.logger.debug("****************************")

        window.logger.debug("transactions signed")
        if len(raw_signed_txs) == 0:
            return
        try:
            delay = delay.timestamp()
        except:
            delay = self.config.get('bitpost_delay')

        now = datetime.now().timestamp()
        if target < delay:
            window.show_error(_("Target should be greater than delay"))
            return
        if now > target:
            target = now + 600
        if now > delay:
            delay = 0

        if self.config.get('testnet'):
            testnet = True
        else:
            testnet = False
        bitpost_interface = BitpostInterface(testnet=testnet)

        request = bitpost_interface.create_bitpost_request(raw_signed_txs,
                                                           int(target),
                                                           delay=int(delay))

        if self.config.get('bitpost_notification_platform') != 'None':
            request.add_notification(
                self.config.get('bitpost_notification_platform'),
                self.config.get('bitpost_notification_address'),
                self.config.get('bitpost_notification_subscriptions'))

        response = request.send_request().json()
        if response['status'] == 'success':
            if len(invoice.message) > 0:
                invoice.message += "\n"
            invoice.message += "{},{},{},{},{}".format(response['data']['url'],
                                                       delay, target, max_fees,
                                                       max_size)

            window.wallet.save_invoice(invoice)

            window.invoice_list.update()
            self.bitpost_list.insert_bitpost(invoice.time, invoice.message)
            window.do_clear()
        elif response['status'] == 'fail':
            import json
            self.window.show_error(str(response), parent=self.window)
Exemple #6
0
def create_settings_window(small_window):
    window = get_parent_main_window(small_window)

    d = WindowModalDialog(window, _("Bitpost settings"))
    vbox = QVBoxLayout(d)

    d.setMinimumSize(500, 200)

    hbox_maxfee = QHBoxLayout()
    hbox_maxfee.addWidget(QLabel("Default max fee to use"))
    max_fees_e = QLineEdit()
    max_fees_e.setText(str(window.config.get('bitpost_max_fee')))
    hbox_maxfee.addWidget(max_fees_e)

    fee_combo = QComboBox()
    fee_combo_values = get_fee_units(window,
                                     window.config.get('bitpost_max_fee_unit'))

    fee_combo.addItems(fee_combo_values)
    hbox_maxfee.addWidget(fee_combo)

    help_button__max_fee = QPushButton("?")
    help_button__max_fee.clicked.connect(
        lambda: d.show_message(HelpTexts.max_fee))
    hbox_maxfee.addWidget(help_button__max_fee)

    vbox.addLayout(hbox_maxfee)

    empty_line = QHBoxLayout()
    empty_line.addWidget(QLabel(""))
    vbox.addLayout(empty_line)

    advanced_settings_title = QHBoxLayout()
    advanced_settings_title.addStretch()
    advanced_settings_title.addWidget(QLabel("<b>Notifications</b>"))
    advanced_settings_title.addStretch()
    vbox.addLayout(advanced_settings_title)

    telegram_reminder = QLabel(
        _("<b>Remember to start <a href='https:/t.me/bitpostbot'>@BitpostBot</a></b>"
          ))
    telegram_reminder.setVisible(True if window.config.get(
        'bitpost_notification_platform') == 'Telegram' else False)
    telegram_reminder.setOpenExternalLinks(True)

    platform_address = QHBoxLayout()
    platform_address.addWidget(QLabel("Platform"))
    platform_combo = QComboBox()
    fee_combo_values = ['None', 'Email', 'Twitter', 'Telegram']
    platform_combo.addItems(fee_combo_values)
    platform_combo.setCurrentText(
        window.config.get('bitpost_notification_platform'))
    platform_combo.currentTextChanged.connect(
        lambda: telegram_reminder.setVisible(True) if platform_combo.
        currentText() == 'Telegram' else telegram_reminder.setVisible(False))

    platform_address.addWidget(platform_combo)

    platform_address.addWidget(QLabel("Address/handle"))
    vbox.addLayout(platform_address)
    address_input = QLineEdit()
    address_input.setText(window.config.get('bitpost_notification_address',
                                            ''))
    platform_address.addWidget(address_input)

    vbox.addWidget(telegram_reminder)

    subscription_title = QHBoxLayout()
    subscription_title.addWidget(QLabel("Subscriptions"))
    subscriptions_help = QPushButton("?")
    subscriptions_help.clicked.connect(
        lambda: d.show_message(HelpTexts.subscriptions))
    subscription_title.addWidget(subscriptions_help)
    subscription_title.addStretch()
    vbox.addLayout(subscription_title)

    subscriptions = {
        subscription['name']
        for subscription in window.config.get(
            'bitpost_notification_subscriptions')
    }
    subscriptions1 = QVBoxLayout()
    overdue_checkbox = QCheckBox("Overdue")
    if 'overdue' in subscriptions:
        overdue_checkbox.setChecked(True)
    subscriptions1.addWidget(overdue_checkbox)

    mined_checkbox = QCheckBox("Mined")
    if 'mine' in subscriptions:
        mined_checkbox.setChecked(True)
    subscriptions1.addWidget(mined_checkbox)
    max_fee_reached_checkbox = QCheckBox("Maximum fee reached")
    if 'reached' in subscriptions:
        max_fee_reached_checkbox.setChecked(True)
    subscriptions1.addWidget(max_fee_reached_checkbox)
    vbox.addLayout(subscriptions1)

    reorg_checkbox = QCheckBox("Block reorganization")
    if 'orphaned_block' in subscriptions:
        reorg_checkbox.setChecked(True)
    subscriptions1.addWidget(reorg_checkbox)
    orphaned_checkbox = QCheckBox("Child transaction orphaned")
    if '' in subscriptions:
        orphaned_checkbox.setChecked(True)
    subscriptions1.addWidget(orphaned_checkbox)

    advanced_settings_title = QHBoxLayout()
    advanced_settings_title.addStretch()
    advanced_settings_title.addWidget(QLabel("<b>Advanced Settings</b>"))
    advanced_settings_title.addStretch()
    vbox.addLayout(advanced_settings_title)

    hbox_ntx = QHBoxLayout()
    hbox_ntx.addWidget(QLabel("Default number of Txs"))
    num_txs_e = QLineEdit()
    num_txs_e.setText(str(window.config.get('bitpost_num_txs')))
    hbox_ntx.addWidget(num_txs_e)
    help_button__num_txs = QPushButton("?")
    help_button__num_txs.clicked.connect(
        lambda: d.show_message(HelpTexts.num_txs))
    hbox_ntx.addWidget(help_button__num_txs)
    vbox.addLayout(hbox_ntx)

    broadcast_policy = QHBoxLayout()
    broadcast_policy.addWidget(QLabel("First broadcast policy"))

    broadcast_policy_combo = QComboBox()
    # 'Broadcast lowest fee transaction immediatly'
    broadcast_policy_options = [
        'Don\'t delay first broadcast', 'Allow delay of first broadcast'
    ]
    if window.config.get('bitpost_delay') == 1:
        broadcast_policy_options.reverse()

    broadcast_policy_combo.addItems(broadcast_policy_options)
    broadcast_policy.addWidget(broadcast_policy_combo)

    help_button__broadcast_policy = QPushButton("?")
    help_button__broadcast_policy.clicked.connect(
        lambda: d.show_message(HelpTexts.delay))
    broadcast_policy.addWidget(help_button__broadcast_policy)
    vbox.addLayout(broadcast_policy)

    vbox.addStretch()
    vbox.addLayout(Buttons(CloseButton(d), OkButton(d)))

    if not d.exec_():
        return

    window.config.set_key('bitpost_max_fee_unit', fee_combo.currentText())
    delay = 1 if broadcast_policy_combo.currentText(
    ) == 'Allow delay of first broadcast' else 0
    window.config.set_key('bitpost_delay', delay)
    platform_text = platform_combo.currentText()

    window.config.set_key('bitpost_notification_platform', platform_text)

    subscriptions = []
    if overdue_checkbox.isChecked():
        subscriptions.append({'name': 'overdue'})
    if mined_checkbox.isChecked():
        subscriptions.append({'name': 'mine'})
    if max_fee_reached_checkbox.isChecked():
        subscriptions.append({'name': 'reached'})
    if reorg_checkbox.isChecked():
        subscriptions.append({'name': 'orphaned_block'})
    if orphaned_checkbox.isChecked():
        pass  # TODO
    window.config.set_key('bitpost_notification_subscriptions', subscriptions)

    try:
        window.config.set_key('bitpost_max_fee', float(max_fees_e.text()))
    except:
        d.show_error('Invalid maximum fee, must be a number.')
        create_settings_window(small_window)

    try:
        window.config.set_key('bitpost_num_txs', int(num_txs_e.text()))
    except:
        d.show_error(
            'Invalid default number of transactions, must be an integer')
        create_settings_window(small_window)

    if not valid_address(platform_combo.currentText(), address_input.text()):
        d.show_error('Invalid handle/address for ' +
                     platform_combo.currentText())
        create_settings_window(small_window)

    window.config.set_key('bitpost_notification_address', address_input.text())