mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Merge branch 'master' into design-revamp
This commit is contained in:
commit
2a487a83a7
9 changed files with 105 additions and 109 deletions
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
- [Specter Desktop](#specter-desktop)
|
||||
- [DISCLAIMER](#disclaimer)
|
||||
- [Video Walkthrough](#video-walkthrough)
|
||||
- [Documentation and Video Walkthrough](#documentation-and-video-walkthrough)
|
||||
- [Why?](#why)
|
||||
- [Help wanted: Do you like Specter?](#help-wanted-do-you-like-specter)
|
||||
- [How to run](#how-to-run)
|
||||
|
|
@ -27,7 +27,6 @@
|
|||
The Crypto Anarchist Manifesto - Timothy C. May - Sun, 22 Nov 92 12:11:24 PST
|
||||
|
||||
[](https://cirrus-ci.com/github/cryptoadvance/specter-desktop)
|
||||
[](https://donate.specter.solutions/apps/3k77BAT6zshCGNd3i7gw9WKwXQy1/pos)
|
||||
|
||||
## DISCLAIMER
|
||||
|
||||
|
|
@ -35,8 +34,10 @@ This software might be ready to be used but at your own risk.
|
|||
|
||||
If something doesn't work open an issue here or ask a question in our [Telegram group](https://t.me/spectersupport).
|
||||
|
||||
## Video Walkthrough
|
||||
 [Watch here](https://www.youtube.com/watch?v=v3SEp0SkOWs)
|
||||
## Documentation and Video Walkthrough
|
||||
|
||||
*  [Watch here](https://www.youtube.com/watch?v=v3SEp0SkOWs)
|
||||
* [documentation](https://docs.specter.solutions/desktop/)
|
||||
|
||||
## Why?
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ sudo yum -y install libusb libudev-devel libffi libffi-devel openssl-devel && su
|
|||
```
|
||||
|
||||
#### Windows
|
||||
* Install python 3.8.x by downloading from [python.org](https://www.python.org/downloads/windows/)
|
||||
* Install python 3.10.x by downloading from [python.org](https://www.python.org/downloads/windows/)
|
||||
|
||||
_Do NOT install python from the Microsoft Store! It runs in a different execution environment that creates enormous headaches!_
|
||||
|
||||
|
|
@ -85,8 +85,7 @@ sudo yum -y install libusb libudev-devel libffi libffi-devel openssl-devel && su
|
|||
|
||||
|
||||
### Set up virtualenv
|
||||
Specter is using `hwi-2.1.0` which by now supports higher Python versions than Specter itself. Specter currently supports Python 3.7-3.9.
|
||||
If you have Python 3.10 as your global version then be sure to also install an old Python version and pass it to `virtualenv` (e.g. `virtualenv --python=python3.8 .env`) or use pyenv.
|
||||
Specter is using `hwi-2.1.0` which by now supports higher Python versions than Specter itself. Specter currently supports Python 3.9 and 3.10.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/cryptoadvance/specter-desktop.git
|
||||
|
|
@ -113,6 +112,7 @@ Run the server:
|
|||
cd specter-desktop
|
||||
python3 -m cryptoadvance.specter server --config DevelopmentConfig --debug
|
||||
```
|
||||
After that, Specter will be available at http://127.0.0.1:25441/.
|
||||
|
||||
#### If `pip install` fails on `cryptography==3.4.x`
|
||||
|
||||
|
|
|
|||
|
|
@ -380,13 +380,13 @@ To check (before and/or afterwards) your installed version, you can use: `pip3 s
|
|||
### Raspiblitz
|
||||
You might want to wait until raspiblitz is providing an update. It takes longer but if you're not technically literate, that might be a better option. However, up from Version 1.6.1, Raspiblitz offers an update-possibility in the menu. Prior to that or as a part of troubleshooting-procedure, you can also do something like this:
|
||||
```
|
||||
sudo su - bitcoin
|
||||
cd .specter/
|
||||
sudo su - specter
|
||||
. ./.env/bin/activate
|
||||
pip3 list | grep specter
|
||||
pip3 install cryptoadvance.specter --upgrade
|
||||
pip3 list | grep specter
|
||||
service cryptoadvance-specter restart
|
||||
exit
|
||||
sudo systemctl restart specter
|
||||
```
|
||||
|
||||
### umbrel
|
||||
|
|
|
|||
12
package-lock.json
generated
12
package-lock.json
generated
|
|
@ -3589,9 +3589,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
|
|
@ -8856,9 +8856,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
|
|
|||
44
pyinstaller/electron/package-lock.json
generated
44
pyinstaller/electron/package-lock.json
generated
|
|
@ -1410,9 +1410,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/minimatch": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
|
||||
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
|
|
@ -1885,13 +1885,10 @@
|
|||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
|
||||
"integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.5"
|
||||
},
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
|
|
@ -2079,9 +2076,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
|
|
@ -4488,9 +4485,9 @@
|
|||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
|
||||
"integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
|
|
@ -4859,13 +4856,10 @@
|
|||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"json5": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
|
||||
"integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
|
|
@ -5012,9 +5006,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from pathlib import Path
|
|||
import cryptography
|
||||
import pgpy
|
||||
import requests
|
||||
from cryptoadvance.specter.util.wallet_importer import WalletImporter
|
||||
from flask import Blueprint, Flask
|
||||
from flask import current_app as app
|
||||
from flask import jsonify, redirect, render_template, request, send_file, url_for
|
||||
|
|
@ -29,7 +30,7 @@ from ..helpers import (
|
|||
from ..persistence import write_devices, write_wallet
|
||||
from ..server_endpoints import flash
|
||||
from ..services.service import callbacks
|
||||
from ..specter_error import handle_exception
|
||||
from ..specter_error import SpecterError, handle_exception
|
||||
from ..user import UserSecretException
|
||||
from ..util.sha256sum import sha256sum
|
||||
from ..util.shell import get_last_lines_from_file
|
||||
|
|
@ -149,74 +150,58 @@ def general():
|
|||
app.specter.device_manager.update()
|
||||
|
||||
rescanning = False
|
||||
logger.info(f"Importing {len(restore_wallets)} wallets ...")
|
||||
counter = {
|
||||
"success": 0,
|
||||
"specific_error": 0,
|
||||
"unspecific_error": 0,
|
||||
"rescan_error": 0,
|
||||
}
|
||||
for wallet in restore_wallets:
|
||||
try:
|
||||
app.specter.wallet_manager.rpc.createwallet(
|
||||
os.path.join(
|
||||
app.specter.wallet_manager.rpc_path, wallet["alias"]
|
||||
),
|
||||
True,
|
||||
)
|
||||
except Exception as e:
|
||||
# if wallet already exists in Bitcoin Core
|
||||
# continue with the existing one
|
||||
if "already exists" not in str(e):
|
||||
flash(
|
||||
_("Failed to import wallet {}, error: {}").format(
|
||||
wallet["name"], e
|
||||
),
|
||||
"error",
|
||||
)
|
||||
handle_exception(e)
|
||||
continue
|
||||
logger.debug(
|
||||
f"Wallet {wallet['alias']} already exists, skipping creation"
|
||||
)
|
||||
write_wallet(wallet)
|
||||
app.specter.wallet_manager.update(use_threading=False)
|
||||
|
||||
try:
|
||||
wallet_obj = app.specter.wallet_manager.get_by_alias(
|
||||
wallet["alias"]
|
||||
logger.info(f"Importing wallet {wallet['name']}")
|
||||
wallet_importer = WalletImporter(json.dumps(wallet), app.specter)
|
||||
wallet_importer.create_wallet(app.specter.wallet_manager)
|
||||
wallet_importer.rescan_as_needed(app.specter)
|
||||
counter["success"] += 1
|
||||
except SpecterError as se:
|
||||
error_type = (
|
||||
"rescan_error" if "rescan" in str(se) else "specific_error"
|
||||
)
|
||||
wallet_obj.keypoolrefill(0, wallet_obj.IMPORT_KEYPOOL, change=False)
|
||||
wallet_obj.keypoolrefill(0, wallet_obj.IMPORT_KEYPOOL, change=True)
|
||||
wallet_obj.import_labels(wallet.get("labels", {}))
|
||||
try:
|
||||
wallet_obj.rpc.rescanblockchain(
|
||||
wallet["blockheight"]
|
||||
if "blockheight" in wallet
|
||||
else get_startblock_by_chain(app.specter),
|
||||
no_wait=True,
|
||||
)
|
||||
app.logger.info("Rescanning Blockchain ...")
|
||||
rescanning = True
|
||||
except Exception as e:
|
||||
app.logger.exception(
|
||||
"Exception while rescanning blockchain for wallet {}: {}".format(
|
||||
wallet["alias"], e
|
||||
),
|
||||
e,
|
||||
)
|
||||
flash(
|
||||
_("Failed to perform rescan for wallet: {}").format(e),
|
||||
"error",
|
||||
)
|
||||
wallet_obj.getdata()
|
||||
flash(f"Wallet '{wallet.get('name')}': {se} (skipped)", "error")
|
||||
counter[error_type] += 1
|
||||
except Exception as e:
|
||||
flash(
|
||||
_("Failed to import wallet {}").format(wallet["name"]), "error"
|
||||
f"Error while importing wallet {wallet['name']}, check logs for details! (skipped)",
|
||||
"error",
|
||||
)
|
||||
handle_exception(e)
|
||||
flash(_("Specter data was successfully loaded from backup"), "info")
|
||||
logger.exception(
|
||||
f"Error while importing wallet {wallet['name']}: {e}"
|
||||
)
|
||||
counter["unspecific_error"] += 1
|
||||
|
||||
counter["errors_sum"] = (
|
||||
counter["rescan_error"]
|
||||
+ counter["specific_error"]
|
||||
+ counter["unspecific_error"]
|
||||
)
|
||||
if counter["success"] > 0:
|
||||
message = f"{counter['success']} wallets successfully restored. "
|
||||
|
||||
else:
|
||||
message = f"Sorry, this doesn't went that well. "
|
||||
if counter["errors_sum"] > 0:
|
||||
message += "however, we had " if counter["success"] > 0 else "We had "
|
||||
message += f"""<br>
|
||||
Successful imports: {counter["success"]}<br>
|
||||
Specific errors: {counter["specific_error"]}<br>
|
||||
Unspecific errors: {counter["unspecific_error"]} (create an issue about it on github)<br>
|
||||
Rescan issues: {counter["rescan_error"]}<br>
|
||||
"""
|
||||
if rescanning:
|
||||
flash(
|
||||
_(
|
||||
"Wallets are rescanning for transactions history.\n\
|
||||
This may take a few hours to complete."
|
||||
),
|
||||
"info",
|
||||
)
|
||||
message += "Wallets are rescanning for transactions history. This may take a few hours to complete."
|
||||
flash(message, "info")
|
||||
|
||||
return render_template(
|
||||
"settings/general_settings.jinja",
|
||||
|
|
|
|||
|
|
@ -197,13 +197,21 @@
|
|||
reader.onload = function(e) {
|
||||
if (files[i].webkitRelativePath.indexOf('devices/') != -1) {
|
||||
console.log(files[i].name);
|
||||
devices.push(JSON.parse(reader.result));
|
||||
try {
|
||||
devices.push(JSON.parse(reader.result));
|
||||
} catch {
|
||||
showError(`Could not load Device ${files[i].name}: not valid json! This Device will be skipped for import which might affect corresponding Wallets.`)
|
||||
}
|
||||
document.getElementById('restoredevices').value = JSON.stringify(devices);
|
||||
document.getElementById('filesloaded').innerHTML = `<br>Found ${devices.length} devices and ${wallets.length} wallets:`;
|
||||
document.getElementById('restore').style.display = 'block';
|
||||
} else if (files[i].webkitRelativePath.indexOf('wallets/') != -1) {
|
||||
console.log(files[i].name);
|
||||
wallets.push(JSON.parse(reader.result));
|
||||
try {
|
||||
wallets.push(JSON.parse(reader.result));
|
||||
} catch {
|
||||
showError(`Could not load Wallet ${files[i].name}: not valid json! This wallet will be skipped for import.`)
|
||||
}
|
||||
document.getElementById('restorewallets').value = JSON.stringify(wallets);
|
||||
document.getElementById('filesloaded').innerHTML = `<br>Found ${devices.length} devices and ${wallets.length} wallets:`;
|
||||
document.getElementById('restore').style.display = 'block';
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ class WalletImporter:
|
|||
try:
|
||||
self.descriptor = DescriptorCls.from_string(recv_descriptor)
|
||||
self.check_descriptor()
|
||||
except Exception as e:
|
||||
raise SpecterError(f"Invalid wallet descriptor: {e}")
|
||||
except ValueError as e:
|
||||
raise SpecterError(f"{e}: {recv_descriptor}")
|
||||
if self.wallet_name in specter.wallet_manager.wallets_names:
|
||||
raise SpecterError(f"Wallet with the same name already exists")
|
||||
(
|
||||
|
|
@ -242,7 +242,7 @@ class WalletImporter:
|
|||
)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
raise SpecterError(f"Failed to create wallet: {e}")
|
||||
raise SpecterError(f"Failed to create wallet: {e} (check logs for details)")
|
||||
logger.info(f"Created Wallet {self.wallet}")
|
||||
self.wallet.keypoolrefill(0, self.wallet.IMPORT_KEYPOOL, change=False)
|
||||
self.wallet.keypoolrefill(0, self.wallet.IMPORT_KEYPOOL, change=True)
|
||||
|
|
@ -333,7 +333,7 @@ class WalletImporter:
|
|||
wallet_data["x1/"]["xpub"]
|
||||
)
|
||||
else:
|
||||
raise Exception('"xpub" not found in "x1/" in Electrum backup json')
|
||||
raise SpecterError('"xpub" not found in "x1/" in Electrum backup json')
|
||||
|
||||
required_sigs = int(wallet_data.get("wallet_type").split("of")[0])
|
||||
recv_descriptor = "{}(sortedmulti({},{}))".format(
|
||||
|
|
@ -482,7 +482,7 @@ class WalletImporter:
|
|||
wallet_data["keystore"]["xpub"], is_multisig=False
|
||||
)
|
||||
else:
|
||||
raise Exception(
|
||||
raise SpecterError(
|
||||
'"xpub" not found in "keystore" in Electrum backup json'
|
||||
)
|
||||
recv_descriptor = "{}({})".format(
|
||||
|
|
@ -517,6 +517,13 @@ class WalletImporter:
|
|||
wallet_name = wallet_data.get("label", "Imported Wallet")
|
||||
recv_descriptor = wallet_data.get("descriptor", None)
|
||||
|
||||
if wallet_name is None:
|
||||
raise SpecterError(
|
||||
f"Couldn't find 'name' in wallet json (alias: {wallet_data.get('alias','also not existing')})."
|
||||
)
|
||||
if recv_descriptor is None:
|
||||
raise SpecterError("Couldn't find 'recv_descriptor' in wallet json.")
|
||||
|
||||
return (wallet_name, recv_descriptor, cosigners_types)
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -1454,6 +1454,7 @@ class Wallet:
|
|||
else self.rpc.getbalances()["watchonly"]
|
||||
)
|
||||
except Exception as e:
|
||||
handle_exception(e)
|
||||
raise SpecterError(f"was not able to get wallet_balance because {e}")
|
||||
self.balance = balance
|
||||
return self.balance
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue