mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Bitcoind without docker (#686)
* improving bitcoind code in prep for cypress testing * implemented datadir and cleanup, improved logging * reworking cleanup * ignoring datadir in case of Docker * black * fix: better platform-independence killing processes * added psutil as dependency
This commit is contained in:
parent
1579d08028
commit
2d4b28402e
5 changed files with 161 additions and 80 deletions
|
|
@ -15,3 +15,4 @@ pysocks==1.7.1
|
|||
six==1.12.0
|
||||
stem==1.8.0
|
||||
embit==0.1.1
|
||||
psutil==5.7.3
|
||||
|
|
|
|||
|
|
@ -209,6 +209,19 @@ protobuf==3.13.0 \
|
|||
--hash=sha256:e7662437ca1e0c51b93cadb988f9b353fa6b8013c0385d63a70c8a77d84da5f9 \
|
||||
--hash=sha256:f68eb9d03c7d84bd01c790948320b768de8559761897763731294e3bc316decb \
|
||||
# via bitbox02
|
||||
psutil==5.7.3 \
|
||||
--hash=sha256:01bc82813fbc3ea304914581954979e637bcc7084e59ac904d870d6eb8bb2bc7 \
|
||||
--hash=sha256:1cd6a0c9fb35ece2ccf2d1dd733c1e165b342604c67454fd56a4c12e0a106787 \
|
||||
--hash=sha256:2cb55ef9591b03ef0104bedf67cc4edb38a3edf015cf8cf24007b99cb8497542 \
|
||||
--hash=sha256:56c85120fa173a5d2ad1d15a0c6e0ae62b388bfb956bb036ac231fbdaf9e4c22 \
|
||||
--hash=sha256:5d9106ff5ec2712e2f659ebbd112967f44e7d33f40ba40530c485cc5904360b8 \
|
||||
--hash=sha256:6a3e1fd2800ca45083d976b5478a2402dd62afdfb719b30ca46cd28bb25a2eb4 \
|
||||
--hash=sha256:ade6af32eb80a536eff162d799e31b7ef92ddcda707c27bbd077238065018df4 \
|
||||
--hash=sha256:af73f7bcebdc538eda9cc81d19db1db7bf26f103f91081d780bbacfcb620dee2 \
|
||||
--hash=sha256:e02c31b2990dcd2431f4524b93491941df39f99619b0d312dfe1d4d530b08b4b \
|
||||
--hash=sha256:fa38ac15dbf161ab1e941ff4ce39abd64b53fec5ddf60c23290daed2bc7d1157 \
|
||||
--hash=sha256:fbcac492cb082fa38d88587d75feb90785d05d7e12d4565cbf1ecc727aff71b7 \
|
||||
# via -r requirements.in
|
||||
pyaes==1.6.1 \
|
||||
--hash=sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f \
|
||||
# via hwi
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ if __name__ == "__main__":
|
|||
# https://flask.palletsprojects.com/en/1.1.x/logging/#basic-configuration
|
||||
# However the dictConfig doesn't work, so let's do something similiar programatically
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.INFO)
|
||||
ch.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter(
|
||||
"[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
|
||||
)
|
||||
ch.setFormatter(formatter)
|
||||
logging.getLogger().addHandler(ch)
|
||||
# However initially, we'll set the root-logger to INFO:
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
logging.getLogger("cryptoadvance").setLevel(logging.INFO)
|
||||
cli()
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class BitcoindController:
|
|||
def __init__(self, rpcport=18443):
|
||||
self.rpcconn = Btcd_conn(rpcport=rpcport)
|
||||
|
||||
def start_bitcoind(self, cleanup_at_exit=False):
|
||||
def start_bitcoind(self, cleanup_at_exit=False, datadir=None):
|
||||
"""starts bitcoind with a specific rpcport=18543 by default.
|
||||
That's not the standard in order to make pytest running while
|
||||
developing locally against a different regtest-instance
|
||||
|
|
@ -76,7 +76,7 @@ class BitcoindController:
|
|||
return self.check_existing()
|
||||
|
||||
logger.debug("Starting bitcoind")
|
||||
self._start_bitcoind(cleanup_at_exit)
|
||||
self._start_bitcoind(cleanup_at_exit, datadir=datadir)
|
||||
|
||||
self.wait_for_bitcoind(self.rpcconn)
|
||||
self.mine(block_count=100)
|
||||
|
|
@ -196,8 +196,9 @@ class BitcoindPlainController(BitcoindController):
|
|||
self.bitcoind_path = bitcoind_path
|
||||
self.rpcconn.ipaddress = "localhost"
|
||||
|
||||
def _start_bitcoind(self, cleanup_at_exit=False):
|
||||
datadir = tempfile.mkdtemp(prefix="bitcoind_plain_datadir")
|
||||
def _start_bitcoind(self, cleanup_at_exit=True, datadir=None):
|
||||
if datadir == None:
|
||||
datadir = tempfile.mkdtemp(prefix="bitcoind_plain_datadir_")
|
||||
bitcoind_cmd = self.construct_bitcoind_cmd(
|
||||
self.rpcconn,
|
||||
run_docker=False,
|
||||
|
|
@ -212,14 +213,13 @@ class BitcoindPlainController(BitcoindController):
|
|||
)
|
||||
|
||||
def cleanup_bitcoind():
|
||||
self.bitcoind_proc.kill() # much faster then terminate() and speed is key here over being nice
|
||||
logger.debug(
|
||||
self.bitcoind_proc.terminate() # might take a bit longer than kill but it'll preserve block-height
|
||||
logger.info(
|
||||
"Killed bitcoind-process with pid {}".format(self.bitcoind_proc.pid)
|
||||
)
|
||||
shutil.rmtree(datadir)
|
||||
logger.debug("removed temp-dir")
|
||||
|
||||
if cleanup_at_exit:
|
||||
logger.debug("REGISTERING EXIT FUNCTIONS")
|
||||
atexit.register(cleanup_bitcoind)
|
||||
|
||||
def stop_bitcoind(self):
|
||||
|
|
@ -251,7 +251,10 @@ class BitcoindDockerController(BitcoindController):
|
|||
rpcconn, self.btcd_container = self.detect_bitcoind_container(rpcport)
|
||||
self.rpcconn = rpcconn
|
||||
|
||||
def _start_bitcoind(self, cleanup_at_exit):
|
||||
def _start_bitcoind(self, cleanup_at_exit, datadir=None):
|
||||
if datadir != None:
|
||||
# ignored
|
||||
pass
|
||||
bitcoind_path = self.construct_bitcoind_cmd(self.rpcconn)
|
||||
dclient = docker.from_env()
|
||||
logger.debug("Running (in docker): {}".format(bitcoind_path))
|
||||
|
|
@ -275,6 +278,7 @@ class BitcoindDockerController(BitcoindController):
|
|||
)
|
||||
|
||||
def cleanup_docker_bitcoind():
|
||||
logger.info("Cleaning up bitcoind-docker-container")
|
||||
self.btcd_container.stop()
|
||||
self.btcd_container.remove()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import logging
|
||||
from logging.config import dictConfig
|
||||
import os
|
||||
from os import path
|
||||
import sys
|
||||
import psutil
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import time
|
||||
from stem.control import Controller
|
||||
from .util.tor import stop_hidden_services, start_hidden_service
|
||||
|
|
@ -10,7 +14,6 @@ import click
|
|||
from .server import create_app, init_app
|
||||
from .helpers import set_loglevel
|
||||
|
||||
from os import path
|
||||
import signal
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -187,57 +190,134 @@ def server(daemon, stop, restart, force, port, host, cert, key, debug, tor, hwib
|
|||
run(debug=debug)
|
||||
|
||||
|
||||
class Echo:
|
||||
def __init__(self, quiet):
|
||||
self.quiet = quiet
|
||||
|
||||
def echo(self, mystring, prefix=True, **kwargs):
|
||||
if self.quiet:
|
||||
pass
|
||||
else:
|
||||
if prefix:
|
||||
click.echo(f" --> ", nl=False)
|
||||
click.echo(f"{mystring}", **kwargs)
|
||||
|
||||
|
||||
@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):
|
||||
@click.option("--debug/--no-debug", default=False, help="Turns on debug-logging")
|
||||
@click.option("--quiet/--no-quiet", default=False, help="as less output as possible")
|
||||
@click.option(
|
||||
"--nodocker", default=False, is_flag=True, help="use without docker (non-default)"
|
||||
)
|
||||
@click.option(
|
||||
"--docker-tag", "docker_tag", default="latest", help="Use a specific docker-tag"
|
||||
)
|
||||
@click.option(
|
||||
"--data-dir",
|
||||
default="/tmp/bitcoind_plain_datadir",
|
||||
help="specify a (maybe not yet existing) datadir. Works only in --nodocker (Default:/tmp/bitcoind_plain_datadir) ",
|
||||
)
|
||||
@click.option("--mining/--no-mining", default=True, help="Turns on mining (default)")
|
||||
@click.option(
|
||||
"--mining-period",
|
||||
default="15",
|
||||
help="Every mining-period (in seconds), a block gets mined (default 15sec)",
|
||||
)
|
||||
@click.option(
|
||||
"--reset",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Will kill the bitcoind. Datadir will get lost.",
|
||||
)
|
||||
def bitcoind(
|
||||
debug, quiet, nodocker, docker_tag, data_dir, mining, mining_period, reset
|
||||
):
|
||||
"""This will start a bitcoind regtest and mines a block every mining-period.
|
||||
If a bitcoind is already running on port 18443, it won't start another one. If you CTRL-C this, the bitcoind will
|
||||
still continue to run. You have to shut it down.
|
||||
"""
|
||||
# In order to avoid these dependencies for production use, we're importing them here:
|
||||
import docker
|
||||
from .bitcoind import (
|
||||
BitcoindDockerController,
|
||||
BitcoindPlainController,
|
||||
fetch_wallet_addresses_for_mining,
|
||||
)
|
||||
|
||||
from .bitcoind import BitcoindDockerController, fetch_wallet_addresses_for_mining
|
||||
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
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)
|
||||
logging.getLogger("cryptoadvance").setLevel(logging.DEBUG)
|
||||
logger.debug("Now on debug-logging")
|
||||
echo = Echo(quiet).echo
|
||||
|
||||
if reset:
|
||||
if not nodocker:
|
||||
echo("ERROR: --reset only works in conjunction with --nodocker currently")
|
||||
return
|
||||
did_something = False
|
||||
|
||||
for proc in psutil.process_iter():
|
||||
try:
|
||||
# Get process name & pid from process object.
|
||||
processName = proc.name()
|
||||
pid = proc.pid
|
||||
if processName.startswith("bitcoind"):
|
||||
echo(f"Killing bitcoind-process with id {pid} ...")
|
||||
did_something = True
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||
echo(f"Pid {pid} not owned by us. Might be a docker-process? {line}")
|
||||
if Path(data_dir).exists():
|
||||
echo(f"Purging Datadirectory {data_dir} ...")
|
||||
did_something = True
|
||||
shutil.rmtree(data_dir)
|
||||
if not did_something:
|
||||
echo("Nothing to do!")
|
||||
return
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
mining_every_x_seconds = float(mining_period)
|
||||
if nodocker:
|
||||
echo("starting plain bitcoind")
|
||||
my_bitcoind = BitcoindPlainController()
|
||||
# Make sure datadir does exist if specified:
|
||||
Path(data_dir).mkdir(parents=True, exist_ok=True)
|
||||
else:
|
||||
echo("starting or detecting container")
|
||||
my_bitcoind = BitcoindDockerController(docker_tag=docker_tag)
|
||||
try:
|
||||
my_bitcoind.start_bitcoind()
|
||||
my_bitcoind.start_bitcoind(cleanup_at_exit=True, datadir=data_dir)
|
||||
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 \
|
||||
echo(f"Image with tag {docker_tag} does not exist!")
|
||||
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
|
||||
if not nodocker:
|
||||
tags_of_image = [
|
||||
image.split(":")[-1] for image in my_bitcoind.btcd_container.image.tags
|
||||
]
|
||||
if docker_tag not in tags_of_image:
|
||||
echo(
|
||||
"The running docker container is not \
|
||||
the tag you requested!"
|
||||
)
|
||||
)
|
||||
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 "
|
||||
echo(
|
||||
"please stop first with docker stop {}".format(
|
||||
my_bitcoind.btcd_container.id
|
||||
)
|
||||
)
|
||||
sys.exit(1)
|
||||
echo("containerImage: %s" % my_bitcoind.btcd_container.image.tags)
|
||||
echo(" url: %s" % my_bitcoind.rpcconn.render_url())
|
||||
echo("user, password: bitcoin, secret")
|
||||
echo(" host, port: localhost, 18443")
|
||||
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"
|
||||
echo(
|
||||
"Now, mining a block every %f seconds, avoid it via --no-mining"
|
||||
% mining_every_x_seconds
|
||||
)
|
||||
# Get each address some coins
|
||||
|
|
@ -250,40 +330,23 @@ def bitcoind(debug, mining, docker_tag):
|
|||
|
||||
# make them spendable
|
||||
my_bitcoind.mine(block_count=100)
|
||||
click.echo(" --> ", nl=False)
|
||||
i = 0
|
||||
echo(
|
||||
f"height: {my_bitcoind.rpcconn.get_rpc().getblockchaininfo()['blocks']} | ",
|
||||
nl=False,
|
||||
)
|
||||
i, j = 0, 0
|
||||
while True:
|
||||
my_bitcoind.mine()
|
||||
click.echo("%i" % (i % 10), nl=False)
|
||||
echo("%i" % (i % 10), prefix=False, nl=False)
|
||||
if i % 10 == 9:
|
||||
click.echo(" ", nl=False)
|
||||
echo(" ", prefix=False, nl=False)
|
||||
i += 1
|
||||
if i >= 50:
|
||||
j = i
|
||||
i = 0
|
||||
click.echo(" ")
|
||||
click.echo(" --> ", nl=False)
|
||||
echo("", prefix=False)
|
||||
echo(
|
||||
f"height: {my_bitcoind.rpcconn.get_rpc().getblockchaininfo()['blocks']} | ",
|
||||
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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue