コード例 #1
0
ファイル: accounts_view.py プロジェクト: wagnehc/electrumsv
    def add_menu_items(self, menu: QMenu, account: AbstractAccount, main_window: ElectrumWindow) \
            -> None:
        menu.clear()

        # This expects a reference to the main window, not the weakref.
        account_id = account.get_id()

        menu.addAction(_("&Information"),
                       partial(self._show_account_information, account_id))
        seed_menu = menu.addAction(
            _("View &Secured Data"),
            partial(self._view_secured_data,
                    main_window=main_window,
                    account_id=account_id))
        seed_menu.setEnabled(self._can_view_secured_data(account))
        menu.addAction(_("&Rename"), partial(self._rename_account, account_id))
        menu.addSeparator()

        private_keys_menu = menu.addMenu(_("&Private keys"))
        import_menu = private_keys_menu.addAction(
            _("&Import"),
            partial(self._import_privkey,
                    main_window=main_window,
                    account_id=account_id))
        import_menu.setEnabled(account.can_import_privkey())
        export_menu = private_keys_menu.addAction(
            _("&Export"),
            partial(self._export_privkeys,
                    main_window=main_window,
                    account_id=account_id))
        export_menu.setEnabled(account.can_export())
        if account.can_import_address():
            menu.addAction(_("Import addresses"),
                           partial(self._import_addresses, account_id))

        menu.addSeparator()

        hist_menu = menu.addMenu(_("&History"))
        hist_menu.addAction("Export", main_window.export_history_dialog)

        labels_menu = menu.addMenu(_("&Labels"))
        action = labels_menu.addAction(
            _("&Import"), partial(self._on_menu_import_labels, account_id))
        labels_menu.addAction(_("&Export"),
                              partial(self._on_menu_export_labels, account_id))

        invoices_menu = menu.addMenu(_("Invoices"))
        self._import_invoices_action = invoices_menu.addAction(
            _("Import"), partial(self._on_menu_import_invoices, account_id))
        self._import_invoices_action.setEnabled(
            main_window.is_send_view_active())

        payments_menu = menu.addMenu(_("Payments"))
        ed_action = payments_menu.addAction(
            _("Export destinations"),
            partial(self._generate_destinations, account_id))
        keystore = account.get_keystore()
        ed_action.setEnabled(
            keystore is not None
            and keystore.type() != KeystoreType.IMPORTED_PRIVATE_KEY)
コード例 #2
0
 def show_send_to_cosigner_button(self, account: AbstractAccount,
                                  tx: Transaction) -> bool:
     if tx.is_complete() or account.can_sign(tx):
         return False
     account_id = account.get_id()
     return any(
         self._is_theirs(account_id, item, tx) for item in self._items)
コード例 #3
0
 async def _broadcast_transaction(self, rawtx: str, tx_hash: bytes,
                                  account: AbstractAccount):
     result = await self.send_request('blockchain.transaction.broadcast',
                                      [rawtx])
     account.maybe_set_transaction_dispatched(tx_hash)
     self.logger.debug("successful broadcast for %s", result)
     return result
コード例 #4
0
 def get_and_set_frozen_utxos_for_tx(self, tx: Transaction, child_wallet: AbstractAccount,
                                     freeze: bool=True) -> List[UTXO]:
     spendable_coins = child_wallet.get_utxos(exclude_frozen=False)
     input_keys = set(
         [(bitcoinx.hash_to_hex_str(input.prev_hash), input.prev_idx) for input in tx.inputs])
     frozen_utxos = [utxo for utxo in spendable_coins if utxo.key() in input_keys]
     child_wallet.set_frozen_coin_state(frozen_utxos, freeze)
     return frozen_utxos
コード例 #5
0
ファイル: cosigner_pool.py プロジェクト: wagnehc/electrumsv
 def show_send_to_cosigner_button(self, window: 'ElectrumWindow', account: AbstractAccount,
         tx: Transaction) -> bool:
     if window.network is None:
         return False
     if tx.is_complete() or account.can_sign(tx):
         return False
     account_id = account.get_id()
     return any(self._is_theirs(window, account_id, item, tx) for item in self._items)
コード例 #6
0
 async def _broadcast_transaction(self, rawtx: str, tx_hash: bytes,
                                  account: AbstractAccount):
     result = await self.send_request('blockchain.transaction.broadcast',
                                      [rawtx])
     account.set_transaction_state(tx_hash=tx_hash,
                                   flags=(TxFlags.StateDispatched
                                          | TxFlags.HasByteData))
     self.logger.debug("successful broadcast for %s", result)
     return result
コード例 #7
0
    def remove_signed_transaction(self, tx: Transaction, wallet: AbstractAccount):
        # must remove signed transactions after a failed broadcast attempt (to unlock utxos)
        # if it's a re-broadcast attempt (same txid) and we already have a StateDispatched or
        # StateCleared transaction then *no deletion* should occur
        tx_hash = tx.hash()
        signed_tx = wallet.get_transaction(tx_hash, flags=TxFlags.StateSigned)

        if signed_tx:
            wallet.delete_transaction(tx_hash)
コード例 #8
0
ファイル: importers.py プロジェクト: cherubtorch/electrumsv
 def _get_addresses(klass,
                    account: AbstractAccount) -> Dict[ScriptTemplate, int]:
     script_type = ScriptType.P2PKH
     if isinstance(account, MultisigAccount):
         script_type = ScriptType.MULTISIG_P2SH
     result: Dict[ScriptTemplate, int] = {}
     for keyinstance_id in account.get_keyinstance_ids():
         template = account.get_script_template_for_id(
             keyinstance_id, script_type)
         result[template] = keyinstance_id
     return result
コード例 #9
0
ファイル: keepkey.py プロジェクト: CallAndGus/electrumsv
 def show_key(self, account: AbstractAccount, keyinstance_id: int) -> None:
     keystore = cast(KeepKey_KeyStore, account.get_keystore())
     client = self.get_client(keystore)
     derivation_path = account.get_derivation_path(keyinstance_id)
     assert derivation_path is not None
     subpath = '/'.join(str(x) for x in derivation_path)
     address_path = f"{keystore.derivation}/{subpath}"
     address_n = bip32_decompose_chain_string(address_path)
     script_type = self.types.SPENDADDRESS
     client.get_address(Net.KEEPKEY_DISPLAY_COIN_NAME, address_n,
                        True, script_type=script_type)
コード例 #10
0
    def _on_account_change(self, new_account_id: int, new_account: AbstractAccount) -> None:
        self._account_id = new_account_id
        self._account = new_account

        script_type = new_account.get_default_script_type()

        # Hardware wallets will not sign OP_FALSE OP_RETURN.
        self._direct_splitting_enabled = self._account.is_deterministic() and \
            new_account.can_spend() and \
            not new_account.involves_hardware_wallet()
        # The faucet requires an address to send to. There are only P2PKH addresses.
        self._faucet_splitting_enabled = self._account.is_deterministic() and \
          script_type == ScriptType.P2PKH
        self.update_layout()
コード例 #11
0
ファイル: label_sync.py プロジェクト: spilop/electrumsv
    def pull_thread(self, account: AbstractAccount,
                    force: bool) -> Optional[Any]:
        account_data = self._accounts.get(account, None)
        if not account_data:
            raise Exception('Account {} not loaded'.format(account))

        wallet_id = account_data[2]
        nonce = 1 if force else self.get_nonce(account) - 1
        logger.debug(f"asking for labels since nonce {nonce}")
        response = self.do_request("GET", ("/labels/since/%d/for/%s" %
                                           (nonce, wallet_id)))
        if response["labels"] is None:
            logger.debug('no new labels')
            return
        result = {}
        for label in response["labels"]:
            try:
                key = self.decode(account, label["externalId"])
                value = self.decode(account, label["encryptedLabel"])
            except Exception:
                continue
            try:
                json.dumps(key)
                json.dumps(value)
            except Exception:
                logger.error(f'no json {key}')
                continue
            result[key] = value

        logger.info(f"received {len(result):,d} labels")

        updates = {}
        for key, value in result.items():
            # TODO(rt12) BACKLOG there is no account.labels any more.
            if force or not account.labels.get(key):
                updates[key] = value

        if DISABLE_INTEGRATION:
            return updates

        if len(updates):
            # TODO(rt12) BACKLOG there is no account.put or account storage at this time, or
            # even `account.labels`.
            account.labels.update(updates)
            # do not write to disk because we're in a daemon thread. The handed off writing to
            # the sqlite writer thread would achieve this.
            account.put('labels', account.labels)
        self.set_nonce(account, response["nonce"] + 1)
        self.on_pulled(account, updates)
コード例 #12
0
 def compare_key_path(account: AbstractAccount, txo_key: TxoKeyType,
                      leading_path: Sequence[int]) -> bool:
     utxo = account._utxos.get(txo_key)
     if utxo is not None:
         key_path = account.get_derivation_path(utxo.keyinstance_id)
         if key_path is not None and key_path[:len(leading_path
                                                   )] == leading_path:
             return True
     stxo_keyinstance_id = account._stxos.get(txo_key)
     if stxo_keyinstance_id is not None:
         key_path = account.get_derivation_path(stxo_keyinstance_id)
         if key_path is not None and key_path[:len(leading_path
                                                   )] == leading_path:
             return True
     return False
