Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 13 additions & 55 deletions src/cryptoadvance/specterext/spectrum/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,21 @@
from flask import current_app as app
from flask_login import login_required, current_user

from cryptoadvance.specter.specter import Specter
from cryptoadvance.specter.services.controller import user_secret_decrypted_required
from cryptoadvance.specter.user import User
from cryptoadvance.specter.wallet import Wallet
from cryptoadvance.specter.specter_error import SpecterError

from cryptoadvance.specterext.spectrum.spectrum_node import SpectrumNode
from .service import SpectrumService
from .controller_helpers import ext, specter, evaluate_current_status, check_for_node_on_same_network


logger = logging.getLogger(__name__)

spectrum_endpoint = SpectrumService.blueprint

def ext() -> SpectrumService:
''' convenience for getting the extension-object'''
return app.specter.ext["spectrum"]

def specter() -> Specter:
''' convenience for getting the specter-object'''
return app.specter


@spectrum_endpoint.route("/")
Expand Down Expand Up @@ -116,7 +110,7 @@ def settings_post():
# BETA_VERSION: Additional check that there is no Bitcoin Core node for the same network alongside the Spectrum node
spectrum_node = ext().spectrum_node

if check_for_node_on_same_network(spectrum_node):
if check_for_node_on_same_network(spectrum_node, specter()):
# Delete Spectrum node again (it wasn't saved to disk yet)
del specter().node_manager.nodes[spectrum_node.alias]
return render_template("spectrum/spectrum_setup_beta.jinja",
Expand All @@ -140,61 +134,25 @@ def settings_post():
host_after_request = ext().spectrum_node.host
logger.debug(f"The host after saving the new settings: {host_after_request}")

if node_is_running_before_request == success and success == True and host_before_request == host_after_request:
# Case 1: We changed a setting that didn't impact the Spectrum node, currently only the menu item setting
return redirect(url_for(f"{ SpectrumService.get_blueprint_name()}.settings_get"))

changed_host, check_port_and_ssl = evaluate_current_status(
node_is_running_before_request,
node_is_running_before_request,
success,
host_before_request,
host_after_request,
success
)

return render_template("spectrum/spectrum_setup.jinja",
success=success,
success=success,
node_is_running_before_request = node_is_running_before_request,
changed_host=changed_host,
host_type = option_mode,
check_port_and_ssl = check_port_and_ssl,
)

def check_for_node_on_same_network(spectrum_node):
if spectrum_node is not None:
current_spectrum_chain = spectrum_node.chain
nodes_current_chain = specter().node_manager.nodes_by_chain(current_spectrum_chain)
# Check whether there is a Bitcoin Core node for the same network:
core_node_exists = False
for node in nodes_current_chain:
logger.debug(node)
if node.fqcn != "cryptoadvance.specterext.spectrum.spectrum_node.SpectrumNode" and not node.is_liquid:
return True
return False

def evaluate_current_status(node_is_running_before_request, host_before_request, host_after_request, success):
''' Figures out whether the:
* the user changed the host and/or
* the user changed the port/ssl
and returns two booleans: changed_host, check_port_and_ssl
useful for user-feedback.
'''
changed_host = False
check_port_and_ssl = False
if node_is_running_before_request == success and success == True and host_before_request == host_after_request:
# Case 1: We changed a setting that didn't impact the Spectrum node, currently only the menu item setting
return redirect(url_for(f"{ SpectrumService.get_blueprint_name()}.settings_get"))
if node_is_running_before_request == success and success == True and host_before_request != host_after_request:
# Case 2: We changed the host but switched from one working connection to another one
changed_host = True
if node_is_running_before_request and not success:
# Case 3: We changed the host from a working to a broken connection
if host_before_request != host_after_request:
changed_host = True
# Case 4: We didn't change the host but probably other configs such as port and / or ssl which are likely the reason for the broken connection
# TODO: Worth it to also check for changes in the port / ssl configs?
else:
check_port_and_ssl = True
if not node_is_running_before_request and success:
# Case 5: We changed the host from a broken to a working connection
if host_before_request != host_after_request and host_before_request != None:
changed_host = True
# Case 6: We didn't change the host but only the port and / or ssl config which did the trick
else:
# Not necessary since this is set to False by default, just to improve readability
check_port_and_ssl = False
return changed_host, check_port_and_ssl



68 changes: 68 additions & 0 deletions src/cryptoadvance/specterext/spectrum/controller_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import logging

from cryptoadvance.specter.specter import Specter
from flask import current_app as app

from .service import SpectrumService

logger = logging.getLogger(__name__)

def ext() -> SpectrumService:
''' convenience for getting the extension-object'''
return app.specter.ext["spectrum"]

def specter() -> Specter:
''' convenience for getting the specter-object'''
return app.specter

def check_for_node_on_same_network(spectrum_node, specter: Specter):
if spectrum_node is not None:
current_spectrum_chain = spectrum_node.chain
nodes_current_chain = specter.node_manager.nodes_by_chain(current_spectrum_chain)
# Check whether there is a Bitcoin Core node for the same network:
core_node_exists = False
for node in nodes_current_chain:
logger.debug(node)
if node.fqcn != "cryptoadvance.specterext.spectrum.spectrum_node.SpectrumNode" and not node.is_liquid:
return True
return False

def evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request):
''' Figures out whether the:
* the user changed the host and/or
* the user changed the port/ssl
and returns two booleans: changed_host, check_port_and_ssl
useful for user-feedback.
'''
changed_host = False
check_port_and_ssl = False
if node_is_running_before_request == success and success == True and host_before_request != host_after_request:
# Case 2: We changed the host but switched from one working connection to another one
changed_host = True
if node_is_running_before_request and not success:
# Case 3: We changed the host from a working to a broken connection
if host_before_request != host_after_request:
changed_host = True
# Case 4: We didn't change the host but probably other configs such as port and / or ssl which are likely the reason for the broken connection
# TODO: Worth it to also check for changes in the port / ssl configs?
else:
check_port_and_ssl = True
if not node_is_running_before_request and success:
# Case 5: We changed the host from a broken to a working connection
if host_before_request != host_after_request and host_before_request != None:
changed_host = True
# Case 6: We didn't change the host but only the port and / or ssl config which did the trick
else:
# Not necessary since this is set to False by default, just to improve readability
check_port_and_ssl = False
if not node_is_running_before_request and not success:
# Case 7: We don't get a connection running for the current host, perhaps it is due to the port / ssl
if host_before_request == host_after_request and host_before_request != None:
check_port_and_ssl = True
# Case 7: Unclear what the issue is, best to check everything
if host_before_request != host_after_request:
changed_host = True
check_port_and_ssl = True


return changed_host, check_port_and_ssl
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<span class="feedback-text">{{ _("Specter is connected via Spectrum to a public Electrum server!") }}</span>
{% else %}
<img class="feedback-icon" src="{{ url_for('static', filename='img/failed.svg') }}"/><br>
{% if changed_host %}
{% if changed_host and node_is_running_before_request %}
<span class="feedback-text">{{ _("Cannot connect to the public Electrum server. You changed the settings from a working connection to a server that is not responding.
Consider switching back to the one you had chosen before.") }}</span>
{% else %}
Expand All @@ -49,11 +49,13 @@
<span class="feedback-text">{{ _("Specter is connected via Spectrum to a manually configured Electrum server!") }}</span>
{% else %}
<img class="feedback-icon" src="{{ url_for('static', filename='img/failed.svg') }}"/><br>
{% if changed_host %}
{% if changed_host and node_is_running_before_request %}
<span class="feedback-text">{{ _("Cannot connect to the manually configured Electrum server. You changed the settings from a working connection to a server that is not responding.
Consider switching back to the one you had chosen before.") }}</span>
{% elif check_port_and_ssl %}
{% elif not changed_host and check_port_and_ssl %}
<span class="feedback-text">{{ _("Cannot connect to the manually configured Electrum server. Double-check that the port and SSL settings are correct.") }}</span>
{% elif changed_host and check_port_and_ssl %}
<span class="feedback-text">{{ _("Cannot connect to the manually configured Electrum server. Double-check all the configuration settings.") }}</span>
{% else %}
<span class="feedback-text">{{ _("Cannot connect to the manually configured Electrum server.") }}</span>
{% endif %}
Expand Down
22 changes: 21 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@
import tempfile
import traceback
from binascii import hexlify

import pytest

from cryptoadvance.specter.key import Key
from cryptoadvance.specter.persistence import PersistentObject
from cryptoadvance.spectrum.cli import setup_logging
from cryptoadvance.spectrum.config import TestConfig
from cryptoadvance.spectrum.server import create_app, init_app
from cryptoadvance.specterext.spectrum.spectrum_node import SpectrumNode

from embit import script
from embit.bip32 import NETWORKS, HDKey
from embit.bip39 import mnemonic_to_seed
Expand Down Expand Up @@ -155,3 +158,20 @@ def client(app):
"""a test_client from an initialized Flask-App"""
return app.test_client()

@pytest.fixture
def spectrum_node():
""" A Spectrum node """
node_dict= {
"python_class": "cryptoadvance.specterext.spectrum.spectrum_node.SpectrumNode",
"name": "Spectrum Node",
"alias": "spectrum_node",
"host": "electrum.emzy.de",
"port": 5002,
"ssl": True
}

# Instantiate via PersistentObject:
sn = PersistentObject.from_json(node_dict)
assert type(sn) == SpectrumNode
return sn

11 changes: 10 additions & 1 deletion tests/integration/wallet_import_rescan.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import logging
import shutil
import sys
import pytest
from decimal import Decimal, getcontext
from random import random
import time
Expand All @@ -34,6 +35,7 @@
number_of_txs = 10
keypoolrefill = number_of_txs

# Test is green in local dev
def test_import_nigiri_core(
caplog,
empty_data_folder,
Expand All @@ -55,6 +57,10 @@ def test_import_nigiri_core(
rootkey_hold_accident
)

# Skipping for now
# It would make more sense to setup a Spectrum and a Spectrum node here and check whether an import of wallet "w1" results in the same balace
# Definitely makes no sense to do to the sending from w0 to w1 twice.
@pytest.mark.skip
def test_import_spectrum_nigiri_electrs_core(
caplog,
app_nigiri,
Expand All @@ -66,7 +72,10 @@ def test_import_spectrum_nigiri_electrs_core(
''' Test is using a rpc connecting to spectrum which is connected via nigiri's electrs to nigiri's core
'''
caplog.set_level(logging.INFO)
# Can't be right here!
spectrum_rpc: BitcoinRPC = BitcoinRPC(user="", password="", host="localhost", port="8081")
spectrum_node = SpectrumNode(host=host, port=port, ssl=ssl)

btc_rpc: BitcoinRPC = BitcoinRPC(user="admin1", password="123", host="localhost", port="18443")
runtest_import_via(spectrum_rpc,
btc_rpc,
Expand Down Expand Up @@ -170,7 +179,7 @@ def runtest_import_via(spectrum_rpc,
spectrum_rpc,
"regtest",
None,
allow_threading=False,
allow_threading_for_testing=False,
)
wallet: Wallet = wm.create_wallet(
"hold_accident", 1, "wpkh", [acc0key], MagicMock()
Expand Down
43 changes: 43 additions & 0 deletions tests/test_controller_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from cryptoadvance.specterext.spectrum.controller_helpers import evaluate_current_status, check_for_node_on_same_network
from mock import Mock

def test_evaluate_current_status():
# We changed the host and made the connection work again with it
node_is_running_before_request, success, host_before_request, host_after_request = False, True, "127.0.0.1", "electrum.emzy.de"
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
assert changed_host == True
assert check_port_and_ssl == False

# We didn't change anything
node_is_running_before_request, success, host_before_request, host_after_request = True, True, "127.0.0.1", "127.0.0.1"
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
assert changed_host == False
assert check_port_and_ssl == False

# We didn't change the host but the connection got lost, which indicates that the ssl / port config was changed
node_is_running_before_request, success, host_before_request, host_after_request = True, False, "127.0.0.1", "127.0.0.1"
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
assert changed_host == False
assert check_port_and_ssl == True

# We didn't change the host but the connection can still not be established
node_is_running_before_request, success, host_before_request, host_after_request = False, False, "127.0.0.1", "127.0.0.1"
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
assert changed_host == False
assert check_port_and_ssl == True

# We did change the host but the connection can still not be established
node_is_running_before_request, success, host_before_request, host_after_request = False, False, "127.0.0.1", "electrum.emzy.de"
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
assert changed_host == True
assert check_port_and_ssl == True

def test_check_for_node_on_same_network():
spectrum_node_mock = Mock()
spectrum_node_mock.fqcn = "cryptoadvance.specterext.spectrum.spectrum_node.SpectrumNode"
bitcoin_core_node_mock = Mock()
bitcoin_core_node_mock.fqcn = "cryptoadvance.specter.node.Node"
bitcoin_core_node_mock.is_liquid = False
specter_mock = Mock()
specter_mock.node_manager.nodes_by_chain.return_value = [spectrum_node_mock, bitcoin_core_node_mock]
assert check_for_node_on_same_network(spectrum_node_mock, specter_mock) == True
3 changes: 3 additions & 0 deletions tests/test_ext_bridge_rpc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from cryptoadvance.specterext.spectrum.bridge_rpc import BridgeRPC
from flask import Flask
import pytest


@pytest.mark.skip()
def test_getmininginfobridge(caplog, app: Flask):
print(app.spectrum)
brpc = BridgeRPC(app.spectrum)
Expand Down