mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Pyinstaller builds (#258)
* pyinstaller specs * add udev rules * remove ecdsa dep * udev rules, pyinstaller spec update * update udev readme * Fix issue with Tor service creation * remove pyinstaller artifacts for bip32 * main -> cli * remove daemon and debug modes from binaries * add aarch64 (ARM) build * fix when no tor * fix flake8 Co-authored-by: benk10 <ben.kaufman10@gmail.com>
This commit is contained in:
parent
6e0bb08744
commit
2575aed4ad
22 changed files with 458 additions and 244 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -2,10 +2,11 @@ __pycache__
|
|||
.pytest_cache
|
||||
*.pyc
|
||||
.env
|
||||
.venv
|
||||
.tor_service_key
|
||||
build
|
||||
dist
|
||||
*.egg-info
|
||||
.vscode
|
||||
cert.pem
|
||||
key.pem
|
||||
key.pem
|
||||
|
|
|
|||
5
pyinstaller/README.md
Normal file
5
pyinstaller/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Pyinstaller build
|
||||
|
||||
To build a binary: `pip3 install pyinstaller`
|
||||
|
||||
Then from this folder run `pyinstaller --onefile specterd.spec`
|
||||
4
pyinstaller/hooks/hook-hwilib.devices.py
Normal file
4
pyinstaller/hooks/hook-hwilib.devices.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
from hwilib.devices import __all__
|
||||
hiddenimports = []
|
||||
for d in __all__:
|
||||
hiddenimports.append('hwilib.devices.' + d)
|
||||
30
pyinstaller/specterd.py
Normal file
30
pyinstaller/specterd.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
from logging.config import dictConfig
|
||||
from cryptoadvance.specter.cli import server
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
# central and early configuring of logging see
|
||||
# https://flask.palletsprojects.com/en/1.1.x/logging/#basic-configuration
|
||||
dictConfig({
|
||||
'version': 1,
|
||||
'formatters': {'default': {
|
||||
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
|
||||
}},
|
||||
'handlers': {'wsgi': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'stream': 'ext://flask.logging.wsgi_errors_stream',
|
||||
'formatter': 'default'
|
||||
}},
|
||||
'root': {
|
||||
'level': 'INFO',
|
||||
'handlers': ['wsgi']
|
||||
}
|
||||
})
|
||||
if "--daemon" in sys.argv:
|
||||
print("Daemon mode is not supported in binaries yet")
|
||||
sys.exit(1)
|
||||
if "--debug" in sys.argv:
|
||||
print("Debug mode is useless in binary mode, don't use it")
|
||||
sys.exit(1)
|
||||
print("Starting Specter server. It may take a while, please be patient")
|
||||
server()
|
||||
60
pyinstaller/specterd.spec
Normal file
60
pyinstaller/specterd.spec
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
import platform
|
||||
import subprocess
|
||||
import mnemonic, os
|
||||
|
||||
mnemonic_path = os.path.join(mnemonic.__path__[0], "wordlist")
|
||||
|
||||
block_cipher = None
|
||||
|
||||
binaries = []
|
||||
if platform.system() == 'Windows':
|
||||
binaries = [("./windll/libusb-1.0.dll", ".")]
|
||||
elif platform.system() == 'Linux':
|
||||
if platform.processor() == 'aarch64': #ARM 64 bit
|
||||
binaries = [("/lib/aarch64-linux-gnu/libusb-1.0.so.0", ".")]
|
||||
else:
|
||||
binaries = [("/lib/x86_64-linux-gnu/libusb-1.0.so.0", ".")]
|
||||
elif platform.system() == 'Darwin':
|
||||
find_brew_libusb_proc = subprocess.Popen(['brew', '--prefix', 'libusb'], stdout=subprocess.PIPE)
|
||||
libusb_path = find_brew_libusb_proc.communicate()[0]
|
||||
binaries = [(libusb_path.rstrip().decode() + "/lib/libusb-1.0.dylib", ".")]
|
||||
|
||||
a = Analysis(['specterd.py'],
|
||||
binaries=binaries,
|
||||
datas=[('../src/cryptoadvance/specter/templates', 'templates'),
|
||||
('../src/cryptoadvance/specter/static', 'static'),
|
||||
(mnemonic_path, 'mnemonic/wordlist'),
|
||||
],
|
||||
hiddenimports=[
|
||||
'pkg_resources.py2_warn',
|
||||
'cryptoadvance.specter.config'
|
||||
],
|
||||
hookspath=['hooks/'],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False)
|
||||
|
||||
if platform.system() == 'Linux':
|
||||
import hwilib
|
||||
a.datas += Tree('../udev', prefix='hwilib/udev')
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='specterd',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True )
|
||||
BIN
pyinstaller/windll/libsecp256k1.dll
Executable file
BIN
pyinstaller/windll/libsecp256k1.dll
Executable file
Binary file not shown.
BIN
pyinstaller/windll/libusb-1.0.dll
Executable file
BIN
pyinstaller/windll/libusb-1.0.dll
Executable file
Binary file not shown.
BIN
pyinstaller/windll/libusb-1.0.lib
Executable file
BIN
pyinstaller/windll/libusb-1.0.lib
Executable file
Binary file not shown.
BIN
pyinstaller/windll/libusb-1.0.pdb
Executable file
BIN
pyinstaller/windll/libusb-1.0.pdb
Executable file
Binary file not shown.
|
|
@ -1,244 +1,9 @@
|
|||
import logging
|
||||
from logging.config import dictConfig
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from stem.control import Controller
|
||||
from . import tor_util
|
||||
import click
|
||||
|
||||
import docker
|
||||
|
||||
from .bitcoind import (BitcoindDockerController,
|
||||
fetch_wallet_addresses_for_mining)
|
||||
from .server import DATA_FOLDER, create_app, init_app
|
||||
|
||||
from os import path
|
||||
import signal
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
pass
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option("--daemon", is_flag=True)
|
||||
@click.option("--stop", is_flag=True)
|
||||
@click.option("--restart", is_flag=True)
|
||||
@click.option("--force", is_flag=True)
|
||||
# options below can help to run it on a remote server,
|
||||
# but better use nginx
|
||||
@click.option("--port") # default - 25441 set to 80 for http, 443 for https
|
||||
@click.option("--host", default="127.0.0.1") # set to 0.0.0.0 to make it available outside
|
||||
# for https:
|
||||
@click.option("--cert")
|
||||
@click.option("--key")
|
||||
@click.option('--debug/--no-debug', default=None)
|
||||
@click.option('--tor', is_flag=True)
|
||||
@click.option("--hwibridge", is_flag=True)
|
||||
def server(daemon, stop, restart, force, port, host, cert, key, debug, tor, hwibridge):
|
||||
# we will store our daemon PID here
|
||||
pid_file = path.expanduser(path.join(DATA_FOLDER, "daemon.pid"))
|
||||
toraddr_file = path.expanduser(path.join(DATA_FOLDER, "onion.txt"))
|
||||
# check if pid file exists
|
||||
if path.isfile(pid_file):
|
||||
# if we need to stop daemon
|
||||
if stop or restart:
|
||||
print("Stopping the Specter server...")
|
||||
with open(pid_file) as f:
|
||||
pid = int(f.read())
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
time.sleep(0.3)
|
||||
try:
|
||||
os.remove(pid_file)
|
||||
except Exception:
|
||||
pass
|
||||
elif daemon:
|
||||
if not force:
|
||||
print(f"PID file \"{pid_file}\" already exists. Use --force to overwrite")
|
||||
return
|
||||
else:
|
||||
os.remove(pid_file)
|
||||
if stop:
|
||||
return
|
||||
else:
|
||||
if stop or restart:
|
||||
print(f"Can't find PID file \"{pid_file}\"")
|
||||
if stop:
|
||||
return
|
||||
|
||||
app = create_app()
|
||||
app.app_context().push()
|
||||
init_app(app, hwibridge=hwibridge)
|
||||
|
||||
# watch templates folder to reload when something changes
|
||||
extra_dirs = ['templates']
|
||||
extra_files = extra_dirs[:]
|
||||
for extra_dir in extra_dirs:
|
||||
for dirname, dirs, files in os.walk(extra_dir):
|
||||
for filename in files:
|
||||
filename = os.path.join(dirname, filename)
|
||||
if os.path.isfile(filename):
|
||||
extra_files.append(filename)
|
||||
|
||||
# if port is not defined - get it from environment
|
||||
if port is None:
|
||||
port = int(os.getenv('PORT', 25441))
|
||||
else:
|
||||
port = int(port)
|
||||
|
||||
# certificates
|
||||
if cert is None:
|
||||
cert = os.getenv('CERT', None)
|
||||
if key is None:
|
||||
key = os.getenv('KEY', None)
|
||||
|
||||
protocol = "http"
|
||||
kwargs = {
|
||||
"host": host,
|
||||
"port": port,
|
||||
"extra_files": extra_files,
|
||||
}
|
||||
if cert is not None and key is not None:
|
||||
cert = os.path.abspath(cert)
|
||||
key = os.path.abspath(key)
|
||||
kwargs["ssl_context"] = (cert, key)
|
||||
protocol = "https"
|
||||
|
||||
if hwibridge:
|
||||
app.logger.info(
|
||||
"Running HWI Bridge mode, you can configure access \
|
||||
to the API at: %s://%s:%d/hwi/settings"
|
||||
% (protocol, host, port)
|
||||
)
|
||||
|
||||
# debug is false by default
|
||||
def run(debug=debug):
|
||||
with Controller.from_port() as controller:
|
||||
app.controller = controller
|
||||
port = 5000 # default flask port
|
||||
if 'port' in kwargs:
|
||||
port = kwargs['port']
|
||||
else:
|
||||
kwargs['port'] = port
|
||||
# if we have certificates
|
||||
if "ssl_context" in kwargs:
|
||||
tor_port = 443
|
||||
else:
|
||||
tor_port = 80
|
||||
app.port = port
|
||||
app.tor_port = tor_port
|
||||
app.save_tor_address_to = toraddr_file
|
||||
if debug and (tor or os.getenv('CONNECT_TOR') == 'True'):
|
||||
print(
|
||||
'* Warning: Cannot use Tor in debug mode. Starting in production mode instead.'
|
||||
)
|
||||
if tor or os.getenv('CONNECT_TOR') == 'True':
|
||||
try:
|
||||
app.tor_enabled = True
|
||||
tor_util.start_hidden_service(app)
|
||||
except Exception as e:
|
||||
print('* Failed to start Tor hidden service: {}'.format(e))
|
||||
print('* Continuing process with Tor disabled')
|
||||
app.tor_service_id = None
|
||||
app.tor_enabled = False
|
||||
else:
|
||||
app.tor_service_id = None
|
||||
app.tor_enabled = False
|
||||
app.run(debug=debug, **kwargs)
|
||||
tor_util.stop_hidden_services(app)
|
||||
|
||||
# check if we should run a daemon or not
|
||||
if daemon or restart:
|
||||
print("Starting server in background...")
|
||||
print("* Hopefully running on %s://%s:%d/" % (protocol, host, port))
|
||||
# macOS + python3.7 is buggy
|
||||
if sys.platform == "darwin" and \
|
||||
(sys.version_info.major == 3 and sys.version_info.minor < 8):
|
||||
print(
|
||||
"* WARNING: --daemon mode might not work properly in python 3.7 \
|
||||
and lower on MacOS. Upgrade to python 3.8+"
|
||||
)
|
||||
from daemonize import Daemonize
|
||||
d = Daemonize(app="specter", pid=pid_file, action=run)
|
||||
d.start()
|
||||
else:
|
||||
# if not a daemon we can use DEBUG
|
||||
if debug is None:
|
||||
debug = app.config['DEBUG']
|
||||
run(debug=debug)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--debug/--no-debug', default=False)
|
||||
@click.option('--mining/--no-mining', default=True)
|
||||
@click.option('--docker-tag', "docker_tag", default="latest")
|
||||
def bitcoind(debug, mining, docker_tag):
|
||||
mining_every_x_seconds = 15
|
||||
if debug:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
click.echo(" --> starting or detecting container")
|
||||
my_bitcoind = BitcoindDockerController(docker_tag=docker_tag)
|
||||
try:
|
||||
my_bitcoind.start_bitcoind()
|
||||
except docker.errors.ImageNotFound:
|
||||
click.echo(" --> Image with tag {} does not exist!".format(docker_tag))
|
||||
click.echo(
|
||||
" --> Try to download first with docker pull \
|
||||
registry.gitlab.com/cryptoadvance/specter-desktop/python-bitcoind:{}"
|
||||
.format(docker_tag)
|
||||
)
|
||||
sys.exit(1)
|
||||
tags_of_image = [image.split(":")[-1] for image in my_bitcoind.btcd_container.image.tags]
|
||||
if docker_tag not in tags_of_image:
|
||||
click.echo(" --> The running docker container is not the tag you requested!")
|
||||
click.echo(
|
||||
" --> please stop first with docker stop {}"
|
||||
.format(my_bitcoind.btcd_container.id)
|
||||
)
|
||||
sys.exit(1)
|
||||
click.echo(" --> containerImage: %s" % my_bitcoind.btcd_container.image.tags)
|
||||
click.echo(" --> url: %s" % my_bitcoind.rpcconn.render_url())
|
||||
click.echo(" --> user, password: bitcoin, secret")
|
||||
click.echo(" --> host, port: localhost, 18443")
|
||||
click.echo(
|
||||
" --> bitcoin-cli: bitcoin-cli -regtest -rpcuser=bitcoin \
|
||||
-rpcpassword=secret getblockchaininfo "
|
||||
)
|
||||
if mining:
|
||||
click.echo(
|
||||
" --> Now, mining a block every %i seconds. Avoid it via --no-mining" %
|
||||
mining_every_x_seconds
|
||||
)
|
||||
# Get each address some coins
|
||||
try:
|
||||
for address in fetch_wallet_addresses_for_mining():
|
||||
my_bitcoind.mine(address=address)
|
||||
except FileNotFoundError:
|
||||
# might happen if there no ~/.specter folder yet
|
||||
pass
|
||||
|
||||
# make them spendable
|
||||
my_bitcoind.mine(block_count=100)
|
||||
click.echo(" --> ", nl=False)
|
||||
i = 0
|
||||
while True:
|
||||
my_bitcoind.mine()
|
||||
click.echo("%i" % (i % 10), nl=False)
|
||||
if i % 10 == 9:
|
||||
click.echo(" ", nl=False)
|
||||
i += 1
|
||||
if i >= 50:
|
||||
i = 0
|
||||
click.echo(" ")
|
||||
click.echo(" --> ", nl=False)
|
||||
time.sleep(mining_every_x_seconds)
|
||||
|
||||
from .cli import cli
|
||||
|
||||
if __name__ == "__main__":
|
||||
# central and early configuring of logging
|
||||
# see https://flask.palletsprojects.com/en/1.1.x/logging/#basic-configuration
|
||||
# central and early configuring of logging see
|
||||
# https://flask.palletsprojects.com/en/1.1.x/logging/#basic-configuration
|
||||
dictConfig({
|
||||
'version': 1,
|
||||
'formatters': {'default': {
|
||||
|
|
|
|||
268
src/cryptoadvance/specter/cli.py
Normal file
268
src/cryptoadvance/specter/cli.py
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
import logging
|
||||
from logging.config import dictConfig
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from stem.control import Controller
|
||||
from . import tor_util
|
||||
import click
|
||||
|
||||
import docker
|
||||
|
||||
from .bitcoind import (BitcoindDockerController,
|
||||
fetch_wallet_addresses_for_mining)
|
||||
from .server import DATA_FOLDER, create_app, init_app
|
||||
|
||||
from os import path
|
||||
import signal
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
pass
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option("--daemon", is_flag=True)
|
||||
@click.option("--stop", is_flag=True)
|
||||
@click.option("--restart", is_flag=True)
|
||||
@click.option("--force", is_flag=True)
|
||||
# options below can help to run it on a remote server,
|
||||
# but better use nginx
|
||||
@click.option("--port") # default - 25441 set to 80 for http, 443 for https
|
||||
# set to 0.0.0.0 to make it available outside
|
||||
@click.option("--host", default="127.0.0.1")
|
||||
# for https:
|
||||
@click.option("--cert")
|
||||
@click.option("--key")
|
||||
@click.option('--debug/--no-debug', default=None)
|
||||
@click.option('--tor', is_flag=True)
|
||||
@click.option("--hwibridge", is_flag=True)
|
||||
def server(daemon, stop, restart, force,
|
||||
port, host, cert, key,
|
||||
debug, tor, hwibridge):
|
||||
# we will store our daemon PID here
|
||||
pid_file = path.expanduser(path.join(DATA_FOLDER, "daemon.pid"))
|
||||
toraddr_file = path.expanduser(path.join(DATA_FOLDER, "onion.txt"))
|
||||
# check if pid file exists
|
||||
if path.isfile(pid_file):
|
||||
# if we need to stop daemon
|
||||
if stop or restart:
|
||||
print("Stopping the Specter server...")
|
||||
with open(pid_file) as f:
|
||||
pid = int(f.read())
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
time.sleep(0.3)
|
||||
try:
|
||||
os.remove(pid_file)
|
||||
except Exception:
|
||||
pass
|
||||
elif daemon:
|
||||
if not force:
|
||||
print(f"PID file \"{pid_file}\" already exists. \
|
||||
Use --force to overwrite")
|
||||
return
|
||||
else:
|
||||
os.remove(pid_file)
|
||||
if stop:
|
||||
return
|
||||
else:
|
||||
if stop or restart:
|
||||
print(f"Can't find PID file \"{pid_file}\"")
|
||||
if stop:
|
||||
return
|
||||
|
||||
app = create_app()
|
||||
app.app_context().push()
|
||||
init_app(app, hwibridge=hwibridge)
|
||||
|
||||
# watch templates folder to reload when something changes
|
||||
extra_dirs = ['templates']
|
||||
extra_files = extra_dirs[:]
|
||||
for extra_dir in extra_dirs:
|
||||
for dirname, dirs, files in os.walk(extra_dir):
|
||||
for filename in files:
|
||||
filename = os.path.join(dirname, filename)
|
||||
if os.path.isfile(filename):
|
||||
extra_files.append(filename)
|
||||
|
||||
# if port is not defined - get it from environment
|
||||
if port is None:
|
||||
port = int(os.getenv('PORT', 25441))
|
||||
else:
|
||||
port = int(port)
|
||||
|
||||
# certificates
|
||||
if cert is None:
|
||||
cert = os.getenv('CERT', None)
|
||||
if key is None:
|
||||
key = os.getenv('KEY', None)
|
||||
|
||||
protocol = "http"
|
||||
kwargs = {
|
||||
"host": host,
|
||||
"port": port,
|
||||
"extra_files": extra_files,
|
||||
}
|
||||
if cert is not None and key is not None:
|
||||
cert = os.path.abspath(cert)
|
||||
key = os.path.abspath(key)
|
||||
kwargs["ssl_context"] = (cert, key)
|
||||
protocol = "https"
|
||||
|
||||
if hwibridge:
|
||||
app.logger.info(
|
||||
"Running HWI Bridge mode, you can configure access \
|
||||
to the API at: %s://%s:%d/hwi/settings"
|
||||
% (protocol, host, port)
|
||||
)
|
||||
|
||||
# debug is false by default
|
||||
def run(debug=debug):
|
||||
try:
|
||||
app.controller = Controller.from_port()
|
||||
except Exception:
|
||||
app.controller = None
|
||||
try:
|
||||
port = 5000 # default flask port
|
||||
if 'port' in kwargs:
|
||||
port = kwargs['port']
|
||||
else:
|
||||
kwargs['port'] = port
|
||||
# if we have certificates
|
||||
if "ssl_context" in kwargs:
|
||||
tor_port = 443
|
||||
else:
|
||||
tor_port = 80
|
||||
app.port = port
|
||||
app.tor_port = tor_port
|
||||
app.save_tor_address_to = toraddr_file
|
||||
if debug and (tor or os.getenv('CONNECT_TOR') == 'True'):
|
||||
print(" * Warning: Cannot use Tor in debug mode. \
|
||||
Starting in production mode instead.")
|
||||
debug = False
|
||||
if tor or os.getenv('CONNECT_TOR') == 'True':
|
||||
try:
|
||||
app.tor_enabled = True
|
||||
tor_util.start_hidden_service(app)
|
||||
except Exception as e:
|
||||
print(f' * Failed to start Tor hidden service: {e}')
|
||||
print(' * Continuing process with Tor disabled')
|
||||
app.tor_service_id = None
|
||||
app.tor_enabled = False
|
||||
else:
|
||||
app.tor_service_id = None
|
||||
app.tor_enabled = False
|
||||
app.run(debug=debug, **kwargs)
|
||||
tor_util.stop_hidden_services(app)
|
||||
finally:
|
||||
if app.controller is not None:
|
||||
app.controller.close()
|
||||
|
||||
# check if we should run a daemon or not
|
||||
if daemon or restart:
|
||||
print("Starting server in background...")
|
||||
print(" * Hopefully running on %s://%s:%d/" % (protocol, host, port))
|
||||
# macOS + python3.7 is buggy
|
||||
if sys.platform == "darwin" and \
|
||||
(sys.version_info.major == 3 and sys.version_info.minor < 8):
|
||||
print(" * WARNING: --daemon mode might not \
|
||||
work properly in python 3.7 and lower \
|
||||
on MacOS. Upgrade to python 3.8+")
|
||||
from daemonize import Daemonize
|
||||
d = Daemonize(app="specter", pid=pid_file, action=run)
|
||||
d.start()
|
||||
else:
|
||||
# if not a daemon we can use DEBUG
|
||||
if debug is None:
|
||||
debug = app.config['DEBUG']
|
||||
run(debug=debug)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--debug/--no-debug', default=False)
|
||||
@click.option('--mining/--no-mining', default=True)
|
||||
@click.option('--docker-tag', "docker_tag", default="latest")
|
||||
def bitcoind(debug, mining, docker_tag):
|
||||
mining_every_x_seconds = 15
|
||||
if debug:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
click.echo(" --> starting or detecting container")
|
||||
my_bitcoind = BitcoindDockerController(docker_tag=docker_tag)
|
||||
try:
|
||||
my_bitcoind.start_bitcoind()
|
||||
except docker.errors.ImageNotFound:
|
||||
click.echo(f" --> Image with tag {docker_tag} does not exist!")
|
||||
click.echo(f" --> Try to download first with docker pull \
|
||||
registry.gitlab.com/cryptoadvance/specter-desktop\
|
||||
/python-bitcoind:{docker_tag}")
|
||||
sys.exit(1)
|
||||
tags_of_image = [image.split(":")[-1]
|
||||
for image in my_bitcoind.btcd_container.image.tags]
|
||||
if docker_tag not in tags_of_image:
|
||||
click.echo(" --> The running docker container is not \
|
||||
the tag you requested!")
|
||||
click.echo(
|
||||
" --> please stop first with docker stop {}"
|
||||
.format(my_bitcoind.btcd_container.id)
|
||||
)
|
||||
sys.exit(1)
|
||||
click.echo(" --> containerImage: %s" %
|
||||
my_bitcoind.btcd_container.image.tags)
|
||||
click.echo(" --> url: %s" % my_bitcoind.rpcconn.render_url())
|
||||
click.echo(" --> user, password: bitcoin, secret")
|
||||
click.echo(" --> host, port: localhost, 18443")
|
||||
click.echo(
|
||||
" --> bitcoin-cli: bitcoin-cli -regtest -rpcuser=bitcoin \
|
||||
-rpcpassword=secret getblockchaininfo "
|
||||
)
|
||||
if mining:
|
||||
click.echo(
|
||||
" --> Now, mining a block every %i seconds. \
|
||||
Avoid it via --no-mining" %
|
||||
mining_every_x_seconds
|
||||
)
|
||||
# Get each address some coins
|
||||
try:
|
||||
for address in fetch_wallet_addresses_for_mining():
|
||||
my_bitcoind.mine(address=address)
|
||||
except FileNotFoundError:
|
||||
# might happen if there no ~/.specter folder yet
|
||||
pass
|
||||
|
||||
# make them spendable
|
||||
my_bitcoind.mine(block_count=100)
|
||||
click.echo(" --> ", nl=False)
|
||||
i = 0
|
||||
while True:
|
||||
my_bitcoind.mine()
|
||||
click.echo("%i" % (i % 10), nl=False)
|
||||
if i % 10 == 9:
|
||||
click.echo(" ", nl=False)
|
||||
i += 1
|
||||
if i >= 50:
|
||||
i = 0
|
||||
click.echo(" ")
|
||||
click.echo(" --> ", nl=False)
|
||||
time.sleep(mining_every_x_seconds)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# central and early configuring of logging see
|
||||
# https://flask.palletsprojects.com/en/1.1.x/logging/#basic-configuration
|
||||
dictConfig({
|
||||
'version': 1,
|
||||
'formatters': {'default': {
|
||||
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
|
||||
}},
|
||||
'handlers': {'wsgi': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'stream': 'ext://flask.logging.wsgi_errors_stream',
|
||||
'formatter': 'default'
|
||||
}},
|
||||
'root': {
|
||||
'level': 'INFO',
|
||||
'handlers': ['wsgi']
|
||||
}
|
||||
})
|
||||
cli()
|
||||
|
|
@ -78,7 +78,13 @@ def inject_tor():
|
|||
if request.args.get('action', '') == 'stoptor' and len(current_hidden_services) != 0:
|
||||
stop_hidden_services(app)
|
||||
if request.args.get('action', '') == 'starttor' and len(current_hidden_services) == 0:
|
||||
start_hidden_service(app)
|
||||
try:
|
||||
start_hidden_service(app)
|
||||
except Exception as e:
|
||||
flash('Failed to start Tor hidden service.\
|
||||
Make sure you have Tor running with ControlPort configured and try again.\
|
||||
Error returned: {}'.format(e), 'error')
|
||||
return dict(tor_service_id='', tor_enabled=False)
|
||||
return dict(tor_service_id=app.tor_service_id, tor_enabled=app.tor_enabled)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,9 @@ def get_version_info():
|
|||
'''
|
||||
name="cryptoadvance.specter"
|
||||
try:
|
||||
# fail right away if it's a binary
|
||||
if getattr(sys, 'frozen', False):
|
||||
raise RuntimeError("Using frozen binary, verision unavailable")
|
||||
latest_version = str(subprocess.run([sys.executable, '-m', 'pip', 'install', '{}==random'.format(name)], capture_output=True, text=True))
|
||||
latest_version = latest_version[latest_version.find('(from versions:')+15:]
|
||||
latest_version = latest_version[:latest_version.find(')')]
|
||||
|
|
|
|||
|
|
@ -118,7 +118,6 @@ def autodetect_cli_confs(port=None):
|
|||
conf_arr = detect_cli_confs()
|
||||
available_conf_arr = []
|
||||
if len(conf_arr) > 0:
|
||||
print("trying %d different configs" % len(conf_arr))
|
||||
for conf in conf_arr:
|
||||
cli = BitcoinCLI(**conf)
|
||||
if port is not None:
|
||||
|
|
@ -131,9 +130,6 @@ def autodetect_cli_confs(port=None):
|
|||
pass
|
||||
except Exception as e:
|
||||
pass
|
||||
else:
|
||||
print("Bitcoin-cli not found :(")
|
||||
print("Detected %d bitcoin daemons" % len(available_conf_arr))
|
||||
return available_conf_arr
|
||||
|
||||
class RpcError(Exception):
|
||||
|
|
|
|||
9
udev/20-hw1.rules
Normal file
9
udev/20-hw1.rules
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="1b7c", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="2b7c", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="3b7c", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="4b7c", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="1807", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="1808", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0000", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0001", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0004", MODE="0660", GROUP="plugdev"
|
||||
5
udev/49-micropython.rules
Normal file
5
udev/49-micropython.rules
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# f055:9800 - MicroPython board
|
||||
ATTRS{idVendor}=="f055", ATTRS{idProduct}=="9800", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||
ATTRS{idVendor}=="f055", ATTRS{idProduct}=="9800", ENV{MTP_NO_PROBE}="1"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="f055", ATTRS{idProduct}=="9800", MODE:="0666"
|
||||
KERNEL=="ttyACM*", ATTRS{idVendor}=="f055", ATTRS{idProduct}=="9800", MODE:="0666"
|
||||
8
udev/51-coinkite.rules
Normal file
8
udev/51-coinkite.rules
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
# probably not needed:
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="d13e", ATTRS{idProduct}=="cc10", GROUP="plugdev", MODE="0666"
|
||||
|
||||
# required:
|
||||
# from <https://github.com/signal11/hidapi/blob/master/udev/99-hid.rules>
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="d13e", ATTRS{idProduct}=="cc10", GROUP="plugdev", MODE="0666"
|
||||
|
||||
1
udev/51-hid-digitalbitbox.rules
Normal file
1
udev/51-hid-digitalbitbox.rules
Normal file
|
|
@ -0,0 +1 @@
|
|||
SUBSYSTEM=="usb", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="dbb%n", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2402"
|
||||
17
udev/51-trezor.rules
Normal file
17
udev/51-trezor.rules
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# TREZOR: The Original Hardware Wallet
|
||||
# https://trezor.io/
|
||||
#
|
||||
# Put this file into /etc/udev/rules.d
|
||||
#
|
||||
# If you are creating a distribution package,
|
||||
# put this into /usr/lib/udev/rules.d or /lib/udev/rules.d
|
||||
# depending on your distribution
|
||||
|
||||
# TREZOR
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="534c", ATTR{idProduct}=="0001", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"
|
||||
|
||||
# TREZOR v2
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c0", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="53c1", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="trezor%n"
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"
|
||||
11
udev/51-usb-keepkey.rules
Normal file
11
udev/51-usb-keepkey.rules
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# KeepKey: Your Private Bitcoin Vault
|
||||
# http://www.keepkey.com/
|
||||
# Put this file into /usr/lib/udev/rules.d or /etc/udev/rules.d
|
||||
|
||||
# KeepKey HID Firmware/Bootloader
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="2b24", ATTR{idProduct}=="0001", MODE="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="keepkey%n"
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="2b24", ATTRS{idProduct}=="0001", MODE="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"
|
||||
|
||||
# KeepKey WebUSB Firmware/Bootloader
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="2b24", ATTR{idProduct}=="0002", MODE="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="keepkey%n"
|
||||
KERNEL=="hidraw*", ATTRS{idVendor}=="2b24", ATTRS{idProduct}=="0002", MODE="0666", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"
|
||||
1
udev/52-hid-digitalbitbox.rules
Normal file
1
udev/52-hid-digitalbitbox.rules
Normal file
|
|
@ -0,0 +1 @@
|
|||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2402", TAG+="uaccess", TAG+="udev-acl", SYMLINK+="dbbf%n"
|
||||
24
udev/README.md
Normal file
24
udev/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# udev rules
|
||||
|
||||
This directory contains all of the udev rules for the supported devices as retrieved from vendor websites and repositories.
|
||||
These are necessary for the devices to be reachable on linux environments.
|
||||
|
||||
- `20-hw1.rules` (Ledger): https://github.com/LedgerHQ/udev-rules/blob/master/20-hw1.rules
|
||||
- `51-coinkite.rules` (Coldcard): https://github.com/Coldcard/ckcc-protocol/blob/master/51-coinkite.rules
|
||||
- `51-hid-digitalbitbox.rules`, `52-hid-digitalbitbox.rules` (Digital Bitbox): https://shiftcrypto.ch/start_linux
|
||||
- `51-trezor.rules` (Trezor): https://github.com/trezor/trezor-common/blob/master/udev/51-trezor.rules
|
||||
- `51-usb-keepkey.rules` (Keepkey): https://github.com/keepkey/udev-rules/blob/master/51-usb-keepkey.rules
|
||||
- `49-micropython.rules` (Specter-DIY): http://wiki.micropython.org/Installation#USB-Permissioning-on-Linux
|
||||
|
||||
# Usage
|
||||
|
||||
Apply these rules by copying them to `/etc/udev/rules.d/` and notifying `udevadm`.
|
||||
Your user will need to be added to the `plugdev` group, which needs to be created if it does not already exist.
|
||||
|
||||
```
|
||||
$ sudo cp udev/*.rules /etc/udev/rules.d/
|
||||
$ sudo udevadm trigger
|
||||
$ sudo udevadm control --reload-rules
|
||||
$ sudo groupadd plugdev
|
||||
$ sudo usermod -aG plugdev `whoami`
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue