Skip to content

Commit a9b56bc

Browse files
moneymanolisKim Neunert
andauthored
Bugfix: Saving settings bugfix (#19)
* controller fix * start testing * fix tests * refactoring, tests, connection feedback fine-tuning * quick fix for integration test Co-authored-by: Kim Neunert <k9ert@gmx.de>
1 parent eca9771 commit a9b56bc

File tree

7 files changed

+163
-60
lines changed

7 files changed

+163
-60
lines changed

src/cryptoadvance/specterext/spectrum/controller.py

Lines changed: 13 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,21 @@
33
from flask import current_app as app
44
from flask_login import login_required, current_user
55

6-
from cryptoadvance.specter.specter import Specter
76
from cryptoadvance.specter.services.controller import user_secret_decrypted_required
87
from cryptoadvance.specter.user import User
98
from cryptoadvance.specter.wallet import Wallet
109
from cryptoadvance.specter.specter_error import SpecterError
1110

1211
from cryptoadvance.specterext.spectrum.spectrum_node import SpectrumNode
1312
from .service import SpectrumService
13+
from .controller_helpers import ext, specter, evaluate_current_status, check_for_node_on_same_network
1414

1515

1616
logger = logging.getLogger(__name__)
1717

1818
spectrum_endpoint = SpectrumService.blueprint
1919

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

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

2822

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

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

137+
if node_is_running_before_request == success and success == True and host_before_request == host_after_request:
138+
# Case 1: We changed a setting that didn't impact the Spectrum node, currently only the menu item setting
139+
return redirect(url_for(f"{ SpectrumService.get_blueprint_name()}.settings_get"))
140+
143141
changed_host, check_port_and_ssl = evaluate_current_status(
144-
node_is_running_before_request,
142+
node_is_running_before_request,
143+
success,
145144
host_before_request,
146145
host_after_request,
147-
success
148146
)
149147

150148
return render_template("spectrum/spectrum_setup.jinja",
151-
success=success,
149+
success=success,
150+
node_is_running_before_request = node_is_running_before_request,
152151
changed_host=changed_host,
153152
host_type = option_mode,
154153
check_port_and_ssl = check_port_and_ssl,
155154
)
156155

157-
def check_for_node_on_same_network(spectrum_node):
158-
if spectrum_node is not None:
159-
current_spectrum_chain = spectrum_node.chain
160-
nodes_current_chain = specter().node_manager.nodes_by_chain(current_spectrum_chain)
161-
# Check whether there is a Bitcoin Core node for the same network:
162-
core_node_exists = False
163-
for node in nodes_current_chain:
164-
logger.debug(node)
165-
if node.fqcn != "cryptoadvance.specterext.spectrum.spectrum_node.SpectrumNode" and not node.is_liquid:
166-
return True
167-
return False
168-
169-
def evaluate_current_status(node_is_running_before_request, host_before_request, host_after_request, success):
170-
''' Figures out whether the:
171-
* the user changed the host and/or
172-
* the user changed the port/ssl
173-
and returns two booleans: changed_host, check_port_and_ssl
174-
useful for user-feedback.
175-
'''
176-
changed_host = False
177-
check_port_and_ssl = False
178-
if node_is_running_before_request == success and success == True and host_before_request == host_after_request:
179-
# Case 1: We changed a setting that didn't impact the Spectrum node, currently only the menu item setting
180-
return redirect(url_for(f"{ SpectrumService.get_blueprint_name()}.settings_get"))
181-
if node_is_running_before_request == success and success == True and host_before_request != host_after_request:
182-
# Case 2: We changed the host but switched from one working connection to another one
183-
changed_host = True
184-
if node_is_running_before_request and not success:
185-
# Case 3: We changed the host from a working to a broken connection
186-
if host_before_request != host_after_request:
187-
changed_host = True
188-
# 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
189-
# TODO: Worth it to also check for changes in the port / ssl configs?
190-
else:
191-
check_port_and_ssl = True
192-
if not node_is_running_before_request and success:
193-
# Case 5: We changed the host from a broken to a working connection
194-
if host_before_request != host_after_request and host_before_request != None:
195-
changed_host = True
196-
# Case 6: We didn't change the host but only the port and / or ssl config which did the trick
197-
else:
198-
# Not necessary since this is set to False by default, just to improve readability
199-
check_port_and_ssl = False
200-
return changed_host, check_port_and_ssl
156+
157+
158+
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import logging
2+
3+
from cryptoadvance.specter.specter import Specter
4+
from flask import current_app as app
5+
6+
from .service import SpectrumService
7+
8+
logger = logging.getLogger(__name__)
9+
10+
def ext() -> SpectrumService:
11+
''' convenience for getting the extension-object'''
12+
return app.specter.ext["spectrum"]
13+
14+
def specter() -> Specter:
15+
''' convenience for getting the specter-object'''
16+
return app.specter
17+
18+
def check_for_node_on_same_network(spectrum_node, specter: Specter):
19+
if spectrum_node is not None:
20+
current_spectrum_chain = spectrum_node.chain
21+
nodes_current_chain = specter.node_manager.nodes_by_chain(current_spectrum_chain)
22+
# Check whether there is a Bitcoin Core node for the same network:
23+
core_node_exists = False
24+
for node in nodes_current_chain:
25+
logger.debug(node)
26+
if node.fqcn != "cryptoadvance.specterext.spectrum.spectrum_node.SpectrumNode" and not node.is_liquid:
27+
return True
28+
return False
29+
30+
def evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request):
31+
''' Figures out whether the:
32+
* the user changed the host and/or
33+
* the user changed the port/ssl
34+
and returns two booleans: changed_host, check_port_and_ssl
35+
useful for user-feedback.
36+
'''
37+
changed_host = False
38+
check_port_and_ssl = False
39+
if node_is_running_before_request == success and success == True and host_before_request != host_after_request:
40+
# Case 2: We changed the host but switched from one working connection to another one
41+
changed_host = True
42+
if node_is_running_before_request and not success:
43+
# Case 3: We changed the host from a working to a broken connection
44+
if host_before_request != host_after_request:
45+
changed_host = True
46+
# 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
47+
# TODO: Worth it to also check for changes in the port / ssl configs?
48+
else:
49+
check_port_and_ssl = True
50+
if not node_is_running_before_request and success:
51+
# Case 5: We changed the host from a broken to a working connection
52+
if host_before_request != host_after_request and host_before_request != None:
53+
changed_host = True
54+
# Case 6: We didn't change the host but only the port and / or ssl config which did the trick
55+
else:
56+
# Not necessary since this is set to False by default, just to improve readability
57+
check_port_and_ssl = False
58+
if not node_is_running_before_request and not success:
59+
# Case 7: We don't get a connection running for the current host, perhaps it is due to the port / ssl
60+
if host_before_request == host_after_request and host_before_request != None:
61+
check_port_and_ssl = True
62+
# Case 7: Unclear what the issue is, best to check everything
63+
if host_before_request != host_after_request:
64+
changed_host = True
65+
check_port_and_ssl = True
66+
67+
68+
return changed_host, check_port_and_ssl

