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()
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()
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)
def receive_list_menu(self, menu, addr): window = get_parent_main_window(menu) menu.addAction(_("Send via e-mail"), lambda: self.send(window, addr))
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)
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())