コード例 #13
0
def get_tx_status(account: AbstractAccount, tx_hash: bytes, height: int,
                  conf: int, timestamp: Union[bool, int]) -> TxStatus:
    if not account.have_transaction_data(tx_hash):
        return TxStatus.MISSING

    metadata = account.get_transaction_metadata(tx_hash)
    if metadata.position == 0:
        if height + COINBASE_MATURITY > account._wallet.get_local_height():
            return TxStatus.UNMATURED
    elif conf == 0:
        if height > 0:
            return TxStatus.UNVERIFIED
        return TxStatus.UNCONFIRMED

    return TxStatus.FINAL
コード例 #14
0
ファイル: importers.py プロジェクト: cherubtorch/electrumsv
 def _get_derivations(klass,
                      account: AbstractAccount) -> Dict[Sequence[int], int]:
     keypaths = account.get_key_paths()
     result: Dict[Sequence[int], int] = {}
     for keyinstance_id, derivation_path in keypaths.items():
         result[derivation_path] = keyinstance_id
     return result
コード例 #15
0
    def do_send(self, window: 'ElectrumWindow', account: AbstractAccount,
                tx: Transaction) -> None:
        def on_done(window, future):
            try:
                future.result()
            except Exception as exc:
                window.on_exception(exc)
            else:
                window.show_message('\n'.join((
                    _("Your transaction was sent to the cosigning pool."),
                    _("Open your cosigner wallet to retrieve it."),
                )))

        def send_message():
            server.put(item.keyhash_hex, message)

        account_id = account.get_id()
        for item in self._items:
            if self._is_theirs(window, account_id, item, tx):
                raw_tx_bytes = json.dumps(tx.to_dict()).encode()
                public_key = PublicKey.from_bytes(item.pubkey_bytes)
                message = public_key.encrypt_message_to_base64(raw_tx_bytes)
                WaitingDialog(item.window,
                              _('Sending transaction to cosigning pool...'),
                              send_message,
                              on_done=partial(on_done, item.window))
コード例 #16
0
    def account_widgets(self, account: AbstractAccount):
        label = QLabel(
            _("The settings below only affect the account '{}'").format(
                account.display_name()))

        script_type_combo = QComboBox()

        def update_script_types():
            default_script_type = account.get_default_script_type()
            combo_items = [v.name for v in account.get_valid_script_types()]

            script_type_combo.clear()
            script_type_combo.addItems(combo_items)
            script_type_combo.setCurrentIndex(
                script_type_combo.findText(default_script_type.name))

        def on_script_type_change(index):
            script_type_name = script_type_combo.currentText()
            new_script_type = getattr(ScriptType, script_type_name)
            current_script_type = account.get_default_script_type()
            if current_script_type == new_script_type:
                return
            account.set_default_script_type(new_script_type)
            self._main_window.update_receive_address_widget()

        script_type_combo.currentIndexChanged.connect(on_script_type_change)

        update_script_types()

        return [
            (label, ),
            (script_type_combo, ),
        ]
コード例 #17
0
 def _fetch_transaction_dto(self, account: AbstractAccount,
                            tx_id) -> Optional[Dict]:
     tx_hash = hex_str_to_hash(tx_id)
     tx = account.get_transaction(tx_hash)
     if not tx:
         raise Fault(Errors.TRANSACTION_NOT_FOUND_CODE,
                     Errors.TRANSACTION_NOT_FOUND_MESSAGE)
     return {"tx_hex": tx.to_hex()}
コード例 #18
0
 def remove_transaction(self, tx_hash: bytes, wallet: AbstractAccount):
     # removal of txs that are not in the StateSigned tx state is disabled for now as it may
     # cause issues with expunging utxos inadvertently.
     try:
         tx = wallet.get_transaction(tx_hash)
         tx_flags = wallet._wallet._transaction_cache.get_flags(tx_hash)
         is_signed_state = (tx_flags
                            & TxFlags.StateSigned) == TxFlags.StateSigned
         # Todo - perhaps remove restriction to StateSigned only later (if safe for utxos state)
         if tx and is_signed_state:
             wallet.delete_transaction(tx_hash)
         if tx and not is_signed_state:
             raise Fault(Errors.DISABLED_FEATURE_CODE,
                         Errors.DISABLED_FEATURE_MESSAGE)
     except MissingRowError:
         raise Fault(Errors.TRANSACTION_NOT_FOUND_CODE,
                     Errors.TRANSACTION_NOT_FOUND_MESSAGE)
コード例 #19
0
ファイル: label_sync.py プロジェクト: spilop/electrumsv
 def get_nonce(self, account: AbstractAccount) -> int:
     # nonce is the nonce to be used with the next change
     if DISABLE_INTEGRATION:
         return 1
     # TODO BACKLOG there is no working account get/set
     nonce = account.get('wallet_nonce', None)
     if nonce is None:
         nonce = 1
         self.set_nonce(account, nonce)
     return nonce
コード例 #20
0
    def _transaction_state_dto(self, wallet: AbstractAccount,
        tx_ids: Optional[Iterable[str]]=None) -> Union[Fault, Dict[Any, Any]]:
        chain = self.app_state.daemon.network.chain()

        result = {}
        for tx_id in tx_ids:
            tx_hash = hex_str_to_hash(tx_id)
            if wallet.has_received_transaction(tx_hash):
                # height, conf, timestamp
                height, conf, timestamp = wallet.get_tx_height(tx_hash)
                block_id = None
                if timestamp:
                    block_id = self.app_state.headers.header_at_height(chain, height).hex_str()
                result[tx_id] = {
                    "block_id": block_id,
                    "height": height,
                    "conf": conf,
                    "timestamp": timestamp,
                }
        return result
コード例 #21
0
ファイル: label_sync.py プロジェクト: spilop/electrumsv
    def start_account(self, account: AbstractAccount) -> None:
        nonce = self.get_nonce(account)
        logger.debug("Account %s nonce is %s", account.name(), nonce)
        mpk = ''.join(sorted(account.get_master_public_keys()))
        if not mpk:
            return
        mpk = mpk.encode('ascii')
        password = hashlib.sha1(mpk).hexdigest()[:32].encode('ascii')
        iv = hashlib.sha256(password).digest()[:16]
        wallet_id = hashlib.sha256(mpk).hexdigest()
        self._accounts[account] = (password, iv, wallet_id)

        if DISABLE_INTEGRATION:
            return

        # If there is an auth token we can try to actually start syncing
        t = threading.Thread(target=self.pull_thread_safe,
                             args=(account, False))
        t.setDaemon(True)
        t.start()
コード例 #22
0
    def account_widgets(self, account: AbstractAccount, tab: QWidget) -> None:
        label = QLabel(
            _("The settings below only affect the account '{}'").format(
                account.display_name()))

        script_type_combo = QComboBox()

        def update_script_types():
            default_script_type = account.get_default_script_type()
            combo_items = [v.name for v in account.get_valid_script_types()]

            script_type_combo.clear()
            script_type_combo.addItems(combo_items)
            script_type_combo.setCurrentIndex(
                script_type_combo.findText(default_script_type.name))

        def on_script_type_change(index):
            script_type_name = script_type_combo.currentText()
            new_script_type = getattr(ScriptType, script_type_name)
            current_script_type = account.get_default_script_type()
            if current_script_type == new_script_type:
                return
            account.set_default_script_type(new_script_type)
            self._main_window.update_receive_address_widget()

        script_type_combo.currentIndexChanged.connect(on_script_type_change)

        update_script_types()

        form = FormSectionWidget(minimum_label_width=120)
        form.add_title(_("Account: {}").format(account.display_name()))
        form.add_row(_("Default script type"), script_type_combo)

        vbox = QVBoxLayout()
        vbox.addWidget(form)
        vbox.addStretch(1)
        tab.setLayout(vbox)