src/cryptoadvance/specterext/spectrum/templates/spectrum/spectrum_setup.jinja

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
<span class="feedback-text">{{ _("Specter is connected via Spectrum to a public Electrum server!") }}</span>
3636
{% else %}
3737
<img class="feedback-icon" src="{{ url_for('static', filename='img/failed.svg') }}"/><br>
38-
{% if changed_host %}
38+
{% if changed_host and node_is_running_before_request %}
3939
<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.
4040
Consider switching back to the one you had chosen before.") }}</span>
4141
{% else %}
@@ -49,11 +49,13 @@
4949
<span class="feedback-text">{{ _("Specter is connected via Spectrum to a manually configured Electrum server!") }}</span>
5050
{% else %}
5151
<img class="feedback-icon" src="{{ url_for('static', filename='img/failed.svg') }}"/><br>
52-
{% if changed_host %}
52+
{% if changed_host and node_is_running_before_request %}
5353
<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.
5454
Consider switching back to the one you had chosen before.") }}</span>
55-
{% elif check_port_and_ssl %}
55+
{% elif not changed_host and check_port_and_ssl %}
5656
<span class="feedback-text">{{ _("Cannot connect to the manually configured Electrum server. Double-check that the port and SSL settings are correct.") }}</span>
57+
{% elif changed_host and check_port_and_ssl %}
58+
<span class="feedback-text">{{ _("Cannot connect to the manually configured Electrum server. Double-check all the configuration settings.") }}</span>
5759
{% else %}
5860
<span class="feedback-text">{{ _("Cannot connect to the manually configured Electrum server.") }}</span>
5961
{% endif %}

tests/conftest.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
import tempfile
88
import traceback
99
from binascii import hexlify
10-
1110
import pytest
11+
1212
from cryptoadvance.specter.key import Key
13+
from cryptoadvance.specter.persistence import PersistentObject
1314
from cryptoadvance.spectrum.cli import setup_logging
1415
from cryptoadvance.spectrum.config import TestConfig
1516
from cryptoadvance.spectrum.server import create_app, init_app
17+
from cryptoadvance.specterext.spectrum.spectrum_node import SpectrumNode
18+
1619
from embit import script
1720
from embit.bip32 import NETWORKS, HDKey
1821
from embit.bip39 import mnemonic_to_seed
@@ -155,3 +158,20 @@ def client(app):
155158
"""a test_client from an initialized Flask-App"""
156159
return app.test_client()
157160

161+
@pytest.fixture
162+
def spectrum_node():
163+
""" A Spectrum node """
164+
node_dict= {
165+
"python_class": "cryptoadvance.specterext.spectrum.spectrum_node.SpectrumNode",
166+
"name": "Spectrum Node",
167+
"alias": "spectrum_node",
168+
"host": "electrum.emzy.de",
169+
"port": 5002,
170+
"ssl": True
171+
}
172+
173+
# Instantiate via PersistentObject:
174+
sn = PersistentObject.from_json(node_dict)
175+
assert type(sn) == SpectrumNode
176+
return sn
177+

tests/integration/wallet_import_rescan.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import logging
1616
import shutil
1717
import sys
18+
import pytest
1819
from decimal import Decimal, getcontext
1920
from random import random
2021
import time
@@ -34,6 +35,7 @@
3435
number_of_txs = 10
3536
keypoolrefill = number_of_txs
3637

38+
# Test is green in local dev
3739
def test_import_nigiri_core(
3840
caplog,
3941
empty_data_folder,
@@ -55,6 +57,10 @@ def test_import_nigiri_core(
5557
rootkey_hold_accident
5658
)
5759

60+
# Skipping for now
61+
# 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
62+
# Definitely makes no sense to do to the sending from w0 to w1 twice.
63+
@pytest.mark.skip
5864
def test_import_spectrum_nigiri_electrs_core(
5965
caplog,
6066
app_nigiri,
@@ -66,7 +72,10 @@ def test_import_spectrum_nigiri_electrs_core(
6672
''' Test is using a rpc connecting to spectrum which is connected via nigiri's electrs to nigiri's core
6773
'''
6874
caplog.set_level(logging.INFO)
75+
# Can't be right here!
6976
spectrum_rpc: BitcoinRPC = BitcoinRPC(user="", password="", host="localhost", port="8081")
77+
spectrum_node = SpectrumNode(host=host, port=port, ssl=ssl)
78+
7079
btc_rpc: BitcoinRPC = BitcoinRPC(user="admin1", password="123", host="localhost", port="18443")
7180
runtest_import_via(spectrum_rpc,
7281
btc_rpc,
@@ -170,7 +179,7 @@ def runtest_import_via(spectrum_rpc,
170179
spectrum_rpc,
171180
"regtest",
172181
None,
173-
allow_threading=False,
182+
allow_threading_for_testing=False,
174183
)
175184
wallet: Wallet = wm.create_wallet(
176185
"hold_accident", 1, "wpkh", [acc0key], MagicMock()

tests/test_controller_helpers.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from cryptoadvance.specterext.spectrum.controller_helpers import evaluate_current_status, check_for_node_on_same_network
2+
from mock import Mock
3+
4+
def test_evaluate_current_status():
5+
# We changed the host and made the connection work again with it
6+
node_is_running_before_request, success, host_before_request, host_after_request = False, True, "127.0.0.1", "electrum.emzy.de"
7+
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
8+
assert changed_host == True
9+
assert check_port_and_ssl == False
10+
11+
# We didn't change anything
12+
node_is_running_before_request, success, host_before_request, host_after_request = True, True, "127.0.0.1", "127.0.0.1"
13+
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
14+
assert changed_host == False
15+
assert check_port_and_ssl == False
16+
17+
# We didn't change the host but the connection got lost, which indicates that the ssl / port config was changed
18+
node_is_running_before_request, success, host_before_request, host_after_request = True, False, "127.0.0.1", "127.0.0.1"
19+
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
20+
assert changed_host == False
21+
assert check_port_and_ssl == True
22+
23+
# We didn't change the host but the connection can still not be established
24+
node_is_running_before_request, success, host_before_request, host_after_request = False, False, "127.0.0.1", "127.0.0.1"
25+
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
26+
assert changed_host == False
27+
assert check_port_and_ssl == True
28+
29+
# We did change the host but the connection can still not be established
30+
node_is_running_before_request, success, host_before_request, host_after_request = False, False, "127.0.0.1", "electrum.emzy.de"
31+
changed_host, check_port_and_ssl = evaluate_current_status(node_is_running_before_request, success, host_before_request, host_after_request)
32+
assert changed_host == True
33+
assert check_port_and_ssl == True
34+
35+
def test_check_for_node_on_same_network():
36+
spectrum_node_mock = Mock()
37+
spectrum_node_mock.fqcn = "cryptoadvance.specterext.spectrum.spectrum_node.SpectrumNode"
38+
bitcoin_core_node_mock = Mock()
39+
bitcoin_core_node_mock.fqcn = "cryptoadvance.specter.node.Node"
40+
bitcoin_core_node_mock.is_liquid = False
41+
specter_mock = Mock()
42+
specter_mock.node_manager.nodes_by_chain.return_value = [spectrum_node_mock, bitcoin_core_node_mock]
43+
assert check_for_node_on_same_network(spectrum_node_mock, specter_mock) == True

tests/test_ext_bridge_rpc.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from cryptoadvance.specterext.spectrum.bridge_rpc import BridgeRPC
22
from flask import Flask
3+
import pytest
34

5+
6+
@pytest.mark.skip()
47
def test_getmininginfobridge(caplog, app: Flask):
58
print(app.spectrum)
69
brpc = BridgeRPC(app.spectrum)

0 commit comments

Comments
 (0)