Beispiel #1
0
    def create_menu(self, position):
        menu = QMenu()
        selected = self.selectedItems()
        if not selected:
            menu.addAction(_("New contact"), lambda: self.parent.new_contact_dialog())
            menu.addAction(_("Import file"), lambda: self.import_contacts())
            menu.addAction(_("Export file"), lambda: self.export_contacts())
        else:
            names = [item.text(0) for item in selected]
            keys = [item.text(1) for item in selected]
            column = self.currentColumn()
            column_title = self.headerItem().text(column)
            column_data = '\n'.join([item.text(column) for item in selected])
            menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data))
            if column in self.editable_columns:
                item = self.currentItem()
                menu.addAction(_("Edit {}").format(column_title), lambda: self.editItem(item, column))
            menu.addAction(_("Pay to"), lambda: self.parent.payto_contacts(keys))
            menu.addAction(_("Delete"), lambda: self.parent.delete_contacts(keys))
            URLs = [block_explorer_URL(self.config, 'addr', Address.from_string(key))
                    for key in keys if Address.is_valid(key)]
            if URLs:
                menu.addAction(_("View on block explorer"), lambda: [webbrowser.open(URL) for URL in URLs])

        run_hook('create_contact_menu', menu, selected)
        menu.exec_(self.viewport().mapToGlobal(position))
    def on_update(self):
        self.wallet = self.parent.wallet
        self.chkVisible()

        # update the receive address if necessary
        current_address_string = self.parent.receive_address_e.text()
        current_address = Address.from_string(current_address_string)
        domain = self.wallet.get_receiving_addresses()
        addr = self.wallet.get_unused_address()
        if not current_address in domain and addr:
            self.parent.set_receive_address(addr)

        # clear the list and fill it again
        item = self.currentItem()
        prev_sel = item.data(0, Qt.UserRole) if item else None
        self.clear()
        for req in self.wallet.get_sorted_requests(self.config):
            address = req['address']
            if address not in domain:
                continue
            timestamp = req.get('time', 0)
            amount = req.get('amount')
            expiration = req.get('exp', None)
            message = req.get('memo', '')
            date = format_time(timestamp)
            status = req.get('status')
            signature = req.get('sig')
            requestor = req.get('name', '')
            token_id = req.get('token_id', None)
            if token_id:
                amount_str = str(amount) if amount else ""
                item = QTreeWidgetItem([
                    date,
                    address.to_ui_string(), '', message, amount_str,
                    _(pr_tooltips.get(status, ''))
                ])
            else:
                amount_str = self.parent.format_amount(
                    amount) if amount else ""
            item = QTreeWidgetItem([
                date,
                address.to_ui_string(), '', message, amount_str,
                pr_tooltips.get(status, '')
            ])
            item.setData(0, Qt.UserRole, address)
            if signature is not None:
                item.setIcon(2, self.icon_cache.get(":icons/seal.png"))
                item.setToolTip(2, 'signed by ' + requestor)
            if status is not PR_UNKNOWN:
                item.setIcon(6, self.icon_cache.get(pr_icons.get(status)))
            self.addTopLevelItem(item)
            if prev_sel == address:
                self.setCurrentItem(item)
Beispiel #3
0
 def parse_address(self, address):
     if constants.net.SLPADDR_PREFIX not in address:
         address = constants.net.SLPADDR_PREFIX + ":" + address
     return Address.from_string(address)
Beispiel #4
0
    def sign_txns(self):

        # set all file Metadata to None for now... UI needs updated for this
        self.metadata = { 'filename': None, 'fileext': None, 'filesize': None, 'file_sha256': None, 'prev_file_sha256': None, 'uri': None }
        self.metadata['prev_file_sha256'] = self.prev_hash.text()

        if self.prev_hash.text() != '':
            if len(self.prev_hash.text()) != 64:
                self.show_message(_("Previous document hash must be a 32 byte hexidecimal string or left empty."))
                return

        if self.file_org_addr_e.text() != '':
            try: 
                Address.from_string(self.file_org_addr_e.text())
            except Base58Error:
                self.show_message(_("Originating address checksum fails."))
                return

        if self.file_receiver_e.text() != '':
            try: 
                #addr = Address.from_string(self.file_receiver_e.text())
                Address.from_string(self.file_receiver_e.text())
            except Base58Error:
                self.show_message(_("Receiver address checksum fails."))
                return
            
            self.file_receiver = Address.from_string(self.file_receiver_e.text())
        else:
            self.file_receiver = None
    
        if self.filename != '':
            self.select_file_button.setDefault(False)
            with open(self.filename,"rb") as f:

                # clear fields before re-populating
                self.hash.setText('')
                self.path.setText('')
                self.upload_cost_label.setText('')
                self.bitcoinfileAddr_label.setText('')

                bytes = f.read()
                if len(bytes) > 5261:
                    self.show_error("Files cannot be larger than 5.261kB in size.")
                    return
                import hashlib
                readable_hash = hashlib.sha256(bytes).hexdigest()
                self.hash.setText(readable_hash)
                self.path.setText(self.filename)
                self.metadata['filesize'] = len(bytes)
                try:
                    self.metadata['filename'] = basename(self.filename).split(os.extsep, 1)[0]
                    self.metadata['fileext'] = basename(self.filename).split(os.extsep, 1)[1]
                except IndexError:
                    pass
                self.metadata['file_sha256'] = readable_hash
                cost = calculateUploadCost(len(bytes), self.metadata)
                self.upload_cost_label.setText(str(cost))
                if(self.org_addr_cb.isChecked and self.file_org_addr_e.text() != ''):
                    addr = Address.from_string(self.file_org_addr_e.text())
                elif self.parent.wallet.get_unused_address():
                    addr = self.parent.wallet.get_unused_address()
                else: 
                    addr = self.parent.wallet.get_addresses()[0]

                # # IMPORTANT: set wallet.send_slpTokenId to None to guard tokens during this transaction
                if self.main_window.is_slp_wallet:
                    self.main_window.token_type_combo.setCurrentIndex(0)
                    assert self.main_window.slp_token_id is None

                try:
                    self.tx_batch.append(getFundingTxn(self.parent.wallet, addr, cost, self.parent.config))
                    self.progress_label.setText('')
                except NotEnoughFunds:
                    self.show_message("Insufficient funds.\n\nYou must have a CONFIRMED balance of at least: " + str(cost) + " satoshis.")
                    self.progress_label.setText('')
                    self.filename = None
                    self.make_dirty()
                    return

                # Rewind and put file into chunks
                f.seek(0, 0)
                chunks = []
                while True:
                    b = f.read(220)
                    if b == b'': break
                    try:
                        chunks.append(b)
                        self.chunks_total += 1
                    except ValueError:
                        break

                min_len = 223 - len(make_bitcoinfile_metadata_opreturn(1, 0, None, self.metadata['filename'], self.metadata['fileext'], self.metadata['filesize'], self.metadata['file_sha256'], self.metadata['prev_file_sha256'], self.metadata['uri'])[1].to_script())
                
                # determine if the metadata txn data chunk will be empty for progress bar accuracy
                if len(bytes) < 220:
                    chunk_count_adder = 1 if len(bytes) > min_len else 0
                else:
                    chunk_count_adder = 1 if min_len - (len(bytes) % 220) < 0 else 0

                self.progress.setMaximum(len(chunks) + chunk_count_adder + 1)
                self.progress.setMinimum(0)
                self.progress.setVisible(True)
                self.progress_label.setText("Signing 1 of " + str(len(chunks) + chunk_count_adder + 1) + " transactions")
                
                # callback to recursive sign next txn or finish
                def sign_done(success):
                    if success:
                        self.tx_batch_signed_count += 1
                        self.progress.setValue(self.tx_batch_signed_count)
                        self.activateWindow()
                        self.raise_()
                        self.progress_label.setText("Signing " + str(self.tx_batch_signed_count + 1) + " of " + str(len(chunks) + chunk_count_adder + 1) + " transactions")
                        if self.chunks_processed <= self.chunks_total and not self.final_metadata_txn_created:
                            try:
                                chunk_bytes = chunks[self.chunks_processed]
                            except IndexError:
                                chunk_bytes = None
                            # try:
                            txn, self.final_metadata_txn_created = getUploadTxn(self.parent.wallet, self.tx_batch[self.chunks_processed], self.chunks_processed, self.chunks_total, chunk_bytes, self.parent.config, self.metadata, self.file_receiver)
                            self.tx_batch.append(txn)
                            # except NotEnoughFunds as e:
                            #     self.show_message("Insufficient funds for file chunk #" + str(self.chunks_processed + 1))
                            #     return
                            self.chunks_processed += 1

                        if self.tx_batch_signed_count < len(self.tx_batch):
                            self.sign_tx_with_password(self.tx_batch[self.tx_batch_signed_count], sign_done, self.password)
                        else:
                            uri = "bitcoinfile:" + self.tx_batch[len(self.tx_batch)-1].txid()
                            self.bitcoinfileAddr_label.setText(uri)
                            self.progress_label.setText("Signing complete. Ready to upload.")
                            self.progress.setHidden(True)
                            self.is_dirty = False
                            self.progress.setValue(0)
                            self.sign_button.setDisabled(True)
                            self.upload_button.setEnabled(True)
                            self.upload_button.setDefault(True)
                            self.activateWindow()
                            self.raise_()
                self.sign_tx_with_password(self.tx_batch[0], sign_done, self.password)
Beispiel #5
0
 def parse_address(self, line):
     r = line.strip()
     m = re.match('^' + RE_ALIAS + '$', r)
     address = m.group(2) if m else r
     return Address.from_string(address)
Beispiel #6
0
    def check_text(self):
        self.errors = []
        if self.is_pr:
            return
        # filter out empty lines
        lines = [i for i in self.lines() if i]
        outputs = []
        total = 0
        self.payto_address = None
        if len(lines) == 1:
            data = lines[0]
            if ':' in data and '?' in data and len(data) > 35:
                try:
                    self.scan_f(data)
                except AddressError as e:
                    self.errors.append((0, str(e)))
                else:
                    return
            elif ':' not in data and len(data) > 35:
                self.setText(
                    Address.prefix_from_address_string(data) + ':' + data)
                return
            try:
                self.parse_address(data)
            except Exception as e:
                self.errors.append((0, str(e)))
            try:
                self.payto_address = self.parse_output(data)
                self.address_string_for_slp_check = data
            except:
                self.address_string_for_slp_check = ''
            if self.payto_address:
                self.win.lock_amount(False)
                return

        is_max = False
        for i, line in enumerate(lines):
            try:
                _type, to_address, amount = self.parse_address_and_amount(line)
            except Exception as e:
                if len(self.errors) < 1:
                    self.errors.append((i, line.strip()))
                continue

            outputs.append((_type, to_address, amount))
            if amount == '!':
                is_max = True
            else:
                total += amount

        self.win.is_max = is_max
        self.outputs = outputs
        self.payto_address = None

        if self.win.is_max:
            self.win.do_update_fee()
        else:
            """
            The following line is commented out for SLP.
            If this line is not commented out then the amount field will
            always be reset to 0 when the address text is edited.
            For SLP, an amount of 546 sat should be left in the amount_e field.
            """
            #self.amount_edit.setAmount(total if outputs else None)
            self.win.lock_amount(total or len(lines) > 1)
Beispiel #7
0
    def __init__(self, main_window, *, nft_parent_id=None):
        #self.provided_token_name = token_name
        # We want to be a top-level window
        QDialog.__init__(self, parent=None)
        from .main_window import ElectrumWindow

        assert isinstance(main_window, ElectrumWindow)
        main_window._slp_dialogs.add(self)
        finalization_print_error(self)  # track object lifecycle

        self.main_window = main_window
        self.wallet = main_window.wallet
        self.config = main_window.config
        self.network = main_window.network
        self.app = main_window.app
        self.nft_parent_id = nft_parent_id
        if nft_parent_id != None:
            self.token_type = 65
        else:
            self.token_type = 1

        if self.main_window.gui_object.warn_if_no_network(self.main_window):
            return

        self.setWindowTitle(_("Create a New Token"))

        vbox = QVBoxLayout()
        self.setLayout(vbox)

        grid = QGridLayout()
        grid.setColumnStretch(1, 1)
        vbox.addLayout(grid)
        row = 0

        msg = _(
            'An optional name string embedded in the token genesis transaction.'
        )
        grid.addWidget(HelpLabel(_('Token Name (optional):'), msg), row, 0)
        self.token_name_e = QLineEdit()
        grid.addWidget(self.token_name_e, row, 1)
        row += 1

        msg = _(
            'An optional ticker symbol string embedded into the token genesis transaction.'
        )
        grid.addWidget(HelpLabel(_('Ticker Symbol (optional):'), msg), row, 0)
        self.token_ticker_e = QLineEdit()
        self.token_ticker_e.setFixedWidth(110)
        self.token_ticker_e.textChanged.connect(self.upd_token)
        grid.addWidget(self.token_ticker_e, row, 1)
        row += 1

        msg = _(
            'An optional URL string embedded into the token genesis transaction.'
        )
        grid.addWidget(HelpLabel(_('Document URL (optional):'), msg), row, 0)
        self.token_url_e = QLineEdit()
        self.token_url_e.setFixedWidth(560)
        self.token_url_e.textChanged.connect(self.upd_token)
        grid.addWidget(self.token_url_e, row, 1)
        row += 1

        msg = _('An optional hash hexidecimal bytes embedded into the token genesis transaction for hashing') \
                                                    + 'the document file contents at the URL provided above.'
        grid.addWidget(HelpLabel(_('Document Hash (optional):'), msg), row, 0)
        self.token_dochash_e = QLineEdit()
        self.token_dochash_e.setInputMask(
            "HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH")
        self.token_dochash_e.setFixedWidth(560)
        self.token_dochash_e.textChanged.connect(self.upd_token)
        grid.addWidget(self.token_dochash_e, row, 1)
        row += 1

        msg = _('Sets the number of decimals of divisibility for this token (embedded into genesis).') \
                + '\n\n' \
                + _('Each 1 token is divisible into 10^(decimals) base units, and internally in the protocol') \
                + _('the token amounts are represented as 64-bit integers measured in these base units.')
        grid.addWidget(HelpLabel(_('Decimal Places:'), msg), row, 0)
        self.token_ds_e = QDoubleSpinBox()
        self.token_ds_e.setRange(0, 9)
        self.token_ds_e.setDecimals(0)
        self.token_ds_e.setFixedWidth(50)
        self.token_ds_e.valueChanged.connect(self.upd_token)
        grid.addWidget(self.token_ds_e, row, 1)
        row += 1

        msg = _('The number of tokens created during token genesis transaction,') \
                                    + _('send to the receiver address provided below.')
        if nft_parent_id == None:
            self.token_qty_label = HelpLabel(_('Token Quantity:'), msg)
        else:
            self.token_qty_label = HelpLabel(_('Number of Child NFTs:'), msg)
        grid.addWidget(self.token_qty_label, row, 0)
        self.token_qty_e = SLPAmountEdit('', 0)
        self.token_qty_e.setFixedWidth(200)
        self.token_qty_e.textChanged.connect(self.check_token_qty)
        grid.addWidget(self.token_qty_e, row, 1)
        row += 1

        msg = _(
            'The \'simpleledger:\' formatted bitcoin address for the genesis receiver of all genesis tokens.'
        )
        grid.addWidget(HelpLabel(_('Token Receiver Address:'), msg), row, 0)
        self.token_pay_to_e = ButtonsLineEdit()
        self.token_pay_to_e.setFixedWidth(560)
        grid.addWidget(self.token_pay_to_e, row, 1)
        try:
            slpAddr = self.wallet.get_unused_address().to_slpaddr()
            self.token_pay_to_e.setText(
                Address.prefix_from_address_string(slpAddr) + ":" + slpAddr)
        except:
            pass
        row += 1

        self.token_fixed_supply_cb = cb = QCheckBox(_('Fixed Supply'))
        self.token_fixed_supply_cb.setChecked(True)
        grid.addWidget(self.token_fixed_supply_cb, row, 1)
        cb.clicked.connect(self.show_mint_baton_address)
        row += 1

        self.token_nft_parent_cb = cb = QCheckBox(_('Is NFT Parent?'))
        self.token_nft_parent_cb.setChecked(False)
        grid.addWidget(self.token_nft_parent_cb, row, 1)
        cb.clicked.connect(self.token_nft_parent_cb_update)
        row += 1

        if self.nft_parent_id:
            self.token_nft_parent_cb.setDisabled(True)
            self.token_fixed_supply_cb.setDisabled(True)
            self.token_qty_e.setAmount(1)
            self.token_qty_e.setDisabled(True)
            self.token_ds_e.setDisabled(True)

        msg = _('The \'simpleledger:\' formatted bitcoin address for the "minting baton" receiver.') + '\n\n' \
                + _('After the genesis transaction, further unlimited minting operations can be performed by the owner of') \
                + ' the "minting baton" transaction output. This baton can be repeatedly used for minting operations but' \
                + ' it cannot be duplicated.'
        self.token_baton_label = HelpLabel(_('Address for Baton:'), msg)
        self.token_baton_label.setHidden(True)
        grid.addWidget(self.token_baton_label, row, 0)
        self.token_baton_to_e = ButtonsLineEdit()
        self.token_baton_to_e.setFixedWidth(560)
        self.token_baton_to_e.setHidden(True)
        grid.addWidget(self.token_baton_to_e, row, 1)
        row += 1

        if nft_parent_id:
            nft_parent_coin = get_nft_parent_coin(nft_parent_id, main_window)
            if not get_nft_parent_coin(nft_parent_id, main_window):
                hbox2 = QHBoxLayout()
                vbox.addLayout(hbox2)
                warnpm = QIcon(":icons/warning.png").pixmap(20, 20)
                self.warn1 = l = QLabel()
                l.setPixmap(warnpm)
                hbox2.addWidget(l)
                self.warn_msg = msg = QLabel(
                    _("     NOTE: The parent token needs to be split before a new NFT can be created, click 'Prepare Group Parent'."
                      ))
                hbox2.addWidget(msg)
                self.warn2 = l = QLabel()
                l.setPixmap(warnpm)
                hbox2.addStretch(1)
                hbox2.addWidget(l)

        hbox = QHBoxLayout()
        vbox.addLayout(hbox)

        self.cancel_button = b = QPushButton(_("Cancel"))
        self.cancel_button.setAutoDefault(False)
        self.cancel_button.setDefault(False)
        b.clicked.connect(self.close)
        hbox.addWidget(self.cancel_button)

        hbox.addStretch(1)

        # self.hash_button = b = QPushButton(_("Compute Document Hash..."))
        # self.hash_button.setAutoDefault(False)
        # self.hash_button.setDefault(False)
        # b.clicked.connect(self.hash_file)
        # b.setDefault(True)
        # hbox.addWidget(self.hash_button)

        # self.tok_doc_button = b = QPushButton(_("Upload a Token Document..."))
        # self.tok_doc_button.setAutoDefault(False)
        # self.tok_doc_button.setDefault(False)
        # b.clicked.connect(self.show_upload)
        # b.setDefault(True)
        # hbox.addWidget(self.tok_doc_button)

        self.preview_button = EnterButton(_("Preview"), self.do_preview)
        hbox.addWidget(self.preview_button)

        self.create_button = b = QPushButton(
            _("Create New Token"
              ))  #if self.provided_token_name is None else _("Change"))
        b.clicked.connect(self.create_token)
        self.create_button.setAutoDefault(True)
        self.create_button.setDefault(True)
        hbox.addWidget(self.create_button)

        if nft_parent_id:
            self.create_button.setText("Create NFT")
            if not nft_parent_coin:
                self.create_button.setHidden(True)
                self.prepare_parent_bttn = QPushButton(
                    _("Prepare Group Parent"))
                self.prepare_parent_bttn.clicked.connect(
                    self.prepare_nft_parent)
                self.prepare_parent_bttn.setAutoDefault(True)
                self.prepare_parent_bttn.setDefault(True)
                hbox.addWidget(self.prepare_parent_bttn)
                self.token_name_e.setDisabled(True)
                self.token_ticker_e.setDisabled(True)
                self.token_url_e.setDisabled(True)
                self.token_dochash_e.setDisabled(True)
                self.token_pay_to_e.setDisabled(True)
                self.token_baton_to_e.setDisabled(True)

        dialogs.append(self)
        self.show()
        self.token_name_e.setFocus()