コード例 #23
0
 def _add_account_to_list(self, account: AbstractAccount) -> None:
     account_id = account.get_id()
     item = QListWidgetItem()
     keystore = account.get_keystore()
     derivation_type = keystore.derivation_type if keystore is not None \
         else DerivationType.NONE
     is_watching_only = keystore.is_watching_only(
     ) if keystore is not None else True
     icon_state = "inactive" if is_watching_only else "active"
     if derivation_type == DerivationType.ELECTRUM_MULTISIG:
         tooltip_text = _("Multi-signature account")
         icon_filename = "icons8-group-task-80-blueui-{}.png"
     elif derivation_type == DerivationType.HARDWARE:
         tooltip_text = _("Hardware wallet account")
         icon_filename = "icons8-usb-2-80-blueui-{}.png"
     elif derivation_type == DerivationType.IMPORTED:
         # This should not be watch only as imported public keys have no keystore.
         tooltip_text = _("Imported private key account")
         icon_filename = "icons8-key-80-plus-blueui-{}.png"
     elif derivation_type == DerivationType.ELECTRUM_OLD:
         tooltip_text = _("Old-style Electrum account")
         icon_filename = "icons8-password-1-80-blueui-{}.png"
     elif derivation_type == DerivationType.BIP32:
         tooltip_text = _("BIP32 account")
         icon_filename = "icons8-grand-master-key-80-blueui-{}.png"
     else:
         # This should always be watch only as imported public keys have no keystore.
         tooltip_text = _("Imported public key account")
         icon_filename = "icons8-key-80-plus-blueui-{}.png"
     if is_watching_only:
         tooltip_text += f" ({_('watch only')})"
     item.setIcon(read_QIcon(icon_filename.format(icon_state)))
     item.setData(Qt.UserRole, account_id)
     item.setText(account.display_name())
     item.setToolTip(tooltip_text)
     self._selection_list.addItem(item)
     self._account_ids.append(account_id)
コード例 #24
0
 def _history_dto(self,
                  account: AbstractAccount,
                  tx_flags: int = None) -> List[Dict[Any, Any]]:
     result = []
     entries = account._wallet._transaction_cache.get_entries(mask=tx_flags)
     for tx_hash, entry in entries:
         tx_values = account._wallet.get_transaction_deltas(
             tx_hash, account.get_id())
         assert len(tx_values) == 1
         result.append({
             "txid": hash_to_hex_str(tx_hash),
             "height": entry.metadata.height,
             "tx_flags": entry.flags,
             "value": int(tx_values[0].total)
         })
     return result
コード例 #25
0
ファイル: importers.py プロジェクト: cherubtorch/electrumsv
    def parse_label_export_json(klass, account: AbstractAccount,
                                text: str) -> LabelImportResult:
        updates: Dict[str, Any] = json.loads(text)
        results = LabelImportResult(LabelImportFormat.ACCOUNT)
        for tx_id, label_text in updates.get("transactions", []):
            if len(tx_id) == 64:  # length of the transaction id (hex of hash)
                try:
                    tx_hash = hex_str_to_hash(tx_id)
                except (TypeError, ValueError):
                    pass
                else:
                    results.transaction_labels[tx_hash] = label_text
                    continue
            results.unknown_labels[tx_id] = label_text

        keydata: Optional[Dict[str, Any]] = updates.get("keys")
        if keydata is not None:
            account_fingerprint = account.get_fingerprint().hex()
            if isinstance(keydata.get("account_fingerprint"), str):
                results.account_fingerprint = keydata["account_fingerprint"]
            derivations = klass._get_derivations(account)
            for derivation_path_text, label_text in keydata["entries"]:
                try:
                    derivation_path = tuple(
                        bip32_decompose_chain_string(derivation_path_text))
                except (TypeError, ValueError):
                    pass
                else:
                    # We never import key descriptions if the account does not match.
                    if account_fingerprint == results.account_fingerprint:
                        keyinstance_id = derivations.get(derivation_path)
                        if keyinstance_id is not None:
                            results.key_labels[keyinstance_id] = label_text
                            continue
                results.unknown_labels[derivation_path_text] = label_text

        return results
コード例 #26
0
 def name_for_account(account: AbstractAccount) -> str:
     name = account.display_name()
     return f"{account.get_id()}: {name}"
コード例 #27
0
 def compare_key_path(account: AbstractAccount, keyinstance_id: int,
                      leading_path: Sequence[int]) -> bool:
     key_path = account.get_derivation_path(keyinstance_id)
     return key_path is not None and key_path[:len(leading_path
                                                   )] == leading_path
コード例 #28
0
ファイル: label_sync.py プロジェクト: spilop/electrumsv
 def set_nonce(self, account: AbstractAccount, nonce: int) -> None:
     logger.debug("set {} nonce to {}".format(account.name(), nonce))
     # TODO BACKLOG there is no working account get/set
     account.put("wallet_nonce", nonce)
コード例 #29
0
ファイル: label_sync.py プロジェクト: spilop/electrumsv
 def on_pulled(self, account: AbstractAccount, updates: Any) -> None:
     app_state.app.labels_changed_signal.emit(
         account._wallet.get_storage_path(), account.get_id(), updates)
コード例 #30
0
 def _can_view_secured_data(self, account: AbstractAccount) -> None:
     return not account.is_watching_only() and not isinstance(account, MultisigAccount) \
         and not account.is_hardware_wallet() \
         and account.type() != AccountType.IMPORTED_PRIVATE_KEY