mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Chore: Porting chore/rid_of_defaultnode to uiux-revamp (#2098)
* Chore: Remove default node (#2072) * change passwordaspin auth implementation * env-var for RASPIBLITZ_SPECTER_RPC_LOGIN_BITCOIN_CONF_LOCATION * remove default * fix tests * fix the tests part2 * fix cypress tests * docstrings and other default removals * improve error_handling * do not raise exception * fixed debugging artifact * Spinning refresh icon if node is still syncing * Cypress test for sync status * remove notification of fully synced node * Set timeout for sync check to 5min --------- Co-authored-by: moneymanolis <moneymanolis@protonmail.com> Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>
This commit is contained in:
parent
02b95af61f
commit
3ee881805e
34 changed files with 946 additions and 356 deletions
|
|
@ -45,9 +45,16 @@ describe('Connecting nodes', () => {
|
|||
|
||||
// TODO: For testing the deletion we could delete the Liquid connection here if we don't run the Liquid tests
|
||||
it('Select Bitcoin Core connection', () => {
|
||||
cy.get('#node-switch-icon').click()
|
||||
cy.contains('Bitcoin Core').click()
|
||||
cy.contains('Switched to use Bitcoin Core as connection')
|
||||
cy.get('#node-switch-icon').click()
|
||||
cy.contains('Bitcoin Core').click()
|
||||
cy.contains('Switched to use Bitcoin Core as connection')
|
||||
})
|
||||
|
||||
it('Check sync status of Bitcoin Core node', () => {
|
||||
cy.intercept("GET", "/nodes/sync_status/", {'fullySynced': false});
|
||||
cy.visit('/')
|
||||
cy.contains('Your Bitcoin node is syncing.')
|
||||
cy.get('[data-cy="unfinished-sync-indicator"]').should('be.visible')
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
|||
|
|
@ -77,6 +77,13 @@ class BaseConfig(object):
|
|||
# CERT and KEY is for running self-signed-ssl-certs. Check cli_server for details
|
||||
CERT = os.getenv("CERT", None)
|
||||
KEY = os.getenv("KEY", None)
|
||||
|
||||
# This will be used to search for a bitcoin.conf in order to enable the
|
||||
# auth method "RPC password as pin"
|
||||
RASPIBLITZ_SPECTER_RPC_LOGIN_BITCOIN_CONF_LOCATION = os.getenv(
|
||||
"RASPIBLITZ_SPECTER_RPC_LOGIN_BITCOIN_CONF_LOCATION", "/mnt/hdd/bitcoin"
|
||||
)
|
||||
|
||||
# This will get passed to initialize the specter-object
|
||||
DEFAULT_SPECTER_CONFIG = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from typing import Type
|
|||
|
||||
from cryptoadvance.specter.util.reflection import get_subclasses_for_clazz
|
||||
from .key import Key
|
||||
from typing import List
|
||||
from .persistence import read_json_file, write_json_file
|
||||
import logging
|
||||
from .helpers import is_testnet, is_liquid
|
||||
|
|
@ -44,7 +45,7 @@ class Device:
|
|||
"""
|
||||
self.name = name
|
||||
self.alias = alias
|
||||
self.keys = keys
|
||||
self.keys: List[Key] = keys
|
||||
self.fullpath = fullpath
|
||||
self.blinding_key = blinding_key
|
||||
self.manager = manager
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class ConfigManager(GenericDataManager):
|
|||
"asset_labels": {
|
||||
"liquidv1": {},
|
||||
},
|
||||
"active_node_alias": "default",
|
||||
"active_node_alias": None,
|
||||
"proxy_url": "socks5h://localhost:9050", # Tor proxy URL
|
||||
"only_tor": False,
|
||||
"tor_control_port": "",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from ..rpc import get_default_datadir, RPC_PORTS
|
|||
from ..specter_error import SpecterError, SpecterInternalException
|
||||
from ..persistence import PersistentObject, write_node, delete_file
|
||||
from ..helpers import alias, calc_fullpath, load_jsons
|
||||
from ..node import Node
|
||||
from ..node import Node, NonExistingNode
|
||||
from ..internal_node import InternalNode
|
||||
from ..services import callbacks
|
||||
from ..managers.service_manager import ServiceManager
|
||||
|
|
@ -18,14 +18,11 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class NodeManager:
|
||||
# chain is required to manage wallets when bitcoind is not running
|
||||
DEFAULT_ALIAS = "default"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
proxy_url="socks5h://localhost:9050",
|
||||
only_tor=False,
|
||||
active_node="default",
|
||||
active_node=None,
|
||||
bitcoind_path="",
|
||||
internal_bitcoind_version="",
|
||||
data_folder="",
|
||||
|
|
@ -56,6 +53,7 @@ class NodeManager:
|
|||
if not os.path.isdir(data_folder):
|
||||
os.mkdir(data_folder)
|
||||
nodes_files = load_jsons(self.data_folder, key="alias")
|
||||
logger.debug(nodes_files)
|
||||
for node_alias in nodes_files:
|
||||
try:
|
||||
valid_node = True
|
||||
|
|
@ -101,49 +99,15 @@ class NodeManager:
|
|||
port=7041,
|
||||
host="localhost",
|
||||
protocol="http",
|
||||
default_alias=self.DEFAULT_ALIAS,
|
||||
)
|
||||
logger.debug(
|
||||
"Creating an external BTC node with the initial configuration."
|
||||
)
|
||||
self.add_external_node(
|
||||
node_type="BTC",
|
||||
name="",
|
||||
autodetect=False,
|
||||
datadir=get_default_datadir(),
|
||||
user="",
|
||||
password="",
|
||||
port=8332,
|
||||
host="localhost",
|
||||
protocol="http",
|
||||
default_alias=self.DEFAULT_ALIAS,
|
||||
)
|
||||
|
||||
# Make sure we always have the default node
|
||||
# (needed for the rpc-as-pin-authentication used on Raspiblitz)
|
||||
has_default_node = False
|
||||
for node in self.nodes.values():
|
||||
if node.alias == self.DEFAULT_ALIAS:
|
||||
has_default_node = True
|
||||
# Recreate the default node if it doesn't exist anymore
|
||||
if not has_default_node:
|
||||
logger.debug("Recreating the default node.")
|
||||
self.add_external_node(
|
||||
node_type="BTC",
|
||||
name="",
|
||||
autodetect=False,
|
||||
datadir=get_default_datadir(),
|
||||
user="",
|
||||
password="",
|
||||
port=8332,
|
||||
host="localhost",
|
||||
protocol="http",
|
||||
default_alias=self.DEFAULT_ALIAS,
|
||||
)
|
||||
|
||||
@property
|
||||
def active_node(self) -> Node:
|
||||
return self.get_by_alias(self._active_node)
|
||||
"""returns the current active node or a NonExistingNode
|
||||
if no node is active, currently.
|
||||
"""
|
||||
active_node = self.get_by_alias(self._active_node)
|
||||
return active_node if active_node else NonExistingNode()
|
||||
|
||||
@property
|
||||
def nodes_names(self) -> list:
|
||||
|
|
@ -155,26 +119,37 @@ class NodeManager:
|
|||
return [node for node in self.nodes.values() if node.chain == chain]
|
||||
|
||||
def switch_node(self, node_alias: str):
|
||||
# This will throw an error if the node doesn't exist
|
||||
"""This will throw an SpecterError if the node doesn't exist.
|
||||
It won't persist anything! Use specter.update_active_node to persist!
|
||||
"""
|
||||
new_node = self.get_by_alias(node_alias)
|
||||
if not new_node:
|
||||
raise SpecterError(f"Node alias {node_alias} does not exist!")
|
||||
logger.debug(f"Switching from {self._active_node} to {node_alias}.")
|
||||
self._active_node = self.get_by_alias(node_alias).alias
|
||||
|
||||
def default_node(self) -> Node:
|
||||
return self.get_by_alias(self.DEFAULT_ALIAS)
|
||||
self._active_node = node_alias
|
||||
|
||||
def get_by_alias(self, alias: str) -> Node:
|
||||
"""Returns a Node instance for the given alias.
|
||||
None if a node with that alias doesn't exist
|
||||
"""
|
||||
for node in self.nodes.values():
|
||||
if node.alias == alias:
|
||||
return node
|
||||
raise SpecterError("Node alias %s does not exist!" % alias)
|
||||
return None
|
||||
|
||||
def get_by_name(self, name: str) -> Node:
|
||||
"""Returns a Node instance for the given alias.
|
||||
raises an SpecterError if it doesn't exist
|
||||
"""
|
||||
for node in self.nodes.values():
|
||||
if node.name == name:
|
||||
return node
|
||||
raise SpecterError("Node name %s does not exist!" % name)
|
||||
|
||||
def get_name_from_alias(self, alias: str) -> str:
|
||||
"""Returns the name for a specific node alias
|
||||
raises an SpecterError if it doesn't exist
|
||||
"""
|
||||
for node in self.nodes.values():
|
||||
if node.alias == alias:
|
||||
return node.name
|
||||
|
|
@ -200,26 +175,25 @@ class NodeManager:
|
|||
|
||||
def add_external_node(
|
||||
self,
|
||||
node_type,
|
||||
name,
|
||||
autodetect,
|
||||
node_type: str,
|
||||
name: str,
|
||||
autodetect: bool,
|
||||
datadir,
|
||||
user,
|
||||
password,
|
||||
port,
|
||||
host,
|
||||
protocol,
|
||||
default_alias=None,
|
||||
user: str,
|
||||
password: str,
|
||||
port: str,
|
||||
host: str,
|
||||
protocol: str,
|
||||
):
|
||||
"""Adding a node. Params:
|
||||
:param node_type: Either BTC or ELM, used to distinguish between Bitcoin and Liquid nodes.
|
||||
"""Adding a node and saves it to disk as well. Params:
|
||||
* node_type: only valid for autodetect. Either BTC or ELM
|
||||
* name: A nice name for this node. The alias will get calculated out of that
|
||||
* autodetect (boolean): whether this node should get autodetected
|
||||
* datadir: questionable! Why is that here needed?!
|
||||
This should only be used for an external node. Use add_internal_node for internal node
|
||||
and if you have defined your own node type, use save_node directly to save the node (and create it yourself)
|
||||
"""
|
||||
if not default_alias:
|
||||
node_alias = alias(name)
|
||||
else:
|
||||
node_alias = default_alias
|
||||
node_alias = alias(name)
|
||||
fullpath = os.path.join(self.data_folder, "%s.json" % node_alias)
|
||||
i = 2
|
||||
while os.path.isfile(fullpath):
|
||||
|
|
@ -247,6 +221,8 @@ class NodeManager:
|
|||
return node
|
||||
|
||||
def save_node(self, node):
|
||||
"""writes the node to disk. Will also apply a fullpath based on the datadir of the
|
||||
NodeManager if the node doesn't have one."""
|
||||
if not hasattr(node, "fullpath"):
|
||||
node.fullpath = calc_fullpath(self.data_folder, node.alias)
|
||||
write_node(node, node.fullpath)
|
||||
|
|
@ -254,20 +230,16 @@ class NodeManager:
|
|||
|
||||
def add_internal_node(
|
||||
self,
|
||||
name,
|
||||
name: str,
|
||||
network="main",
|
||||
port=None,
|
||||
default_alias=None,
|
||||
port: str = None,
|
||||
datadir=None,
|
||||
):
|
||||
"""Adding an internal node. Params:
|
||||
This should only be used for internal nodes. Use add__External_node for external nodes
|
||||
and if you have defined your own node-type, use save_node directly. to save the node (and create it yourself)
|
||||
"""
|
||||
if not default_alias:
|
||||
node_alias = alias(name)
|
||||
else:
|
||||
node_alias = default_alias
|
||||
node_alias = alias(name)
|
||||
fullpath = os.path.join(self.data_folder, "%s.json" % node_alias)
|
||||
i = 2
|
||||
while os.path.isfile(fullpath):
|
||||
|
|
@ -298,6 +270,7 @@ class NodeManager:
|
|||
return node
|
||||
|
||||
def delete_node(self, node, specter):
|
||||
"""Deletes the node. Also from the disk."""
|
||||
logger.info("Deleting {}".format(node.alias))
|
||||
try:
|
||||
# Delete from wallet manager
|
||||
|
|
@ -306,7 +279,7 @@ class NodeManager:
|
|||
delete_file(node.fullpath)
|
||||
delete_file(node.fullpath + ".bkp")
|
||||
# Update the active node
|
||||
if self._active_node == node.alias:
|
||||
if self._active_node == node.alias and len(self.nodes) > 0:
|
||||
specter.update_active_node(
|
||||
next(iter(self.nodes.values())).alias
|
||||
) # This switches to the first node in the node list, which is usually the default node
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from cryptoadvance.specter.key import Key
|
|||
from ..helpers import add_dicts, alias, is_liquid, load_jsons
|
||||
from ..liquid.wallet import LWallet
|
||||
from ..persistence import delete_folder
|
||||
from ..rpc import RpcError, get_default_datadir
|
||||
from ..rpc import RpcError, get_default_datadir, BrokenCoreConnectionException
|
||||
from ..specter_error import SpecterError, SpecterInternalException, handle_exception
|
||||
from ..util.flask import FlaskThread
|
||||
from ..wallet import ( # TODO: `purposes` unused here, but other files rely on this import
|
||||
|
|
@ -31,7 +31,6 @@ class WalletManager:
|
|||
# chain is required to manage wallets when bitcoind is not running
|
||||
def __init__(
|
||||
self,
|
||||
bitcoin_core_version_raw,
|
||||
data_folder,
|
||||
rpc,
|
||||
chain,
|
||||
|
|
@ -50,8 +49,6 @@ class WalletManager:
|
|||
# key is the name of the wallet, value is the actual instance
|
||||
|
||||
self.wallets = {}
|
||||
# A way to communicate failed wallets to the outside
|
||||
self.bitcoin_core_version_raw = bitcoin_core_version_raw
|
||||
self.allow_threading_for_testing = allow_threading_for_testing
|
||||
# define different wallet classes for liquid and bitcoin
|
||||
self.WalletClass = LWallet if is_liquid(chain) else Wallet
|
||||
|
|
@ -333,6 +330,16 @@ class WalletManager:
|
|||
self._wallets[self.chain] = {}
|
||||
self._wallets[self.chain] = value
|
||||
|
||||
@property
|
||||
def bitcoin_core_version_raw(self):
|
||||
try:
|
||||
bitcoin_core_version_raw = self.rpc.getnetworkinfo()["version"]
|
||||
return bitcoin_core_version_raw or 200000
|
||||
except BrokenCoreConnectionException:
|
||||
# In good faith and in order to keep the tests running, we assume
|
||||
# a reasonable core version
|
||||
return 200000
|
||||
|
||||
def create_wallet(self, name, sigs_required, key_type, keys, devices, **kwargs):
|
||||
try:
|
||||
walletsindir = [
|
||||
|
|
|
|||
|
|
@ -28,7 +28,94 @@ from .device import Device
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AbstractNode(PersistentObject):
|
||||
class NonExistingNode(PersistentObject):
|
||||
"""A kind of Null-object as it represents a non-existing Node. It's deriving from PersistentObject but is not meant to be
|
||||
persisted. Instead, it's be created on the fly from the NodeManager if it doesn't have a reasonable node available.
|
||||
It also works as some kind of minimal implementation so that specter doesn't fail gracefully.
|
||||
"""
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
return {}
|
||||
|
||||
@property
|
||||
def network_info(self):
|
||||
return {}
|
||||
|
||||
@property
|
||||
def uptime(self):
|
||||
return -1
|
||||
|
||||
@property
|
||||
def chain(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def bitcoin_core_version_raw(self):
|
||||
return 9999999
|
||||
|
||||
def update_rpc(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def is_running(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def rpc(self):
|
||||
return None
|
||||
|
||||
def check_blockheight(self):
|
||||
"""check_blockheight is a method which is probably deprecated.
|
||||
It should return True if there are new blocks available since check_info has been called
|
||||
(which updates the cached _info[] dict)
|
||||
"""
|
||||
raise NotImplemented(
|
||||
"A Node Implementation need to implement the check_blockheight method"
|
||||
)
|
||||
|
||||
def is_device_supported(self, device_class_or_device_instance):
|
||||
"""Lets the node deactivate specific devices. The parameter could be a device or a device_type
|
||||
You have to check yourself if overriding this method.
|
||||
e.g.
|
||||
if device_instance_or_device_class.__class__ == type:
|
||||
device_class = device_instance_or_device_class
|
||||
else:
|
||||
device_class = device_instance_or_device_class.__class__
|
||||
# example:
|
||||
# if BitcoinCore == device_class:
|
||||
# return False
|
||||
return True
|
||||
"""
|
||||
return True
|
||||
|
||||
def node_info_template(self):
|
||||
"""This should return the path to a Info template as string"""
|
||||
return "node/components/bitcoin_core_info.jinja"
|
||||
|
||||
def node_logo_template(self):
|
||||
"""This should return the path to a Logo template as string
|
||||
The template should contain the logo independent from the
|
||||
status of the node. It's used in the node-selector
|
||||
"""
|
||||
return "includes/sidebar/components/node_logo.jinja"
|
||||
|
||||
def node_connection_template(self):
|
||||
"""This should return the path to a connection template as string"""
|
||||
return "includes/sidebar/components/node_connection.jinja"
|
||||
|
||||
def delete_wallet_file(self, wallet) -> bool:
|
||||
"""Deleting the wallet file located on the node. This only works if the node is on the same machine as Specter.
|
||||
Returns True if the wallet file could be deleted, otherwise returns False.
|
||||
|
||||
In the case of an Abtract Node, we consider that method as an edge-case anyway and we just return False here.
|
||||
That is the normal usage if you don't have access to the internals of your Bitcoin Core.
|
||||
Overwrite as necessary.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
class AbstractNode(NonExistingNode):
|
||||
"""This is a Node class worth deriving from. It tries to define as many attributes as possible which are needed but probably in a very
|
||||
inefficient way, e.g. without any caching. Feel free to improve that in subclasses and you might get inspired by existing sublasses
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -501,7 +501,8 @@ class BitcoinRPC:
|
|||
r = self.multi([(method, *args)], **kwargs)[0]
|
||||
if r["error"] is not None:
|
||||
raise RpcError(
|
||||
f"Request error for method {method}: {r['error']['message']}", r
|
||||
f"Request error for method {method}{args}: {r['error']['message']}",
|
||||
r,
|
||||
)
|
||||
return r["result"]
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,13 @@ from flask import jsonify, redirect, render_template, request, url_for
|
|||
from flask_babel import lazy_gettext as _
|
||||
from flask_login import current_user, login_required, logout_user
|
||||
|
||||
from cryptoadvance.specter.specter import Specter
|
||||
|
||||
from ..helpers import alias
|
||||
from ..server_endpoints import flash
|
||||
from ..services import ExtensionException
|
||||
from ..user import User, hash_password, verify_password
|
||||
from ..rpc import BitcoinRPC, _detect_rpc_confs_via_datadir
|
||||
|
||||
rand = random.randint(0, 1e32) # to force style refresh
|
||||
last_sensitive_request = 0 # to rate limit sensitive requests
|
||||
|
|
@ -28,60 +31,13 @@ def login():
|
|||
auth = app.specter.config["auth"]
|
||||
|
||||
if auth["method"] == "none":
|
||||
app.login("admin")
|
||||
app.logger.info("AUDIT: Successful Login no credentials")
|
||||
return redirect_login(request)
|
||||
|
||||
if auth["method"] == "rpcpasswordaspin":
|
||||
# TODO: check the password via RPC-call
|
||||
if (
|
||||
app.specter.default_node.rpc is None
|
||||
or not app.specter.default_node.rpc.test_connection()
|
||||
):
|
||||
if app.specter.default_node.password == request.form["password"]:
|
||||
app.login("admin", request.form["password"])
|
||||
app.logger.info(
|
||||
"AUDIT: Successfull Login via RPC-credentials (node disconnected)"
|
||||
)
|
||||
return redirect_login(request)
|
||||
|
||||
flash(
|
||||
_(
|
||||
"We could not check your password, maybe Bitcoin Core is not running or not configured?"
|
||||
),
|
||||
"error",
|
||||
)
|
||||
app.logger.info("AUDIT: Failed to check password")
|
||||
return (
|
||||
render_template(
|
||||
"login.jinja",
|
||||
specter=app.specter,
|
||||
data={"controller": "controller.login"},
|
||||
),
|
||||
401,
|
||||
)
|
||||
rpc = app.specter.default_node.rpc.clone()
|
||||
rpc.password = request.form["password"]
|
||||
if rpc.test_connection():
|
||||
app.login("admin", request.form["password"])
|
||||
app.logger.info("AUDIT: Successfull Login via RPC-credentials")
|
||||
return redirect_login(request)
|
||||
|
||||
return login_method_none()
|
||||
elif auth["method"] == "rpcpasswordaspin":
|
||||
return login_method_rpcpasswordaspin()
|
||||
elif auth["method"] == "passwordonly":
|
||||
password = request.form["password"]
|
||||
if verify_password(app.specter.user_manager.admin.password_hash, password):
|
||||
app.login("admin", request.form["password"])
|
||||
return redirect_login(request)
|
||||
|
||||
return login_method_passwordonly()
|
||||
elif auth["method"] == "usernamepassword":
|
||||
# TODO: This way both "User" and "user" will pass as usernames, should there be strict check on that here? Or should we keep it like this?
|
||||
username = request.form["username"]
|
||||
password = request.form["password"]
|
||||
user = app.specter.user_manager.get_user_by_username(username)
|
||||
if user:
|
||||
if verify_password(user.password_hash, password):
|
||||
app.login(user.id, request.form["password"])
|
||||
return redirect_login(request)
|
||||
return login_method_usernamepassword()
|
||||
|
||||
# Either invalid method or incorrect credentials
|
||||
flash(_("Invalid username or password"), "error")
|
||||
|
|
@ -103,6 +59,83 @@ def login():
|
|||
)
|
||||
|
||||
|
||||
def deny_login(comment=""):
|
||||
flash("Invalid user or password!")
|
||||
app.logger.info("AUDIT: Invalid password login attempt")
|
||||
return redirect("login")
|
||||
|
||||
|
||||
def login_method_none():
|
||||
app.login("admin")
|
||||
app.logger.info("AUDIT: Successful Login no credentials")
|
||||
return redirect_login(request)
|
||||
|
||||
|
||||
def login_method_rpcpasswordaspin():
|
||||
# This authentiaction method has been especially created for Raspiblitz
|
||||
# We assume here, that no preconfigured node-connection is available
|
||||
# otherwise, which configured node should we use?
|
||||
# We want to get rid of the default node, so we can't use that
|
||||
# Instead we use a default location where we assume the bitcoin.conf
|
||||
# This can be overridden via ENV_var.
|
||||
|
||||
specter: Specter = app.specter
|
||||
if specter.node and specter.node._get_rpc().test_connection():
|
||||
rpc = specter.node._get_rpc().clone()
|
||||
else:
|
||||
confs = _detect_rpc_confs_via_datadir(
|
||||
None,
|
||||
datadir=app.config["RASPIBLITZ_SPECTER_RPC_LOGIN_BITCOIN_CONF_LOCATION"],
|
||||
)
|
||||
if len(confs) == 0:
|
||||
flash(
|
||||
"No RPC connection to Bitcoin Core found. Cannot Log you in.", "error"
|
||||
)
|
||||
return redirect(url_for("login"))
|
||||
conf = confs[0]
|
||||
rpc = BitcoinRPC(**conf)
|
||||
|
||||
# A bit redundant as this has been checked before but let's be sure
|
||||
if not rpc.test_connection():
|
||||
flash(
|
||||
"It seems that there is no working RPC connection to Bitcoin Core. Cannot Log you in."
|
||||
)
|
||||
return redirect(url_for("login"))
|
||||
orig_password = rpc.password
|
||||
rpc.password = request.form["password"]
|
||||
if rpc.password == request.form["password"] and rpc.test_connection():
|
||||
app.login("admin", request.form["password"])
|
||||
app.logger.info("AUDIT: Successfull Login via RPC-credentials")
|
||||
return redirect_login(request)
|
||||
if orig_password == request.form["password"]:
|
||||
app.login("admin", request.form["password"])
|
||||
app.logger.info(
|
||||
f"AUDIT: Successfull Login via RPC-credentials (node not reachable) {rpc.password}"
|
||||
)
|
||||
return redirect_login(request)
|
||||
return deny_login(comment="Pin")
|
||||
|
||||
|
||||
def login_method_passwordonly():
|
||||
password = request.form["password"]
|
||||
if verify_password(app.specter.user_manager.admin.password_hash, password):
|
||||
app.login("admin", request.form["password"])
|
||||
return redirect_login(request)
|
||||
return deny_login(comment="passwordonly")
|
||||
|
||||
|
||||
def login_method_usernamepassword():
|
||||
# TODO: This way both "User" and "user" will pass as usernames, should there be strict check on that here? Or should we keep it like this?
|
||||
username = request.form["username"]
|
||||
password = request.form["password"]
|
||||
user = app.specter.user_manager.get_user_by_username(username)
|
||||
if user:
|
||||
if verify_password(user.password_hash, password):
|
||||
app.login(user.id, request.form["password"])
|
||||
return redirect_login(request)
|
||||
return deny_login(comment="usernamepassword")
|
||||
|
||||
|
||||
@auth_endpoint.route("/register", methods=["GET", "POST"])
|
||||
def register():
|
||||
"""register"""
|
||||
|
|
|
|||
|
|
@ -553,3 +553,13 @@ def rename_node():
|
|||
node.rename(new_name)
|
||||
response = {"nameChanged": True, "error": None}
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
@nodes_endpoint.route("sync_status/", methods=["GET"])
|
||||
@login_required
|
||||
def check_sync_status():
|
||||
if app.specter.info.get("initialblockdownload") == True:
|
||||
response = {"fullySynced": False}
|
||||
else:
|
||||
response = {"fullySynced": True}
|
||||
return jsonify(response)
|
||||
|
|
|
|||
|
|
@ -127,10 +127,8 @@ class Specter:
|
|||
)
|
||||
except SpecterError as e:
|
||||
if str(e).endswith("does not exist!"):
|
||||
logger.warning(
|
||||
f"Current Node doesn't exist. Switching over to node {self.node_manager.DEFAULT_ALIAS}."
|
||||
)
|
||||
self.update_active_node(self.node_manager.DEFAULT_ALIAS)
|
||||
if len(self.node_manager.nodes) > 0:
|
||||
self.update_active_node(next(iter(self.nodes.values())).alias)
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
|
@ -237,13 +235,7 @@ class Specter:
|
|||
|
||||
@property
|
||||
def node(self) -> Node:
|
||||
try:
|
||||
return self.node_manager.active_node
|
||||
except SpecterError as e:
|
||||
logger.error("SpecterError while accessing active_node")
|
||||
logger.exception(e)
|
||||
self.update_active_node(list(self.node_manager.nodes.values())[0].alias)
|
||||
return self.node_manager.active_node
|
||||
return self.node_manager.active_node
|
||||
|
||||
@property
|
||||
def default_node(self):
|
||||
|
|
@ -565,7 +557,7 @@ class Specter:
|
|||
|
||||
@property
|
||||
def active_node_alias(self):
|
||||
return self.user_config.get("active_node_alias", "default")
|
||||
return self.user_config.get("active_node_alias", None)
|
||||
|
||||
@property
|
||||
def explorer(self):
|
||||
|
|
@ -745,7 +737,7 @@ class Specter:
|
|||
old_internal_rpc = self.config.get("internal_node", None)
|
||||
if old_internal_rpc and os.path.isfile(self.bitcoind_path):
|
||||
internal_node = InternalNode(
|
||||
"Specter Bitcoin",
|
||||
"Specter Bitcoin internal",
|
||||
"specter_bitcoin",
|
||||
old_internal_rpc.get("autodetect", False),
|
||||
old_internal_rpc.get("datadir", get_default_datadir()),
|
||||
|
|
@ -755,7 +747,7 @@ class Specter:
|
|||
old_internal_rpc.get("host", "localhost"),
|
||||
old_internal_rpc.get("protocol", "http"),
|
||||
os.path.join(
|
||||
os.path.join(self.data_folder, "nodes"), "specter_bitcoin.json"
|
||||
os.path.join(self.data_folder, "nodes"), "bitcoin_core.json"
|
||||
),
|
||||
self,
|
||||
self.bitcoind_path,
|
||||
|
|
@ -776,7 +768,7 @@ class Specter:
|
|||
if old_rpc:
|
||||
node = Node(
|
||||
"Bitcoin Core",
|
||||
"default",
|
||||
"bitcoin_core",
|
||||
old_rpc.get("autodetect", True),
|
||||
old_rpc.get("datadir", get_default_datadir()),
|
||||
old_rpc.get("user", ""),
|
||||
|
|
@ -784,16 +776,21 @@ class Specter:
|
|||
old_rpc.get("port", None),
|
||||
old_rpc.get("host", "localhost"),
|
||||
old_rpc.get("protocol", "http"),
|
||||
os.path.join(os.path.join(self.data_folder, "nodes"), "default.json"),
|
||||
os.path.join(
|
||||
os.path.join(self.data_folder, "nodes"), "bitcoin_core.json"
|
||||
),
|
||||
"BTC",
|
||||
self,
|
||||
)
|
||||
logger.info(f"persisting {node} in migrate_old_node_format")
|
||||
write_node(
|
||||
node,
|
||||
os.path.join(os.path.join(self.data_folder, "nodes"), "default.json"),
|
||||
os.path.join(
|
||||
os.path.join(self.data_folder, "nodes"), "bitcoin_core.json"
|
||||
),
|
||||
)
|
||||
del self.config["rpc"]
|
||||
self.config_manager.update_active_node("bitcoin_core")
|
||||
self._save()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -48,8 +48,13 @@ class ExtProcTimeoutException(SpecterInternalException):
|
|||
|
||||
def handle_exception(exception, user=None):
|
||||
"""prints the exception and most important the stacktrace"""
|
||||
if app.config["SPECTER_CONFIGURATION_CLASS_FULLNAME"].endswith("DevelopmentConfig"):
|
||||
raise exception
|
||||
try:
|
||||
if app.config["SPECTER_CONFIGURATION_CLASS_FULLNAME"].endswith(
|
||||
"DevelopmentConfig"
|
||||
):
|
||||
raise exception
|
||||
except RuntimeError: # Application context might be missing
|
||||
pass
|
||||
logger.error("Unexpected error:")
|
||||
logger.error(
|
||||
"----START-TRACEBACK-----------------------------------------------------------------"
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ async function send_request(url, method_str, csrf_token, formData) {
|
|||
return jsonResponse
|
||||
}
|
||||
catch(error) {
|
||||
showError(`Failed to fetch transactions list: ${error}`)
|
||||
showError(`Error occured durch fetch call: ${error}`)
|
||||
return { 'error': error}
|
||||
}
|
||||
}
|
||||
|
|
@ -863,6 +863,10 @@ select {
|
|||
background-color: rgb(27 28 33 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.animate-spin-slow {
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
*, ::before, ::after {
|
||||
--tw-border-spacing-x: 0;
|
||||
--tw-border-spacing-y: 0;
|
||||
|
|
@ -1114,6 +1118,10 @@ input[type="number"]::-webkit-outer-spin-button,
|
|||
margin: auto;
|
||||
}
|
||||
|
||||
.m-3 {
|
||||
margin: 0.75rem;
|
||||
}
|
||||
|
||||
.m-4 {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
|
@ -1740,6 +1748,11 @@ input[type="number"]::-webkit-outer-spin-button,
|
|||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.rounded-l-md {
|
||||
border-top-left-radius: 0.375rem;
|
||||
border-bottom-left-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.rounded-t-xl {
|
||||
border-top-left-radius: 0.75rem;
|
||||
border-top-right-radius: 0.75rem;
|
||||
|
|
@ -1896,6 +1909,10 @@ input[type="number"]::-webkit-outer-spin-button,
|
|||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.px-\[calc\(\(100\%-700px\)\/2\)\] {
|
||||
padding-left: calc((100% - 700px) / 2);
|
||||
padding-right: calc((100% - 700px) / 2);
|
||||
|
|
@ -2023,6 +2040,11 @@ input[type="number"]::-webkit-outer-spin-button,
|
|||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-xs {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
|
||||
.font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
|
@ -2171,10 +2193,18 @@ input[type="number"]::-webkit-outer-spin-button,
|
|||
transition-duration: 200ms;
|
||||
}
|
||||
|
||||
.duration-1000 {
|
||||
transition-duration: 1000ms;
|
||||
}
|
||||
|
||||
.ease-out {
|
||||
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.ease-in-out {
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: "Social";
|
||||
font-style: normal;
|
||||
|
|
|
|||
|
|
@ -185,6 +185,10 @@
|
|||
.selection-button {
|
||||
@apply rounded-lg w-full h-10 bg-dark-800 border-2 border-dark-700 text-center block text-lg py-1 hover:bg-dark-700;
|
||||
}
|
||||
|
||||
.animate-spin-slow {
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
{% set node = specter.node %}
|
||||
|
||||
{% if node.is_running %}
|
||||
<div class="px-3 py-2 h-full min-w-0 grow flex items-center cursor-pointer" id="active-node" onclick="showPageOverlay('node-info-popup', {top: '64px', left: '16px'});document.getElementById('side-content').classList.remove('active');" >
|
||||
<svg class="w-7 h-7 text-white" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><!--Generated by IJSVG (https://github.com/iconjar/IJSVG)--><path d="M54.5946,27.4366l5.66603e-08,-5.66066e-08c-0.745305,0.744598 -1.9414,0.780481 -2.73,0.0819001l1.11948e-07,9.86842e-08c-11.3381,-9.99478 -28.3408,-9.99478 -39.6789,-1.97368e-07l4.1375e-08,-3.6659e-08c-0.788564,0.698682 -1.98474,0.662797 -2.73,-0.0818999l-2.831,-2.8295l4.78383e-08,4.78239e-08c-0.782846,-0.782609 -0.783038,-2.05166 -0.000429116,-2.83451c0.0255969,-0.0256047 0.0518829,-0.0505111 0.078829,-0.0746918l-1.08875e-06,9.72979e-07c14.4215,-12.888 36.2226,-12.888 50.6441,-1.94596e-06l-4.18137e-09,-3.75185e-09c0.823858,0.73923 0.892463,2.00636 0.153233,2.83022c-0.0242251,0.0269984 -0.0491787,0.0533343 -0.0748332,0.0789783Zm-17.6311,13.896l1.74829e-07,9.87123e-08c1.00267,0.56613 1.35656,1.8379 0.79043,2.84057c-0.0942659,0.166954 -0.21111,0.320108 -0.34723,0.45513l-3.6928,3.6684l2.2751e-08,-2.25368e-08c-0.943884,0.934995 -2.46482,0.934995 -3.4087,4.50735e-08l-3.6928,-3.6684l-6.02819e-08,-5.98007e-08c-0.8175,-0.810974 -0.82279,-2.13111 -0.0118161,-2.94861c0.134967,-0.136053 0.288047,-0.252849 0.454916,-0.347088l2.96297e-07,-1.66902e-07c3.07549,-1.7324 6.83251,-1.7324 9.908,3.33805e-07Zm-14.8508,-3.792l3.86385e-08,-3.1954e-08c-0.835403,0.690877 -2.05882,0.634623 -2.8273,-0.13l-2.3847,-2.4084c-1.1739,-1.1855 -1.14,-2.6381 -0.1975,-3.4266l9.62086e-07,-8.05987e-07c8.84671,-7.41133 21.7347,-7.41133 30.5814,1.61198e-06l-5.08261e-08,-4.40698e-08c0.88879,0.770643 0.984568,2.11588 0.213926,3.00467c-0.0332999,0.0384052 -0.0679639,0.075606 -0.103925,0.111531l-2.6883,2.7151l2.63897e-08,-2.62774e-08c-0.768944,0.765671 -1.99317,0.823532 -2.8309,0.1338l-1.43462e-07,-1.17058e-07c-5.75168,-4.69307 -14.0109,-4.69307 -19.7626,2.34116e-07Z" fill="currentColor" fill-rule="evenodd"></path></svg>
|
||||
<div class="px-3 py-2 h-full min-w-0 grow flex items-center cursor-pointer" id="active-node" onclick="showPageOverlay('node-info-popup', {top: '64px', left: '16px'});document.getElementById('side-content').classList.remove('active');" >
|
||||
{% if specter.info.get("initialblockdownload") == True %}
|
||||
<img class="rounded-full bg-dark-700 w-4 h-4 animate-spin-slow" id="unfinished-ibd-indicator" src="{{ url_for('static', filename='img/refresh.svg') }}" data-cy="unfinished-sync-indicator"/>
|
||||
<svg class="w-7 h-7 text-white hidden" id="connected-icon" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><!--Generated by IJSVG (https://github.com/iconjar/IJSVG)--><path d="M54.5946,27.4366l5.66603e-08,-5.66066e-08c-0.745305,0.744598 -1.9414,0.780481 -2.73,0.0819001l1.11948e-07,9.86842e-08c-11.3381,-9.99478 -28.3408,-9.99478 -39.6789,-1.97368e-07l4.1375e-08,-3.6659e-08c-0.788564,0.698682 -1.98474,0.662797 -2.73,-0.0818999l-2.831,-2.8295l4.78383e-08,4.78239e-08c-0.782846,-0.782609 -0.783038,-2.05166 -0.000429116,-2.83451c0.0255969,-0.0256047 0.0518829,-0.0505111 0.078829,-0.0746918l-1.08875e-06,9.72979e-07c14.4215,-12.888 36.2226,-12.888 50.6441,-1.94596e-06l-4.18137e-09,-3.75185e-09c0.823858,0.73923 0.892463,2.00636 0.153233,2.83022c-0.0242251,0.0269984 -0.0491787,0.0533343 -0.0748332,0.0789783Zm-17.6311,13.896l1.74829e-07,9.87123e-08c1.00267,0.56613 1.35656,1.8379 0.79043,2.84057c-0.0942659,0.166954 -0.21111,0.320108 -0.34723,0.45513l-3.6928,3.6684l2.2751e-08,-2.25368e-08c-0.943884,0.934995 -2.46482,0.934995 -3.4087,4.50735e-08l-3.6928,-3.6684l-6.02819e-08,-5.98007e-08c-0.8175,-0.810974 -0.82279,-2.13111 -0.0118161,-2.94861c0.134967,-0.136053 0.288047,-0.252849 0.454916,-0.347088l2.96297e-07,-1.66902e-07c3.07549,-1.7324 6.83251,-1.7324 9.908,3.33805e-07Zm-14.8508,-3.792l3.86385e-08,-3.1954e-08c-0.835403,0.690877 -2.05882,0.634623 -2.8273,-0.13l-2.3847,-2.4084c-1.1739,-1.1855 -1.14,-2.6381 -0.1975,-3.4266l9.62086e-07,-8.05987e-07c8.84671,-7.41133 21.7347,-7.41133 30.5814,1.61198e-06l-5.08261e-08,-4.40698e-08c0.88879,0.770643 0.984568,2.11588 0.213926,3.00467c-0.0332999,0.0384052 -0.0679639,0.075606 -0.103925,0.111531l-2.6883,2.7151l2.63897e-08,-2.62774e-08c-0.768944,0.765671 -1.99317,0.823532 -2.8309,0.1338l-1.43462e-07,-1.17058e-07c-5.75168,-4.69307 -14.0109,-4.69307 -19.7626,2.34116e-07Z" fill="currentColor" fill-rule="evenodd"></path></svg>
|
||||
{% else %}
|
||||
<img class="rounded-full bg-dark-700 w-4 h-4 animate-spin-slow hidden" id="unfinished-ibd-indicator" src="{{ url_for('static', filename='img/refresh.svg') }}" data-cy="unfinished-sync-indicator"/>
|
||||
<svg class="w-7 h-7 text-white" id="connected-icon" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><!--Generated by IJSVG (https://github.com/iconjar/IJSVG)--><path d="M54.5946,27.4366l5.66603e-08,-5.66066e-08c-0.745305,0.744598 -1.9414,0.780481 -2.73,0.0819001l1.11948e-07,9.86842e-08c-11.3381,-9.99478 -28.3408,-9.99478 -39.6789,-1.97368e-07l4.1375e-08,-3.6659e-08c-0.788564,0.698682 -1.98474,0.662797 -2.73,-0.0818999l-2.831,-2.8295l4.78383e-08,4.78239e-08c-0.782846,-0.782609 -0.783038,-2.05166 -0.000429116,-2.83451c0.0255969,-0.0256047 0.0518829,-0.0505111 0.078829,-0.0746918l-1.08875e-06,9.72979e-07c14.4215,-12.888 36.2226,-12.888 50.6441,-1.94596e-06l-4.18137e-09,-3.75185e-09c0.823858,0.73923 0.892463,2.00636 0.153233,2.83022c-0.0242251,0.0269984 -0.0491787,0.0533343 -0.0748332,0.0789783Zm-17.6311,13.896l1.74829e-07,9.87123e-08c1.00267,0.56613 1.35656,1.8379 0.79043,2.84057c-0.0942659,0.166954 -0.21111,0.320108 -0.34723,0.45513l-3.6928,3.6684l2.2751e-08,-2.25368e-08c-0.943884,0.934995 -2.46482,0.934995 -3.4087,4.50735e-08l-3.6928,-3.6684l-6.02819e-08,-5.98007e-08c-0.8175,-0.810974 -0.82279,-2.13111 -0.0118161,-2.94861c0.134967,-0.136053 0.288047,-0.252849 0.454916,-0.347088l2.96297e-07,-1.66902e-07c3.07549,-1.7324 6.83251,-1.7324 9.908,3.33805e-07Zm-14.8508,-3.792l3.86385e-08,-3.1954e-08c-0.835403,0.690877 -2.05882,0.634623 -2.8273,-0.13l-2.3847,-2.4084c-1.1739,-1.1855 -1.14,-2.6381 -0.1975,-3.4266l9.62086e-07,-8.05987e-07c8.84671,-7.41133 21.7347,-7.41133 30.5814,1.61198e-06l-5.08261e-08,-4.40698e-08c0.88879,0.770643 0.984568,2.11588 0.213926,3.00467c-0.0332999,0.0384052 -0.0679639,0.075606 -0.103925,0.111531l-2.6883,2.7151l2.63897e-08,-2.62774e-08c-0.768944,0.765671 -1.99317,0.823532 -2.8309,0.1338l-1.43462e-07,-1.17058e-07c-5.75168,-4.69307 -14.0109,-4.69307 -19.7626,2.34116e-07Z" fill="currentColor" fill-rule="evenodd"></path></svg>
|
||||
{% endif %}
|
||||
<div id="sidebar-connection-name" class="ml-2 text-lg min-w-0 truncate">{{ node.name }}</div>
|
||||
<div class="hidden" id="node-info-popup">{% include node.node_info_template() %}</div>
|
||||
</div>
|
||||
|
|
@ -20,4 +25,35 @@
|
|||
document.addEventListener('connectionRenamed', (e) => {
|
||||
sidebarConnectionNameElement.innerText = e.detail.newName
|
||||
})
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
{% if node.is_running %}
|
||||
checkSyncStatus()
|
||||
{% endif %}
|
||||
})
|
||||
|
||||
let count = 0
|
||||
async function checkSyncStatus() {
|
||||
let url = `{{ url_for('nodes_endpoint.check_sync_status') }}`;
|
||||
const response = await send_request(url, 'GET', "{{ csrf_token() }}");
|
||||
if (response.fullySynced === true) {
|
||||
console.log('IBD is finished, Node is fully synced.')
|
||||
document.getElementById('unfinished-ibd-indicator').classList.remove('animate-spin')
|
||||
document.getElementById("unfinished-ibd-indicator").classList.add('hidden')
|
||||
document.getElementById('connected-icon').classList.remove('hidden')
|
||||
}
|
||||
else {
|
||||
console.log('IBD is not finished yet.')
|
||||
document.getElementById('unfinished-ibd-indicator').classList.remove('hidden')
|
||||
document.getElementById('unfinished-ibd-indicator').classList.add('animate-spin')
|
||||
document.getElementById('connected-icon').classList.add('hidden')
|
||||
console.log(count)
|
||||
// Show message only once
|
||||
if (count === 0 ) {
|
||||
showNotification(`{{ _("Your Bitcoin node is syncing. Check the progress by clicking on the name of the node.") }}`)
|
||||
}
|
||||
count++
|
||||
setTimeout(checkSyncStatus, 5*60000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,6 @@
|
|||
<div id="side-content" class="h-screen bg-dark-900 border-r border-dark-700 border-solid w-[300px] min-w-[300px] text-white select-none">
|
||||
<nav class="flex flex-col justify-between max-h-screen h-full">
|
||||
<div class="p-3 bg-dark-900">
|
||||
|
||||
{% if specter.info.get("initialblockdownload") %}
|
||||
<!-- <p class="warning" data-style="margin-top: 30px;"><img src="{{ url_for('static', filename='img/info_sign.svg') }}" data-style="width: 20px;"/><br>{{ _("Bitcoin Core is still syncing...")}}<br>{{ _('(data might be outdated)')}}</p> -->
|
||||
{% endif %}
|
||||
{% if specter.network_info.version < 200000 %}
|
||||
<!-- <p class="warning" data-style="margin-top: 30px;"><img src="{{ url_for('static', filename='img/warning_sign.svg') }}" data-style="width: 20px;"/><br>{{ _("Bitcoin Core version is outdated.")}}<br>{{ _('Some features might not work...')}}<br>{{ _("(minimum required: v20.0.0).") }}</p> -->
|
||||
{% endif %}
|
||||
|
||||
<!-- Connection -->
|
||||
<div class="h-11 flex flex-row justify-between items-center overflow-hidden mb-3 bg-dark-700 rounded-xl shadow-md hover:bg-dark-800 group">
|
||||
{% include specter.node.node_connection_template() %}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
<div class="bg-dark-800 flex flex-col">
|
||||
{% if specter.info.get("initialblockdownload") == True %}
|
||||
{% set verificationprogress = specter.info.get("verificationprogress") * 100 %}
|
||||
<div class="flex flex-col p-4 mt-3 mb-4">
|
||||
<p>{{ _("Bitcoin Core is still syncing")}}<p>
|
||||
<div class="w-full bg-dark-700 rounded-lg">
|
||||
<div class="bg-dark-600 text-xs text-white text-center p-2 leading-none {% if verificationprogress < 100 %} rounded-l-md {% else %} rounded-md {% endif %}" style="width: {{ verificationprogress | round | int }}%">{{ verificationprogress | round | int }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if specter.chain %}
|
||||
<table>
|
||||
<tr> <td data-style="text-align: left;">{{ _("Network") }}:</td> <td data-style="text-align: right;" id="node-info-specter-chain">{{specter.chain}}</td> </tr>
|
||||
|
|
@ -16,7 +25,16 @@
|
|||
<tr> <td data-style="text-align: left;">{{ _("Prune target size") }}:</td> <td data-style="text-align: right;">{{specter.info['prune_target_size']}}</td> </tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
|
||||
{% if specter.bitcoin_core_version_raw < 200000 %}
|
||||
<div class="flex p-4 m-3 mb-4 bg-dark-600 rounded-lg" role="alert">
|
||||
<svg class="flex-shrink-0 inline w-6 h-6 mr-3" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><!--Generated by IJSVG (https://github.com/iconjar/IJSVG)--><path d="M32.0022,55.9844l-1.04907e-06,-2.84217e-14c-13.2548,-5.79387e-07 -24,-10.7452 -24,-24c5.79387e-07,-13.2548 10.7452,-24 24,-24c13.2548,5.79387e-07 24,10.7452 24,24l2.13163e-14,-1.04907e-06c0,13.2548 -10.7452,24 -24,24Zm1.5,-32l3.82016e-08,-2.71086e-09c-1.79133,0.127116 -3.35165,-1.21031 -3.5,-3l6.50646e-08,-7.8524e-07c0.148296,-1.78972 1.70866,-3.12717 3.5,-3l-5.85322e-09,4.15529e-10c1.79134,-0.127171 3.3517,1.21028 3.5,3l-5.57104e-08,6.72365e-07c-0.148295,1.78976 -1.70872,3.12723 -3.5001,3Zm1.4206,6.7635l-2.7853,10.5093l-8.37179e-08,3.15667e-07c-0.270677,1.02062 0.262417,2.08281 1.2425,2.4757l-5.91015e-08,-2.36708e-08c0.379505,0.151996 0.628277,0.519688 0.6282,0.9285v0.323l8.52651e-14,4.05e-07c0,0.551933 -0.447167,0.999503 -0.9991,1h-2.9891l-4.04477e-08,1.96948e-10c-1.65938,0.00807986 -3.01111,-1.33056 -3.01919,-2.98993c-0.00127082,-0.260991 0.0314695,-0.521035 0.0973943,-0.773566l2.7852,-10.5094l2.79671e-08,-1.05468e-07c0.270652,-1.02067 -0.262537,-2.08289 -1.2427,-2.4757l-2.20542e-08,-8.83296e-09c-0.379505,-0.151996 -0.628276,-0.519688 -0.6282,-0.9285v-0.3229l-1.77636e-14,-9.40042e-08c-8.33514e-08,-0.552011 0.447289,-0.999614 0.9993,-1h2.9887l1.06497e-07,-5.28143e-10c1.65943,-0.00822943 3.01133,1.33033 3.01956,2.98976c0.00129458,0.261047 -0.0314356,0.52115 -0.0973639,0.773738Z" fill="currentColor" fill-rule="evenodd"></path></svg>
|
||||
<span class="sr-only">Info</span>
|
||||
<div>
|
||||
<span class="font-medium">Info: </span>
|
||||
{{ _("Your Bitcoin Core version is outdated.")}}<br>{{ _('Some features might not work.')}}<br>{{ _("(minimum required: v20.0.0)") }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="px-3 pb-4">
|
||||
<p class="text-center text-sm m-4" id="total_supply"></p>
|
||||
<div onclick="fetchTotalSupply()" class="button mb-3">
|
||||
|
|
|
|||
|
|
@ -293,7 +293,6 @@ class User(UserMixin):
|
|||
):
|
||||
|
||||
wallet_manager = WalletManager(
|
||||
self.specter.bitcoin_core_version_raw,
|
||||
wallets_folder,
|
||||
self.specter.rpc,
|
||||
self.specter.chain,
|
||||
|
|
|
|||
|
|
@ -41,8 +41,10 @@ class SpectrumService(Service):
|
|||
def spectrum_node(self):
|
||||
"""Iterates all nodes and returns the spectrum Node or None if it doesn't exist"""
|
||||
for node in app.specter.node_manager.nodes.values():
|
||||
|
||||
if (
|
||||
node.fqcn
|
||||
hasattr(node, "fqcn")
|
||||
and node.fqcn
|
||||
== "cryptoadvance.specterext.spectrum.spectrum_node.SpectrumNode"
|
||||
):
|
||||
return node
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
{% set node = specter.node %}
|
||||
{% if specter.node.is_running %}
|
||||
<div class="px-3 py-2 h-full min-w-0 grow flex items-center cursor-pointer" id="active-node" onclick="showPageOverlay('node-info-popup', {top: '64px', left: '16px'});document.getElementById('side-content').classList.remove('active');" >
|
||||
{% if specter.info.get("initialblockdownload") %}
|
||||
<p class="text-white">{{ _("Spectrum is still syncing...")}}<br>{{ _('(data might be outdated)')}}</p>
|
||||
{% endif %}
|
||||
<svg class="w-7 h-7 text-white" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><!--Generated by IJSVG (https://github.com/iconjar/IJSVG)--><path d="M54.5946,27.4366l5.66603e-08,-5.66066e-08c-0.745305,0.744598 -1.9414,0.780481 -2.73,0.0819001l1.11948e-07,9.86842e-08c-11.3381,-9.99478 -28.3408,-9.99478 -39.6789,-1.97368e-07l4.1375e-08,-3.6659e-08c-0.788564,0.698682 -1.98474,0.662797 -2.73,-0.0818999l-2.831,-2.8295l4.78383e-08,4.78239e-08c-0.782846,-0.782609 -0.783038,-2.05166 -0.000429116,-2.83451c0.0255969,-0.0256047 0.0518829,-0.0505111 0.078829,-0.0746918l-1.08875e-06,9.72979e-07c14.4215,-12.888 36.2226,-12.888 50.6441,-1.94596e-06l-4.18137e-09,-3.75185e-09c0.823858,0.73923 0.892463,2.00636 0.153233,2.83022c-0.0242251,0.0269984 -0.0491787,0.0533343 -0.0748332,0.0789783Zm-17.6311,13.896l1.74829e-07,9.87123e-08c1.00267,0.56613 1.35656,1.8379 0.79043,2.84057c-0.0942659,0.166954 -0.21111,0.320108 -0.34723,0.45513l-3.6928,3.6684l2.2751e-08,-2.25368e-08c-0.943884,0.934995 -2.46482,0.934995 -3.4087,4.50735e-08l-3.6928,-3.6684l-6.02819e-08,-5.98007e-08c-0.8175,-0.810974 -0.82279,-2.13111 -0.0118161,-2.94861c0.134967,-0.136053 0.288047,-0.252849 0.454916,-0.347088l2.96297e-07,-1.66902e-07c3.07549,-1.7324 6.83251,-1.7324 9.908,3.33805e-07Zm-14.8508,-3.792l3.86385e-08,-3.1954e-08c-0.835403,0.690877 -2.05882,0.634623 -2.8273,-0.13l-2.3847,-2.4084c-1.1739,-1.1855 -1.14,-2.6381 -0.1975,-3.4266l9.62086e-07,-8.05987e-07c8.84671,-7.41133 21.7347,-7.41133 30.5814,1.61198e-06l-5.08261e-08,-4.40698e-08c0.88879,0.770643 0.984568,2.11588 0.213926,3.00467c-0.0332999,0.0384052 -0.0679639,0.075606 -0.103925,0.111531l-2.6883,2.7151l2.63897e-08,-2.62774e-08c-0.768944,0.765671 -1.99317,0.823532 -2.8309,0.1338l-1.43462e-07,-1.17058e-07c-5.75168,-4.69307 -14.0109,-4.69307 -19.7626,2.34116e-07Z" fill="currentColor" fill-rule="evenodd"></path></svg>
|
||||
<div class="ml-2 text-lg min-w-0 truncate">{{ _("Bitcoin Network") }}</div>
|
||||
<div class="hidden" id="node-info-popup">{% include node.node_info_template() %}</div>
|
||||
|
|
|
|||
|
|
@ -3,17 +3,19 @@ import code
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import signal
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
|
||||
import pytest
|
||||
|
||||
from cryptoadvance.specter.config import TestConfig
|
||||
from cryptoadvance.specter.node import Node
|
||||
from cryptoadvance.specter.managers.device_manager import DeviceManager
|
||||
from cryptoadvance.specter.managers.node_manager import NodeManager
|
||||
from cryptoadvance.specter.managers.user_manager import UserManager
|
||||
from cryptoadvance.specter.node import Node
|
||||
from cryptoadvance.specter.process_controller.bitcoind_controller import (
|
||||
BitcoindPlainController,
|
||||
)
|
||||
|
|
@ -209,7 +211,7 @@ def node(empty_data_folder, bitcoin_regtest):
|
|||
if not os.path.isdir(nodes_folder):
|
||||
os.makedirs(nodes_folder)
|
||||
nm = NodeManager(data_folder=nodes_folder)
|
||||
node = nm.add_external_node(
|
||||
node: Node = nm.add_external_node(
|
||||
"BTC",
|
||||
"Standard node",
|
||||
False,
|
||||
|
|
@ -219,8 +221,8 @@ def node(empty_data_folder, bitcoin_regtest):
|
|||
bitcoin_regtest.rpcconn.rpcport,
|
||||
bitcoin_regtest.rpcconn._ipaddress,
|
||||
"http",
|
||||
"standard_node",
|
||||
)
|
||||
assert node.rpc.test_connection()
|
||||
return node
|
||||
|
||||
|
||||
|
|
@ -240,7 +242,6 @@ def node_with_different_port(empty_data_folder, bitcoin_regtest):
|
|||
18333,
|
||||
bitcoin_regtest.rpcconn._ipaddress,
|
||||
"http",
|
||||
"satoshis_node",
|
||||
)
|
||||
return node
|
||||
|
||||
|
|
@ -290,119 +291,15 @@ def devices_filled_data_folder(empty_data_folder):
|
|||
devices_folder = empty_data_folder + "/devices"
|
||||
if not os.path.isdir(devices_folder):
|
||||
os.makedirs(devices_folder)
|
||||
with open(empty_data_folder + "/devices/trezor.json", "w") as text_file:
|
||||
text_file.write(
|
||||
"""
|
||||
{
|
||||
"name": "Trezor",
|
||||
"type": "trezor",
|
||||
"keys": [
|
||||
{
|
||||
"derivation": "m/49h/0h/0h",
|
||||
"original": "ypub6XFn7hfb676MLm6ZsAviuQKXeRDNgT9Bs32KpRDPnkKgKDjKcrhYCXJ88aBfy8co2k9eujugJX5nwq7RPG4sj6yncDEPWN9dQGmFWPy4kFB",
|
||||
"fingerprint": "1ef4e492",
|
||||
"type": "sh-wpkh",
|
||||
"xpub": "xpub6CRWp2zfwRYsVTuT2p96hKE2UT4vjq9gwvW732KWQjwoG7v6NCXyaTdz7NE5yDxsd72rAGK7qrjF4YVrfZervsJBjsXxvTL98Yhc7poBk7K"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/0h/0h",
|
||||
"original": "zpub6rGoJTXEhKw7hUFkjMqNctTzojkzRPa3VFuUWAirqpuj13mRweRmnYpGD1aQVFpxNfp17zVU9r7F6oR3c4zL3DjXHdewVvA7kjugHSqz5au",
|
||||
"fingerprint": "1ef4e492",
|
||||
"type": "wpkh",
|
||||
"xpub": "xpub6CcGh8BQPxr9zssX4eG8CiGzToU6Y9b3f2s2wNw65p9xtr8ySL6eYRVzAbfEVSX7ZPaPd3JMEXQ9LEBvAgAJSkNKYxG6L6X9DHnPWNQud4H"
|
||||
},
|
||||
{
|
||||
"derivation": "m/48h/0h/0h/1h",
|
||||
"original": "Ypub6jtWQ1r2D7EwqNoxERU28MWZH4WdL3pWdN8guFJRBTmGwstJGzMXJe1VaNZEuAAVsZwpKPhs5GzNPEZR77mmX1mjwzEiouxmQYsrxFBNVNN",
|
||||
"fingerprint": "1ef4e492",
|
||||
"type": "sh-wsh",
|
||||
"xpub": "xpub6EA9y7SfVU96ZWTTTQDR6C5FPJKvB59RPyxoCb8zRgYzGbWAFvogbTVRkTeBLpHgETm2hL7BjQFKNnL66CCoaHyUFBRtpbgHF6YLyi7fr6m"
|
||||
},
|
||||
{
|
||||
"derivation": "m/48h/0h/0h/2h",
|
||||
"original": "Zpub74imhgWwMnnRkSPkiNavCQtSBu1fGo8RP96h9eT2GHCgN5eFU9mZVPhGphvGnG26A1cwJxtkmbHR6nLeTw4okpCDjZCEj2HRLJoVHAEsch9",
|
||||
"fingerprint": "1ef4e492",
|
||||
"type": "wsh",
|
||||
"xpub": "xpub6EA9y7SfVU96dGr96zYgxAMd8AgWBCTqEeQafbPi8VcWdhStCS4AA9X4yb3dE1VM7GKLwRhWy4BpD3VkjK5q1riMAQgz9oBSu8QKv5S7KzD"
|
||||
},
|
||||
{
|
||||
"derivation": "m/49h/1h/0h",
|
||||
"original": "upub5EKoQv21nQNkhdt4yuLyRnWitA3EGhW1ru1Y8VTG8gdys2JZhqiYkhn4LHp2heHnH41kz95bXPvrYVRuFUrdUMik6YdjFV4uL4EubnesttQ",
|
||||
"fingerprint": "1ef4e492",
|
||||
"type": "sh-wpkh",
|
||||
"xpub": "tpubDDCDr9rSwixeXKeGwAgwFy8bjBaE5wya9sAVqEC4ccXWmcQxY34KmLRJdwmaDsCnHsu5r9P9SUpYtXmCoRwukWDqmAUJgkBbjC2FXUzicn6"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/1h/0h",
|
||||
"original": "vpub5Y35MNUT8sUR2SnRCU9A9S6z1JDACMTuNnM8WHXvuS7hCwuVuoRAWJGpi66Yo8evGPiecN26oLqx19xf57mqVQjiYb9hbb4QzbNmFfsS9ko",
|
||||
"fingerprint": "1ef4e492",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDC5EUwdy9WWpzqMWKNhVmXdMgMbi4ywxkdysRdNr1MdM4SCfVLbNtsFvzY6WKSuzsaVAitj6FmP6TugPuNT6yKZDLsHrSwMd816TnqX7kuc"
|
||||
},
|
||||
{
|
||||
"derivation": "m/48h/1h/0h/1h",
|
||||
"original": "Upub5Tk9tZtdzVaTGWtygRTKDDmaN5vfB59pn2L5MQyH6BkVpg2Y5J95rtpQndjmXNs3LNFiy8zxpHCTtvxxeePjgipF7moTHQZhe3E5uPzDXh8",
|
||||
"fingerprint": "1ef4e492",
|
||||
"type": "sh-wsh",
|
||||
"xpub": "tpubDFiVCZzdarbyfdVoh2LJDL3eVKRPmxwnkiqN8tSYCLod75a2966anQbjHajqVAZ97j54xZJPr9hf7ogVuNL4pPCfwvXdKGDQ9SjZF7vXQu1"
|
||||
},
|
||||
{
|
||||
"derivation": "m/48h/1h/0h/2h",
|
||||
"original": "Vpub5naRCEZZ9B7wCKLWuqoNdg6ddWEx8ruztUygXFZDJtW5LRMqUP5HV2TsNw1nc74Ba3QPDSH7qzauZ8LdfNmnmofpfmztCGPgP7vaaYSmpgN",
|
||||
"fingerprint": "1ef4e492",
|
||||
"type": "wsh",
|
||||
"xpub": "tpubDFiVCZzdarbyk8kE65tjRhHCambEo8iTx4xkXL8b33BKZj66HWsDnUb3rg4GZz6Mwm6vTNyzRCjYtiScCQJ77ENedb2deDDtcoNQXiUouJQ"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
)
|
||||
with open(empty_data_folder + "/devices/specter.json", "w") as text_file:
|
||||
text_file.write(
|
||||
"""
|
||||
{
|
||||
"name": "Specter",
|
||||
"type": "specter",
|
||||
"keys": [
|
||||
{
|
||||
"derivation": "m/48h/1h/0h/2h",
|
||||
"original": "Vpub5n9kKePTPPGtw3RddeJWJe29epEyBBcoHbbPi5HhpoG2kTVsSCUzsad33RJUt3LktEUUPPofcZczuudnwR7ZgkAkT6N2K2Z7wdyjYrVAkXM",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wsh",
|
||||
"xpub": "tpubDFHpKypXq4kwUrqLotPs6fCic5bFqTRGMBaTi9s5YwwGymE8FLGwB2kDXALxqvNwFxB1dLWYBmmeFVjmUSdt2AsaQuPmkyPLBKRZW8BGCiL"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/1h/0h",
|
||||
"original": "vpub5ZSem3mLXiSJzgDX6pJb2N9L6sJ8m6ejaksLPLSuB53LBzCi2mMsBg19eEUSDkHtyYp75GATjLgt5p3S43WjaVCXAWU9q9H5GhkwJBrMiAb",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDDUotcvrYMUiy4ncDirveTfhmvggdj8nxcW5JgHpGzYz3UVscJY5aEzFvgUPk4YyajadBnsTBmE2YZmAtJC14Q21xncJgVaHQ7UdqMRVRbU"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/1h/1h",
|
||||
"original": "vpub5ZSem3mLXiSK55jPzfLVhbHbTEwGzEFZv3xrGFCw1vGHSNw7WcVuJXysJLWcgENQd3iXSNQaeSXUBW55Hy4GAjSTjrWP4vpKKkUN9jiU1Tc",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDDUotcvrYMUj3UJV7ZtqKgoy8JKprrjdHubbBb3r7qmwHsEH69g7h6xyanWaCYdVEEV3Yu7a6s4ceFnp8DjXeeFxY8eXvH7XTAC4gxfDNEW"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/1h/2h",
|
||||
"original": "vpub5ZSem3mLXiSK64v64deytnDCoYqbUSYHvmVurUGVMEnXMyEybtF3FEnNuiFDDC6J18a81fv5ptQXaQaaRiYx8MRxahipgxPLdxubpYt1dkD",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDDUotcvrYMUj4TVBBYDKWsjaUcE9M52MJd8emp7QTAJBDTY9BRRFdomVCAFAjWMNcKLe8Cd5HJwg3AJKFyEDcGFTNyryYJgYmNdJMhwB2RG"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/1h/3h",
|
||||
"original": "vpub5ZSem3mLXiSK8cKzh4sHxTvN7mgYQA29HfoAZeCDtX1M2zdejN5XVAtVyqhk8eui18JTtZ9M3VD3AiWCz8VwrybhBUh3HxzS8js3mLVybDT",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDDUotcvrYMUj6zu5oyRdaZSjnq56GnWCfXRuUz38zSWztUvpJuFjsjscGHhheyAncK4z15rLVukBdUDwpPBDLtRBykqC9KHeG9akJWRipKK"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
shutil.copy2(
|
||||
"./tests/misc_testdata/trezor_device.json",
|
||||
empty_data_folder + "/devices/trezor.json",
|
||||
)
|
||||
shutil.copy2(
|
||||
"./tests/misc_testdata/specter_device.json",
|
||||
empty_data_folder + "/devices/specter.json",
|
||||
)
|
||||
return empty_data_folder # no longer empty, though
|
||||
|
||||
|
||||
|
|
@ -540,9 +437,11 @@ def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder, node
|
|||
"allow_threading_for_testing": False,
|
||||
},
|
||||
}
|
||||
specter = Specter(
|
||||
specter: Specter = Specter(
|
||||
data_folder=devices_filled_data_folder, config=config, checker_threads=False
|
||||
)
|
||||
assert specter.active_node_alias == "bitcoin_core"
|
||||
assert specter.node_manager.active_node.alias == "bitcoin_core"
|
||||
assert specter.chain == "regtest"
|
||||
# Create a User
|
||||
someuser = specter.user_manager.add_user(
|
||||
|
|
|
|||
|
|
@ -184,18 +184,88 @@ def funded_taproot_wallet(
|
|||
return funded_taproot_wallet
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wallet(devices_filled_data_folder, device_manager, node):
|
||||
def create_trezor_wallet_with_account(
|
||||
devices_filled_data_folder,
|
||||
device_manager,
|
||||
node,
|
||||
account_number: int,
|
||||
checkbalance=True,
|
||||
):
|
||||
"""An ordinary wallet without private keys"""
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
devices_filled_data_folder,
|
||||
node._get_rpc(),
|
||||
"regtest",
|
||||
device_manager,
|
||||
)
|
||||
device = device_manager.get_by_alias("trezor")
|
||||
device: Device = device_manager.get_by_alias("trezor")
|
||||
wallet_name = f"test_wallet_{random.randint(0, 999999)}"
|
||||
wm.create_wallet(wallet_name, 1, "wpkh", [device.keys[5]], [device])
|
||||
wallet = wm.wallets[wallet_name]
|
||||
ss_segwit_index = account_number * 4 + 1
|
||||
assert device.keys[ss_segwit_index].derivation.startswith(
|
||||
f"m/84h/1h/{account_number}h"
|
||||
), f"At index ss_segwit_index has weird derivation {device.keys[ss_segwit_index].derivation}"
|
||||
wm.create_wallet(wallet_name, 1, "wpkh", [device.keys[ss_segwit_index]], [device])
|
||||
wallet: Wallet = wm.wallets[wallet_name]
|
||||
if checkbalance:
|
||||
assert (
|
||||
wallet.rpc.getbalance() == 0
|
||||
), f"account {account_number} does have a non-zero balance: {wallet.rpc.getbalance()}"
|
||||
return wallet
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trezor_wallet_acc0(devices_filled_data_folder, device_manager, node):
|
||||
"""This wallet might have a nonzero balance"""
|
||||
return create_trezor_wallet_with_account(
|
||||
devices_filled_data_folder, device_manager, node, 0, checkbalance=False
|
||||
)
|
||||
# raise Exception("Do not use this fixture!")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trezor_wallet_acc1(devices_filled_data_folder, device_manager, node):
|
||||
return create_trezor_wallet_with_account(
|
||||
devices_filled_data_folder, device_manager, node, 1
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trezor_wallet_acc2(devices_filled_data_folder, device_manager, node):
|
||||
return create_trezor_wallet_with_account(
|
||||
devices_filled_data_folder, device_manager, node, 2
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trezor_wallet_acc3(devices_filled_data_folder, device_manager, node):
|
||||
return create_trezor_wallet_with_account(
|
||||
devices_filled_data_folder, device_manager, node, 3
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trezor_wallet_acc4(devices_filled_data_folder, device_manager, node):
|
||||
return create_trezor_wallet_with_account(
|
||||
devices_filled_data_folder, device_manager, node, 4
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trezor_wallet_acc5(devices_filled_data_folder, device_manager, node):
|
||||
return create_trezor_wallet_with_account(
|
||||
devices_filled_data_folder, device_manager, node, 5
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trezor_wallet_acc6(devices_filled_data_folder, device_manager, node):
|
||||
return create_trezor_wallet_with_account(
|
||||
devices_filled_data_folder, device_manager, node, 6
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def trezor_wallet_acc7(devices_filled_data_folder, device_manager, node):
|
||||
return create_trezor_wallet_with_account(
|
||||
devices_filled_data_folder, device_manager, node, 7
|
||||
)
|
||||
|
|
|
|||
41
tests/misc_testdata/specter_device.json
Normal file
41
tests/misc_testdata/specter_device.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "Specter",
|
||||
"type": "specter",
|
||||
"keys": [
|
||||
{
|
||||
"derivation": "m/48h/1h/0h/2h",
|
||||
"original": "Vpub5n9kKePTPPGtw3RddeJWJe29epEyBBcoHbbPi5HhpoG2kTVsSCUzsad33RJUt3LktEUUPPofcZczuudnwR7ZgkAkT6N2K2Z7wdyjYrVAkXM",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wsh",
|
||||
"xpub": "tpubDFHpKypXq4kwUrqLotPs6fCic5bFqTRGMBaTi9s5YwwGymE8FLGwB2kDXALxqvNwFxB1dLWYBmmeFVjmUSdt2AsaQuPmkyPLBKRZW8BGCiL"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/1h/0h",
|
||||
"original": "vpub5ZSem3mLXiSJzgDX6pJb2N9L6sJ8m6ejaksLPLSuB53LBzCi2mMsBg19eEUSDkHtyYp75GATjLgt5p3S43WjaVCXAWU9q9H5GhkwJBrMiAb",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDDUotcvrYMUiy4ncDirveTfhmvggdj8nxcW5JgHpGzYz3UVscJY5aEzFvgUPk4YyajadBnsTBmE2YZmAtJC14Q21xncJgVaHQ7UdqMRVRbU"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/1h/1h",
|
||||
"original": "vpub5ZSem3mLXiSK55jPzfLVhbHbTEwGzEFZv3xrGFCw1vGHSNw7WcVuJXysJLWcgENQd3iXSNQaeSXUBW55Hy4GAjSTjrWP4vpKKkUN9jiU1Tc",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDDUotcvrYMUj3UJV7ZtqKgoy8JKprrjdHubbBb3r7qmwHsEH69g7h6xyanWaCYdVEEV3Yu7a6s4ceFnp8DjXeeFxY8eXvH7XTAC4gxfDNEW"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/1h/2h",
|
||||
"original": "vpub5ZSem3mLXiSK64v64deytnDCoYqbUSYHvmVurUGVMEnXMyEybtF3FEnNuiFDDC6J18a81fv5ptQXaQaaRiYx8MRxahipgxPLdxubpYt1dkD",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDDUotcvrYMUj4TVBBYDKWsjaUcE9M52MJd8emp7QTAJBDTY9BRRFdomVCAFAjWMNcKLe8Cd5HJwg3AJKFyEDcGFTNyryYJgYmNdJMhwB2RG"
|
||||
},
|
||||
{
|
||||
"derivation": "m/84h/1h/3h",
|
||||
"original": "vpub5ZSem3mLXiSK8cKzh4sHxTvN7mgYQA29HfoAZeCDtX1M2zdejN5XVAtVyqhk8eui18JTtZ9M3VD3AiWCz8VwrybhBUh3HxzS8js3mLVybDT",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDDUotcvrYMUj6zu5oyRdaZSjnq56GnWCfXRuUz38zSWztUvpJuFjsjscGHhheyAncK4z15rLVukBdUDwpPBDLtRBykqC9KHeG9akJWRipKK"
|
||||
}
|
||||
]
|
||||
}
|
||||
360
tests/misc_testdata/trezor_device.json
Normal file
360
tests/misc_testdata/trezor_device.json
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
{
|
||||
"name": "Trezor",
|
||||
"alias": "mytrezor",
|
||||
"type": "trezor",
|
||||
"keys": [
|
||||
{
|
||||
"original": "upub5EKoQv21nQNkhdt4yuLyRnWitA3EGhW1ru1Y8VTG8gdys2JZhqiYkhn4LHp2heHnH41kz95bXPvrYVRuFUrdUMik6YdjFV4uL4EubnesttQ",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/0h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#0 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixeXKeGwAgwFy8bjBaE5wya9sAVqEC4ccXWmcQxY34KmLRJdwmaDsCnHsu5r9P9SUpYtXmCoRwukWDqmAUJgkBbjC2FXUzicn6"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sUR2SnRCU9A9S6z1JDACMTuNnM8WHXvuS7hCwuVuoRAWJGpi66Yo8evGPiecN26oLqx19xf57mqVQjiYb9hbb4QzbNmFfsS9ko",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/0h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#0 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWpzqMWKNhVmXdMgMbi4ywxkdysRdNr1MdM4SCfVLbNtsFvzY6WKSuzsaVAitj6FmP6TugPuNT6yKZDLsHrSwMd816TnqX7kuc"
|
||||
},
|
||||
{
|
||||
"original": "Upub5Tk9tZtdzVaTGWtygRTKDDmaN5vfB59pn2L5MQyH6BkVpg2Y5J95rtpQndjmXNs3LNFiy8zxpHCTtvxxeePjgipF7moTHQZhe3E5uPzDXh8",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/0h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#0 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDFiVCZzdarbyfdVoh2LJDL3eVKRPmxwnkiqN8tSYCLod75a2966anQbjHajqVAZ97j54xZJPr9hf7ogVuNL4pPCfwvXdKGDQ9SjZF7vXQu1"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5naRCEZZ9B7wCKLWuqoNdg6ddWEx8ruztUygXFZDJtW5LRMqUP5HV2TsNw1nc74Ba3QPDSH7qzauZ8LdfNmnmofpfmztCGPgP7vaaYSmpgN",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/0h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#0 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDFiVCZzdarbyk8kE65tjRhHCambEo8iTx4xkXL8b33BKZj66HWsDnUb3rg4GZz6Mwm6vTNyzRCjYtiScCQJ77ENedb2deDDtcoNQXiUouJQ"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNkkbeX7RLSUgcjnR6nTWFudhmGo5Nxq48FqkKxgPBkWiAwKazG3cd1KjENnTbeGJtNB7iqyuH4QXpxFnVvsRtbGkN2Fg9wStD",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/1h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#1 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixeaHQj4ggQJsEcdSdnGkjTvfvEVp7mJz1nkLSMWaXXXLpBdEwoZqY1LZ7heTuCBPn4XA49XrNLggL3vQLWJh1Hft9NBQDrZ29"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sUR6SVwH1nkeiUwWky58XopeJaFWLwJfQj2GgcGFbwmkmhp3yBMEVTqw2xfvzpvMqSUzCXiGZdxAiWyvmxyyrErPBwPoKSJzus",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/1h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#1 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWq4q52PvM6Gp1KBpMd1AHt2ACzRgnDmLEg8AuRq97z9LgvLRBJkoivYDjC3XXupFydSxFT6pKDedLUj478qCY4Wbf6LZHaEuo"
|
||||
},
|
||||
{
|
||||
"original": "Upub5SNDKzLBXW3QBrbxK14LDuVtxxR2MWYxChNjShS338xKtx1tgwp1tZoM5Prts4J2DXpPKATS1vfntAHrorEXZA7VFBCaUNhemfWYejJRPHg",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/1h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#1 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDELYdzSB7s4vayCnKbwKE1my6BukxQLvBPt2EAuJ9J1TBMZNkjmWp5afaLrxpqz7ztdjJaks3oAz731Q4aArgpVv5KvkWEMMH521zaj118Z"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5mCUdf16gBat7LhtqCQne7zV3en2XV2nodPGR85jhHqv9E4A7UXNnZ9UsA5x3H2fxhc38t5gXcp1XvqGp8ASa3ErgXhyFKNAt1cSMdWGXNN",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/1h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#1 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDELYdzSB7s4vfA7c1SW9S9B3zv8KBkqFsDNLRCf7RSXANXnQvcKK61GfLu8S1A4rLRJaNpnZ6pxesWwFM9gkuTwgeLjihGCP7h4GJuCxsd3"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNknik1KU1syMoEgo8NwkDkYeX6hYSTexRhSdCXqNHHHKsd9W1o8qvqRgqfBP9KmuWBxuMWvmCMqMSicMBqwCJGGa5TVau3eD1",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/2h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#2 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixecQWDGjMqoYR7XpfNkzhJqcg4QHBG8tKEMDJvfZd4HxWsT9yLf4qqSWiz3PSsgzPtJwgpUiHe7VwpGy2RNTQxfhroRPqfBfM"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sUR85w9G2bVdZQrHWXLdxPcsi75fgnBWkJNATPfgbv4Qpabv59fMTG5cVdePeveifFk5TPePFdU7jvPm1qb9fVYcxXKXSvUtgz",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/2h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#2 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWq6UWENw9qFewDxZutWasgFZjpb2d6cfp21wgqG96GoPZiCX9csmXADgQAWBdeB5ntYD7PDWJjbejtZHyk11nkkNF24i7RnLV"
|
||||
},
|
||||
{
|
||||
"original": "Upub5TVK2qPsm6eMaSm9JpaDCqGACPHtXMmn6reTthgdvpqnV7qcyHD3awVpEawcMxRKDbEyvMLr1pKNWFAQYBhzfwzrAo8BpMefcymgMsYvdw4",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/2h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#2 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDFTeLqVsMTfsyZMyKRTCCwYEKcnd8FZk5Z9kgB9u2ytumXP735AYWTH8jXwgKk7Qzx4KumeH3gpZj7swnueKocPGzwrMrDJN8PH9hh7AnfJ"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5nKaLW4nunBqSzRVKf4oTaGySWh8kTJUd3wQHvVMnNp4ksukeC7spEmCyKRfAEHz31UPAkBGQwrEwS5iFpQbuNB4EuigfoTLTSwYmXXP2JK",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/2h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#2 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDFTeLqVsMTfszoqCVuAAFbTYPn3RQj6wgdvUJ14jWXVJzBe1TKup7gtPT4U987LAQjAvQgt8z9ztH2BgnqvvEnstCikS7kHYh8PNimvL3mt"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNkquuCusEL8ofnQaM4V8rsdzPuWe48FD4rRWvmF61hEqVMwwNje2zxpma7ju39zT1ub1hogbGaKdAvmMfPJtdqCSR5FovbUSF",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/3h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#3 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixefbfQs8aHxzHfFbt4JPLRvxYsDNnvj8xPL73A5HMUFU8cFbLHAFuxqbTSbuLhuXubw437EYMrbmg2RyVxk9kXbaCRBYbHftq"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sURA6hs4HJw67NHTFmMsmjDSu9pQS3abpJDrkUdSpXn7n5WEVHvU2uuJWAKHSkdpDYKuFBvhQAJ2xtezVurh2WfBMsP8HorJxH",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/3h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#3 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWq8VGxBBsGiCtf8K9ukQDGpknZKmtVhjosiEmo2MhzWM4cWwHszMAyugvqPyTdGe5UMzufXeqZWsi9nn41YNosJmb5fYoCqFy"
|
||||
},
|
||||
{
|
||||
"original": "Upub5TYty5hhC1kpHss1TPBrhzsP7Jy2UScv6VF8HeEN1jbvVhryoPsrN9sBGxWjcvJp4TZLdkaqKmEvLqFP2CVtnSFw4Vv4HcG1pcp66PCAxnr",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/3h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#3 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDFXEH5ognNnLgzTqTz4qi79TEYTm5LQt5BkR57hd7tf3n7QTsBqMHfeVmuWoahzuqpNgdAtGMdk7ZhxvGvSDv6eMteeEKTuiL2KZS7oDNBc"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5nPAGkNcLhJJCHZhGRt47wH5mBab9m5PfJmUjSazw1XAkbax3vG9RCJQVMei2Z84PgirRvTUHU93uUXynBUXwiiPVT4VwG45Xas6KWDPh5T",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/3h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#3 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDFXEH5ognNnLk6yQSfyQuxTeiSvsp2sritkYjXANfACQyuKCs445ieRay6hBzSAEmQRPfsALrgHhF4dxKCzrH9RDTG6FPCtHmGJvGi6sSC8"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNktcVEn7ormrgocRuFrKvU6wum7ZRfXcTKYtfUfG1RXihAgq4cWyTVM4smEV5VTejiCXot2akjMDr6sYCghMnqUSVuNctuCbm",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/4h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#4 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixeiJFSjP9pc3JgTTSFfaQ2Pv4ipJAU1YLrTUmsVTMCYMLQzV2A3CNVMtm66VP3NjdQYa9BaXr1dNMCYA3G8cuXsaHFJMZNVDP"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sUREDYApTNH7Y188zckaDj6NHaK8tkgRFLsjF2a8cQRXrSzgvJbMoEgHgqZm8259DGsHUXmUA8P3FKEFkjNeMh7aMaivei27JR",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/4h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#4 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWqCc7FwMvcjdXVp41JSrD9k9D44EbbXArXajKji9advRS6yNJYt7Vktsc5sej4bdp1kEFWJQoeXA8j42sXVhzKhmJRTmvjiZL"
|
||||
},
|
||||
{
|
||||
"original": "Upub5TNPzsZrx1ZhKLgEqViJqi6cWUrUyQdPpCwFQ1fbBqtVBGC6CwCscoqnH34TEGGbWnEL4C2eJDPnsqcM2sGnPZXuyH7Dy511H3qnCVK7P5f",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/4h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#4 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDFLjJsfrYNbDiTH4r6bHqpNgdiMDaJRMnuSYBV8rHzwcTfjaGjANYKd6mz4XC3xhJ93g3cL5L5tz6iKtHbD7XDvLoRqPzvehnTMFYEJv8rh"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5nCfJYEn6h7BDYF3ZiuhYa6RgEb6FnVb2ewjDpiYEWKyQVuuHuise1Ygbo1r5EggzF1zfyDSCmywMieJKCR3q1Ergim9UQSUZEWrpGkeBvk",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/4h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#4 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDFLjJsfrYNbDmMekjy14LbGzdVwNv4J46EvoDuHuxf1DdoeA73WowTfs5Y4L37isMxiXuuvJmz8ahJkGrDwNARwgeXntvMGgnuxgmXxNsnU"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNkx9AMEjSYNDSic2p59T2uGrJQYD5URuDZbaF3itK2mjDD9mJutuYmVnzjQuSSCvxE6xjpwXCY8YNpzr3GLqSjWH4n2Cuokvy",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/5h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#5 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixempvZBznWCQ4bT4M4xhWTZpTNEwpGuq76WAMSZ5eonMrTTRGTR8TmWct4Gujz81qvT158VUHpQgsvfTsqn6ZRuQr7x22dD1E"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sURH2nXVtZGcoWLn6CRrkNkFCoYpvbeEvxFqigBvaT8ExBqPPNVzrnCjBq9meh2Adci4WmLrmDBTfpztnpxq1Sa3sAcMK3ub5d",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/5h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#5 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWqFRMcco7cEu2iT9ayjNrod4SHkGSZLrTuhCyMW7dLdXAwfqNTXB3HLNbftBQ1d49rXGV5h1tSwaeVh4y7gMjnBGtJtRw2UE8"
|
||||
},
|
||||
{
|
||||
"original": "Upub5SbGvWuQjHSN9SNQSCQ7x1EeV5iidqbmjzqR6aBmThDNCQqWSZ3R8HmY8vLTcwYUt5PLSZC5hjp3VHTq9hek56Qkka8pPqFNGKAoJRddAPg",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/5h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#5 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDEZcEX1QKeTtYYyESoH6x7WicKDTEjPjihLht3f2ZrGVUpNzWLzv3oYrdsLXajEafSCgRyVWjcKEiABNQRb5CkoBairzRgu4migGeGKXkCR"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5mRYEBaKsxyr2NFZRzrstJ9EKSMsfT7tLWQv6Cm1DNN4XMQJkTmQvVnvp6cEqb9vTvEF8xZTmGSGLevopuuaoCxWnD9m26NTSXdHMTYwU3V",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/5h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#5 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDEZcEX1QKeTtaBfGcExEgKKoGhiAKivMQ6Pz6HLNwX3Jkf8ZZbZMDwv7HqeioUC6qdvnNuGLLUaugF2nMwRu8dfLk2BWU3CfgD57JiADegZ"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNm1DqJZU9wsGAQHT9vZofiwYVKih5SNWA5TDCKmiPzabATbmeFcvQQ7MKKPFHovRqnnVX9a9KZYQzXiLFKGDLbGoQ8AuuMGc9",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/6h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#6 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixepubWWjVuhSnH8UgvP49HEWeHRRpErS3cMoJibujmbDohuRbo99KQ8BCeFFbMqWjV8XrT86QqpZVdNx5thUTHfwBU6fdP51e"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sURKFuKEy9mp1mH9AwEsNMDmF1JzvmD61CkPmsiL2ufvzKTDNNsG7QLKN6RwNSowYVW5g7uJqMFL9thVW9QChJ2KxXagZ2UdJE",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/6h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#6 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWqHeUQMsi7S7HepEKnjzqH96e3vGc8BviQFGAsua5tKZJZVpNpnRfQvYrx3u9oPy2eYRqe962Wp4iCHnHZ43bETNFHDfyLoem"
|
||||
},
|
||||
{
|
||||
"original": "Upub5SibFFj1EPmNEfxxZPKFet6rGVDAdiAXStukEA6AywfBgFrhoQAkkVjYgCbk4VXYUEypFmCsuR5QZSnRtF7s3pREQkKkHCkeHy8oM8GiSk5",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/6h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#6 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDEgvZFpzpkntdnZnZzCEezNvPihuEbxVRbR31dZS66iJxfQBsC8Fg1WsB9bp2HDeFboAFBWJwHabnKVy8y4CBUofEu3vK4QLoNeGgwgVMZi"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5mYrYvPvP5Jr8draW5U6M7m9xCPukVXQtTCp6h5HKDQRQb2W8Ce2Vsc4KbQRuTuaJhyPSJRB7koQ6sTtM4jj4Mi6GN1vHAXZSdQ8SG55ihw",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/6h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#6 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDEgvZFpzpkntgTGHgKZT98wiuTkCQmKsx3Bt6mef3N5fdtkkwLRxoKjEoLSusLwkgRfvgF83gxx3STZrt6G3PnQvEB3fj7MmgJqxPWbWPxa"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNm1q8NAC2rewfMBmq19fvRPM7mwo1A7agcwMHGBEfD6RWz5ucCZSpeQXGjLJPFxN7K838YyVtqwzc1DKYfxtv8UatsPoYeTHC",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/7h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#7 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixeqWta7TNpV8HE2oMzxvPygKGjeXjxbWa9qwPf1Rzz74AEPZZk5fjeRMA4CJgosT11U5TrXSz8E976swPFQA2psigDKUJemiv"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sURLyMhyndVrEKhoKYv3hzjhYFoMGTdK6nm3jtXkBG1u45WrLf2u5fKRyR64Uao5Xs1DnGNgvrpAVv8xojSLET69caKBg7ypie",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/7h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#7 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWqKMvo6hBqUKr5UNwTvLUo5PtYGcJYR2JQuEBhKiSEHd4d8nezRPvQ3ABcB1HnXxQ9gXz7XBY5eQjdm5sbBakJH2J1iq8UvZc"
|
||||
},
|
||||
{
|
||||
"original": "Upub5T4M7Sm4okSNqdSmt1TxbsmWpLczVoot5cmLjw7EbSUoQ13Yh622XmvjTphUgr2MMqWymfGijavxRPo3ZnvRiPteKHSWVFz5KBgMdBVGHMz",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/7h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#7 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDF2gRSs4Q7TuEk3btcLwbz3awa7j6hbr4KGdXQaVhbXvgQb2ksyXTHi3xmhYediT9CLKm5a9mTS9eGWapWrkr4H59SAgX7dmpbBpy24rrEq"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5mtcR7RyxRyrhxjb1ZVmHvzadnmMk6k8PvfyvtosdaA1jVrnCGv73wmkKWk3v8SBrBDNz98wkLAVTmeSfp9xPFKeo7KdM5TbZPCzw7vZoTt",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/7h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#7 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDF2gRSs4Q7TuFn9JBob85xB9b47eQNYbTWf3vyPFMiqFxob31Qi3MPtvoFnXt1UNDtuvE5qpKYK8oMkRCqgGig2UkvMNo2Hoo4eptLhCiST"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNm4XeNALECoMbF21HFcyfrXQb24kQu1LVq8pFpozWp5Uridk19VLT4o6J7Q6RAauT22crjYtDX8zYPYrrreRbcW6X4EpX9Mpn",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/8h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#8 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixetDQa7baAdYD7s2pFSE9QpNjymV9hVGPN3QNDeBrb67VxwPxh1ZN4ovBSG6iiVzLiNfC36qJoR93VDUhS5giJuEJQAZdV1ip"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sURPk9bgmUzqMDNXgyTtNUrTMX56xf4MaYLJhSC8uWGsj1QYeemMAEeJ3dCcUbHSRhpdSsH8cVeD559N6tLpwXKQjJD1eKenwu",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/8h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#8 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWqN8igog3LTSjkCkN1kzxuqD9p2JVyTW3zABjMiSgVGHzWq6eisUViuEPij1JGtrEy6Cb1xsAugyteAP2VgHpXY91uYpkixb8"
|
||||
},
|
||||
{
|
||||
"original": "Upub5SAJAYu4nm5wC2JZT4Pq67xQ7vLsVLqYJgoVqLoyQvWDWkw1XpbhyezkrSmfs6qM28gJ8hnHVnmvtaA4j5PNBHxG5NeWneL6TLdWxva7533",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/8h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#8 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDE8dUZ14P87Tb8uPTfGp6EEUF9qc6EdWHPJncpHEX5ZLoAUVbcZCuAn5MPmjptXSoVVe885iXfH87SsbyoKhJxLguXNgpVynxk8zJiSXwxa"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5kzZUDZywSdR7T4wKcXz61aTTjHQP6Qd19qGsgwjzg4Gi7Wzf26wpq38tX2Kx9qLvUVZbsvULrLSTuFsKHcMhh5YHTmhF48m2qhqiakyF6e",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/8h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#8 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDE8dUZ14P87TfGUeVrdLt2m2Qzdh3ND64jpLsmX7ipjWwRFFU9tt8HAKNG4ov2sXJCC6qpdLv4V5oVMqrK8g37nNFGoSgzxyGX9ffsxL8Cz"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNm6Y8Na9Ep6m8H7A6z9PhAoW8dF1BfLBAvuVYb1xgEqaXRYrRpkxe7vXYw1AaRSB1JE9WuKAxvRMoSCRcpae1CRsSRaWbh43y",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/9h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#9 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixevDtaXQamvwk9xBdyxeAj6UHawjvTp74Tp5eyrA21rDAfrWPNHBZ7wMSFsAsyMFtzaBrCs84ChWJXs3TQ1u7tq1DmWERYJhX"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sURRVraj7fgYjPgp15hqEAjm8jMW6DBMHKfSywQY2GCoAKqUjSzWXzE6KcWdCgKju4vmiPYEa8L3wtdcPSPe1EGi7EgHABMg2o",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/9h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#9 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWqPtRfr2E2Apv4V4UFhreo8zN6RS46TCqKJUEa7ZSRBjJwmBSx2rFJhWP2jjPKCKc5EU7H4pobXri8QfaYVMXUqWxNpM4Thp3"
|
||||
},
|
||||
{
|
||||
"original": "Upub5SZqPpGQLXLrZiASDasMaoUae2fRr6frs17aAWyEpKmnrytb3uDK2TyPJawmuayNdpaheEjLjMBxAfg2j9M2zte5iFwjxSo4tvnuKsryBZW",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/9h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#9 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDEYAhpNPvtNNxpmGEBkLaukemGAASzTpqhcrwzSVvUpv9PS57hAowykhoXwqsNfURBQ3df2mmDh9PYPZysHN8Z2WYQfuzJSmQLJNfeBysrh"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5mQ6hUwKVCtLSmZFysmVTUeMfkSgYdwFYipCf6zaLfyXdVRGY4LjUuKvM7fi5zJKuA7VoZ5gKkuHUdjkS5HH81V9owruYMvZM5JPHgpU94i",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/9h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#9 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDEYAhpNPvtNNzaxyA7rrFVpvd1nyCujicJoGfBZx4pemro9XMC8fnMT6priC3sLWGsp33VnYty3vpDqiy6obTSBymktezJkmakkDEwVb9e2"
|
||||
},
|
||||
{
|
||||
"original": "upub5EKoQv21nQNmBMp8zqKWqi4exGTsvswkeDNbexHNursybdRAiYoXprZFy6yfZDwKQgHmLxPpKh9BwNa1fJdyjRcE3a51s11UVCLaRkmbE3U",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/49h/1h/10h",
|
||||
"type": "sh-wpkh",
|
||||
"purpose": "#10 Single Sig (Nested)",
|
||||
"xpub": "tpubDDCDr9rSwixf13aLx6fUftgXoHzsk8RJwBXZMh2BPnmWWDXZYk9JqVCWGkwD5SrKRWB6CxhNEn2tHQuKDFjG1a7KiBubJG8AtL7vMZ2ccQc"
|
||||
},
|
||||
{
|
||||
"original": "vpub5Y35MNUT8sURVkrRjbTjecBsrupUkvEJMVu7z6xkYoRMMnorxKeBAPbRjAzk3s8qwPJeFbEWi6BFSuVYV46Z1YnEQJ1wuwMpoA1m4LyZ17p",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/84h/1h/10h",
|
||||
"type": "wpkh",
|
||||
"purpose": "#10 Single Sig (Segwit)",
|
||||
"xpub": "tpubDC5EUwdy9WWqU9RWrW25GhiFXyD2dYiMjMXruSofeiw1DH72XrpPYxaY1czhaBPvYa5AN7wWAWiPufDHKJmpVTbjCaA6mHf2vZjTbYS944S"
|
||||
},
|
||||
{
|
||||
"original": "Upub5TmeuYoRtjgjZWavZYFmc7rnHgUWw2kVw6N4Y19u5xjXPu8K5ZM3YT6kZyxwAD2tMHa5tbpJJi1B7zkpzTu6cCE3rRppmFy3BU9DPcLU9AS",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/10h/1h",
|
||||
"type": "sh-wsh",
|
||||
"purpose": "#10 Multisig Sig (Nested)",
|
||||
"xpub": "tpubDFjzDYuRV6iFxdBka98kcE8rQuyFXvYTunsMKUdAC7negJfo9MJYTxt54vy17ziz8ePRt27jLaWNLsUNFBqRjrcUgaYzo7cjgsegjT4EYhP"
|
||||
},
|
||||
{
|
||||
"original": "Vpub5nbvDDUM3REDStVR5hGfkf6DtJAFsWhyg9c2ZAFrBx9sTE16sJeoUe1cWG1UaSZVMn9FFf8hzoQtUMMfLK6Li5AiKgDYS6JSxJ3LuZBRA4K",
|
||||
"fingerprint": "1ef4e492",
|
||||
"derivation": "m/48h/1h/10h/2h",
|
||||
"type": "wsh",
|
||||
"purpose": "#10 Multisig Sig (Segwit)",
|
||||
"xpub": "tpubDFjzDYuRV6iFzhu8FwN2YgGnqZWYXnWSjjb6ZEqDv6q7gXjMgSSjn68nz13xYKbfjVqnVbqaa1ZXowTdsLcf3VsYHVFHt38fByVArkpZ5ZP"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
|
@ -178,10 +178,10 @@ def test_addressinfo(caplog, client, funded_ghost_machine_wallet):
|
|||
res = client.post(
|
||||
url, data={"address": invalid_address}, follow_redirects=True, headers=headers
|
||||
)
|
||||
assert (
|
||||
res.data.decode()
|
||||
== '{"error":"Request error for method getaddressinfo: Invalid address format"}\n'
|
||||
assert res.data.decode().startswith(
|
||||
'{"error":"Request error for method getaddressinfo'
|
||||
)
|
||||
assert res.data.decode().endswith('Invalid address format"}\n')
|
||||
|
||||
# send post request with address, not belonging to wallet
|
||||
# this recreates an edge case, see https://github.com/cryptoadvance/specter-desktop/issues/2000
|
||||
|
|
|
|||
|
|
@ -126,7 +126,6 @@ def test_device_wallets(
|
|||
):
|
||||
caplog.set_level(logging.DEBUG)
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
devices_filled_data_folder,
|
||||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ def test_node_manager_basics(
|
|||
nm = specter_regtest_configured.node_manager
|
||||
# # Load from disk to get the other two nodes
|
||||
assert sorted(list(nm.nodes.keys())) == [
|
||||
"default",
|
||||
"satoshis_node",
|
||||
"bitcoin_core",
|
||||
"node_with_a_different_port",
|
||||
"standard_node",
|
||||
]
|
||||
assert nm.nodes_names == [
|
||||
|
|
@ -39,23 +39,25 @@ def test_node_manager_basics(
|
|||
"Bitcoin Core",
|
||||
]
|
||||
# Checking some standard methods and properties
|
||||
assert nm.get_by_alias("satoshis_node") == nm.get_by_name(
|
||||
assert nm.get_by_alias("node_with_a_different_port") == nm.get_by_name(
|
||||
"Node with a different port"
|
||||
)
|
||||
default_node = nm.get_by_alias("default")
|
||||
satoshis_node = nm.get_by_alias("satoshis_node")
|
||||
assert nm.default_node() == default_node
|
||||
default_node = nm.get_by_alias("bitcoin_core")
|
||||
node_with_a_different_port = nm.get_by_alias("node_with_a_different_port")
|
||||
assert nm.active_node == default_node
|
||||
assert specter_regtest_configured.config["active_node_alias"] == "default"
|
||||
assert specter_regtest_configured.config["active_node_alias"] == "bitcoin_core"
|
||||
# Switching the node via the node manager does not change the active_node_alias in the config, only specter.update_active_node() does
|
||||
nm.switch_node("satoshis_node")
|
||||
assert nm.active_node == satoshis_node
|
||||
assert specter_regtest_configured.config["active_node_alias"] == "default"
|
||||
specter_regtest_configured.update_active_node("satoshis_node")
|
||||
assert specter_regtest_configured.config["active_node_alias"] == "satoshis_node"
|
||||
assert nm.active_node == satoshis_node
|
||||
nm.switch_node("node_with_a_different_port")
|
||||
assert nm.active_node == node_with_a_different_port
|
||||
assert specter_regtest_configured.config["active_node_alias"] == "bitcoin_core"
|
||||
specter_regtest_configured.update_active_node("node_with_a_different_port")
|
||||
assert (
|
||||
specter_regtest_configured.config["active_node_alias"]
|
||||
== "node_with_a_different_port"
|
||||
)
|
||||
assert nm.active_node == node_with_a_different_port
|
||||
# Deleting a node
|
||||
nm.delete_node(satoshis_node, specter_regtest_configured)
|
||||
nm.delete_node(node_with_a_different_port, specter_regtest_configured)
|
||||
assert nm.nodes_names == ["Standard node", "Bitcoin Core"]
|
||||
# Check that with the deletion of the active node the switch to the next node work, the first node in the list, here the Standard node, is switched to
|
||||
assert specter_regtest_configured.config["active_node_alias"] == "standard_node"
|
||||
|
|
@ -65,9 +67,11 @@ def test_node_manager_basics(
|
|||
SpecterError,
|
||||
match="Node with a different port not found, node could not be deleted.",
|
||||
):
|
||||
nm.delete_node(satoshis_node, specter_regtest_configured)
|
||||
with pytest.raises(SpecterError, match="Node alias satoshis_node does not exist!"):
|
||||
nm.switch_node("satoshis_node")
|
||||
nm.delete_node(node_with_a_different_port, specter_regtest_configured)
|
||||
with pytest.raises(
|
||||
SpecterError, match="Node alias node_with_a_different_port does not exist!"
|
||||
):
|
||||
nm.switch_node("node_with_a_different_port")
|
||||
|
||||
|
||||
@pytest.mark.elm
|
||||
|
|
@ -89,10 +93,9 @@ def test_switch_nodes_across_chains(
|
|||
bitcoin_regtest.rpcconn.rpcport,
|
||||
bitcoin_regtest.rpcconn._ipaddress,
|
||||
"http",
|
||||
"bitcoin_regtest_alias",
|
||||
)
|
||||
assert nm.nodes_names == ["", "bitcoin_regtest"]
|
||||
nm.switch_node("bitcoin_regtest_alias")
|
||||
assert nm.nodes_names == ["bitcoin_regtest"]
|
||||
nm.switch_node("bitcoin_regtest")
|
||||
assert nm.active_node.rpc.getblockchaininfo()["chain"] == "regtest"
|
||||
nm.add_external_node(
|
||||
"ELM",
|
||||
|
|
@ -105,6 +108,6 @@ def test_switch_nodes_across_chains(
|
|||
elements_elreg.rpcconn._ipaddress,
|
||||
"http",
|
||||
)
|
||||
assert nm.nodes_names == ["", "bitcoin_regtest", "elements_elreg"]
|
||||
assert nm.nodes_names == ["bitcoin_regtest", "elements_elreg"]
|
||||
nm.switch_node("elements_elreg")
|
||||
assert nm.active_node.rpc.getblockchaininfo()["chain"] == "elreg"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ def test_WalletManager(
|
|||
node_with_empty_datadir,
|
||||
):
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
devices_filled_data_folder,
|
||||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
|
|
@ -127,7 +126,6 @@ def test_WalletManager_2_nodes(
|
|||
):
|
||||
caplog.set_level(logging.INFO)
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
devices_filled_data_folder,
|
||||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
|
|
@ -144,27 +142,30 @@ def test_WalletManager_2_nodes(
|
|||
assert wm.chain == "regtest"
|
||||
assert wm.working_folder.endswith("regtest")
|
||||
assert wm.rpc.port == 18543
|
||||
# Change the rpc - this only works with a different chain!
|
||||
wm.update(rpc=bitcoin_regtest2.get_rpc(), chain="regtest2")
|
||||
# Change the rpc - this works differently with a different chain!
|
||||
# If we use something different that regtest, unfortunately a liquid address gets
|
||||
# generated.
|
||||
# So we don't test that scanrio here of different chains. We test the scenario with the same chain.
|
||||
# but different node
|
||||
wm.update(rpc=bitcoin_regtest2.get_rpc(), chain="regtest")
|
||||
# A WalletManager uses the chain as an index
|
||||
assert list(wm.rpcs.keys()) == [
|
||||
"regtest",
|
||||
"regtest2",
|
||||
] # wm.rpcs looks like this: {'regtest': <BitcoinRpc http://localhost:18543>, 'regtest2': <BitcoinRpc http://localhost:18544>}
|
||||
assert wm.rpc.port == 18544
|
||||
assert wm.wallets_names == []
|
||||
assert wm.chain == "regtest2"
|
||||
assert wm.working_folder.endswith("regtest2")
|
||||
assert wm.wallets_names == ["a_test_wallet"]
|
||||
assert wm.chain == "regtest"
|
||||
assert wm.working_folder.endswith("test")
|
||||
second_wallet = wm.create_wallet(
|
||||
"a_regtest2_test_wallet", 1, "wpkh", [device.keys[5]], [device]
|
||||
)
|
||||
# Note: "regtest2" is recognised by the get_network() from embit as Liquid, that is why there is an error in the logs saying the Bitcoin address is not valid since a Liquid address is derived.
|
||||
assert wm.wallets_names == ["a_regtest2_test_wallet"]
|
||||
assert len(wm.wallets_names) == 2
|
||||
assert wm.wallets_names == ["a_regtest2_test_wallet", "a_test_wallet"]
|
||||
|
||||
|
||||
def test_WalletManager_check_duplicate_keys(empty_data_folder):
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
empty_data_folder,
|
||||
MagicMock(), # needs rpc
|
||||
"regtest",
|
||||
|
|
@ -231,7 +232,6 @@ def test_wallet_sortedmulti(
|
|||
bitcoin_regtest, devices_filled_data_folder, device_manager
|
||||
):
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
devices_filled_data_folder,
|
||||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
|
|
@ -285,7 +285,6 @@ def test_wallet_sortedmulti(
|
|||
|
||||
def test_wallet_labeling(bitcoin_regtest, devices_filled_data_folder, device_manager):
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
devices_filled_data_folder,
|
||||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
|
|
@ -344,7 +343,6 @@ def test_wallet_change_addresses(
|
|||
bitcoin_regtest, devices_filled_data_folder, device_manager
|
||||
):
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
devices_filled_data_folder,
|
||||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
|
|
@ -354,14 +352,14 @@ def test_wallet_change_addresses(
|
|||
device = device_manager.get_by_alias("specter")
|
||||
key = Key.from_json(
|
||||
{
|
||||
"derivation": "m/48h/1h/0h/2h",
|
||||
"original": "Vpub5n9kKePTPPGtw3RddeJWJe29epEyBBcoHbbPi5HhpoG2kTVsSCUzsad33RJUt3LktEUUPPofcZczuudnwR7ZgkAkT6N2K2Z7wdyjYrVAkXM",
|
||||
"derivation": "m/84h/1h/0h",
|
||||
"original": "vpub5ZSem3mLXiSJzgDX6pJb2N9L6sJ8m6ejaksLPLSuB53LBzCi2mMsBg19eEUSDkHtyYp75GATjLgt5p3S43WjaVCXAWU9q9H5GhkwJBrMiAb",
|
||||
"fingerprint": "08686ac6",
|
||||
"type": "wsh",
|
||||
"xpub": "tpubDFHpKypXq4kwUrqLotPs6fCic5bFqTRGMBaTi9s5YwwGymE8FLGwB2kDXALxqvNwFxB1dLWYBmmeFVjmUSdt2AsaQuPmkyPLBKRZW8BGCiL",
|
||||
"type": "wpkh",
|
||||
"xpub": "tpubDDUotcvrYMUiy4ncDirveTfhmvggdj8nxcW5JgHpGzYz3UVscJY5aEzFvgUPk4YyajadBnsTBmE2YZmAtJC14Q21xncJgVaHQ7UdqMRVRbU",
|
||||
}
|
||||
)
|
||||
wallet = wm.create_wallet("a_second_test_wallet", 1, "wpkh", [key], [device])
|
||||
wallet: wallet = wm.create_wallet("a_third_test_wallet", 1, "wpkh", [key], [device])
|
||||
|
||||
address = wallet.address
|
||||
change_address = wallet.change_address
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ from cryptoadvance.specter.specter_error import SpecterError
|
|||
from mock import MagicMock, call, patch
|
||||
|
||||
|
||||
def test_Node_btc(bitcoin_regtest, wallet):
|
||||
def test_Node_btc(bitcoin_regtest, trezor_wallet_acc2):
|
||||
wallet = trezor_wallet_acc2
|
||||
with tempfile.TemporaryDirectory("_some_datafolder_tmp") as data_folder:
|
||||
node = Node.from_json(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -417,29 +417,30 @@ def test_reserve_addresses_with_mocks(empty_data_folder, caplog):
|
|||
assert addresses == ["a", "b"]
|
||||
|
||||
|
||||
def test_reserve_addresses_with_an_actual_wallet(wallet):
|
||||
def test_reserve_addresses_with_an_actual_wallet(trezor_wallet_acc6):
|
||||
wallet = trezor_wallet_acc6
|
||||
specter_mock = MagicMock()
|
||||
test_service = MyTestService(True, specter_mock)
|
||||
# Reserve first address
|
||||
test_service.reserve_address(
|
||||
wallet, "bcrt1qcatuhg0gll3h7py4cmn53rjjn9xlsqfwj3zcej", "reserved_for_john_nash"
|
||||
wallet, "bcrt1qqvqdt5nsjhzrxcvhyz3m29f8qwyd4lrcpmtzy9", "reserved_for_john_nash"
|
||||
)
|
||||
first_address_address_obj = wallet.get_address_obj(
|
||||
"bcrt1qcatuhg0gll3h7py4cmn53rjjn9xlsqfwj3zcej"
|
||||
"bcrt1qqvqdt5nsjhzrxcvhyz3m29f8qwyd4lrcpmtzy9"
|
||||
)
|
||||
# Check that labeling works
|
||||
assert first_address_address_obj["label"] == "reserved_for_john_nash"
|
||||
# Simulating that the address has been used (for the definition of "usage" see the check_unused() method in wallet.py)
|
||||
wallet._addresses.set_used(["bcrt1qcatuhg0gll3h7py4cmn53rjjn9xlsqfwj3zcej"])
|
||||
wallet._addresses.set_used(["bcrt1qqvqdt5nsjhzrxcvhyz3m29f8qwyd4lrcpmtzy9"])
|
||||
wallet.getnewaddress()
|
||||
assert wallet.address_index == 1
|
||||
assert first_address_address_obj["used"] == True
|
||||
# Check that the correct addresses are reserved, should be #2, #4, #6 - since #0 has been used and there is a gap of one address in between
|
||||
addresses = test_service.reserve_addresses(wallet, "satoshi_dice", 3)
|
||||
assert addresses == [
|
||||
"bcrt1qxak08ykhf7r4js9yncysy5p05xp0fwxhamewc8",
|
||||
"bcrt1q2zv9963acq3g7a62mdjgj60rr3hgmyykaccca7",
|
||||
"bcrt1qpys58dndrn9sxnk0z7ngm6wsxskpvs9jsjq7q6",
|
||||
"bcrt1qyqta5muj054x43cmk8rv43up84kefz9ej3y27n",
|
||||
"bcrt1qcswaeygm5w0y7xysqkn4uy9d6u0x6yxtlyntn6",
|
||||
"bcrt1qwjz0ez6g763cfty6pf984247yngfs75h2n6ccz",
|
||||
]
|
||||
address_obj_list = wallet.get_associated_addresses("test_service")
|
||||
# Reserving 3 addresses results in an empty list since we already have 3 unused addresses (the first one was used) reserved
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ def test_abandon_purged_tx(caplog, request, devices_filled_data_folder, device_m
|
|||
# TODO: Make a test fixture in conftest.py that sets up already funded wallets
|
||||
# for a bitcoin core hot wallet.
|
||||
wallet_manager = WalletManager(
|
||||
210100,
|
||||
devices_filled_data_folder,
|
||||
rpc,
|
||||
"regtest",
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ def _check_port_free(port=8332):
|
|||
def test_SpecterMigrator(empty_data_folder, caplog):
|
||||
|
||||
caplog.set_level(logging.DEBUG)
|
||||
assert _check_port_free()
|
||||
assert _check_port_free(), "You probably have a bitcoind running on port 8332"
|
||||
assert MigDataManager.initial_data()["events"] == []
|
||||
assert MigDataManager.initial_data()["migration_executions"] == []
|
||||
assert len(os.listdir(empty_data_folder)) == 0
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from mock import patch
|
||||
from cryptoadvance.specterext.swan.service import SwanService
|
||||
from cryptoadvance.specter.wallet import Wallet
|
||||
import json
|
||||
|
||||
|
||||
|
|
@ -11,7 +12,10 @@ class SwanServiceNoEncryption(SwanService):
|
|||
@patch(
|
||||
"cryptoadvance.specterext.swan.client.SwanClient.update_autowithdrawal_addresses"
|
||||
)
|
||||
def test_reserve_addresses(mocked_update_autowithdrawal_addresses, app_no_node, wallet):
|
||||
def test_reserve_addresses(
|
||||
mocked_update_autowithdrawal_addresses, app_no_node, trezor_wallet_acc5: Wallet
|
||||
):
|
||||
wallet = trezor_wallet_acc5
|
||||
mocked_update_autowithdrawal_addresses.return_value = "some_id"
|
||||
specter = app_no_node.specter
|
||||
storage_manager = specter.service_unencrypted_storage_manager
|
||||
|
|
@ -25,11 +29,11 @@ def test_reserve_addresses(mocked_update_autowithdrawal_addresses, app_no_node,
|
|||
swan.reserve_addresses(wallet, label="Swan withdrawals", num_addresses=5)
|
||||
# Check that the correct addresse list was passed to the client's update_autowithdrawal_addresses-method, should be addresses #1, #3, #5, #7, #9 from the test_wallet (the first address is skipped)
|
||||
addresses = [
|
||||
"bcrt1qsqnuk9hulcfta7kj7687favjv66d5e9yy0lr7t",
|
||||
"bcrt1qee494mauu3fv5aje0t4p6e52hvwq6d5hcqfxqt",
|
||||
"bcrt1qpnem6p9vr8rmjsf7k49p9sleu0h020g34ggn6k",
|
||||
"bcrt1q8534jsqkympwaelaqxhvfr6hc3g8y4kjtgr6d6",
|
||||
"bcrt1qxd6ndd7mt7jqut7797l84675fz4kqhs4fcfgny",
|
||||
"bcrt1qrfdnsdhmp5chxxexywdz37ppre7s5f0y4z4ykn",
|
||||
"bcrt1qp8hq4ngf0uy4r5atackw9ak5ngl8vfd54dz226",
|
||||
"bcrt1qsdzhz4q8y32maeay899jyfdw03pdlrrktx838g",
|
||||
"bcrt1qc65gchplxw57e7hdzdxcudjq90y0y3mxm9m96l",
|
||||
"bcrt1qasuqqj5u7t5e8zr3ug68yzfr2fjj4eg4u4ucj4",
|
||||
]
|
||||
assert (
|
||||
mocked_update_autowithdrawal_addresses.call_args_list[0].kwargs["addresses"]
|
||||
|
|
@ -48,8 +52,8 @@ def test_reserve_addresses(mocked_update_autowithdrawal_addresses, app_no_node,
|
|||
swan.reserve_addresses(wallet, label="Swan withdrawals", num_addresses=7)
|
||||
# Adding address #11 and #13
|
||||
additional_addresses = [
|
||||
"bcrt1q32gd5s7rk9ptkv8e74q4c64ntf48u4sza6c9d9",
|
||||
"bcrt1q463mg67f3tj5d223vf6387ty30qlx2wep4s5gp",
|
||||
"bcrt1qa4n6687f53recfcthfu2xpgcwcqvmzz4pdfw98",
|
||||
"bcrt1qcxq5md4jsnldpc6fswld8edaxgzpstamswt3r6",
|
||||
]
|
||||
addresses.extend(additional_addresses)
|
||||
assert (
|
||||
|
|
@ -81,7 +85,10 @@ class SwanServiceWithMockedMethods(SwanService):
|
|||
|
||||
|
||||
@patch("cryptoadvance.specterext.swan.client.SwanClient.set_autowithdrawal")
|
||||
def test_set_autowithdrawal_settings(mocked_set_autowithdrawal, app_no_node, wallet):
|
||||
def test_set_autowithdrawal_settings(
|
||||
mocked_set_autowithdrawal, app_no_node, trezor_wallet_acc3
|
||||
):
|
||||
wallet = trezor_wallet_acc3
|
||||
autowithdrawal_api_response = """
|
||||
{
|
||||
"entity": "automaticWithdrawal",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue