Bugfixes: Various bugfixes (#1959)

* missing __init__.py

* Add Device modules to hidden imports

* make cli-arguments work in electron

* Bugfix devices not shown

* additional checks for wallet_import

* Update src/cryptoadvance/specter/util/wallet_importer.py

Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>

* Update src/cryptoadvance/specter/util/wallet_importer.py

Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>

* Update src/cryptoadvance/specter/util/wallet_importer.py

Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>

Co-authored-by: k9ert <kim@swanbitcoin.com>
Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>
This commit is contained in:
k9ert 2022-11-07 17:08:54 +01:00 committed by GitHub
parent 1d2fea976a
commit bef9cf69f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 160 additions and 29 deletions

View file

@ -157,6 +157,8 @@ release_electron_linux_windows:
stage: releasing
only:
- tags
dependencies:
- release_binary_windows
before_script:
- python3 -V # Print out python version for debugging
- apt update

View file

@ -348,10 +348,8 @@ function startSpecterd(specterdPath) {
if (specterdArgs == null) {
specterdArgs = []
}
let specterdExtraArgs = appSettings.specterdCLIArgs.split('--')
specterdExtraArgs = specterdExtraArgs.filter(Boolean)
specterdExtraArgs.forEach((arg, index) => specterdExtraArgs[index] = '--' + arg.trim())
let specterdExtraArgs = appSettings.specterdCLIArgs.split(' ')
specterdExtraArgs.forEach((arg, index) => specterdExtraArgs[index] = arg.trim())
specterdArgs = specterdArgs.concat(specterdExtraArgs)
}

View file

@ -7,7 +7,7 @@ import requests
from cryptoadvance.specter.specter_error import SpecterError
from cryptoadvance.specter.util.common import str2bool
from ..helpers import is_testnet, normalize_address
from ..helpers import normalize_address
from ..util.descriptor import AddChecksum, Descriptor
logger = logging.getLogger(__name__)

View file

@ -4,7 +4,6 @@ import psutil
from pathlib import Path
from .helpers import is_testnet
from .specter_error import SpecterError, ExtProcTimeoutException
from .rpc import (
get_default_datadir,

View file

@ -429,16 +429,37 @@ class ServiceManager:
@classmethod
def get_service_packages(cls):
"""returns a list of strings containing the service-classes (+ controller/config-classes)
"""returns a list of strings containing the service-classes (+ controller +config-classes +devices)
This is used for hiddenimports in pyinstaller
"""
arr = get_subclasses_for_clazz(Service)
logger.info(f"initial arr: {arr}")
arr.extend(
get_classlist_of_type_clazz_from_modulelist(
Service, ProductionConfig.EXTENSION_LIST
)
)
logger.info(f"After extending: {arr}")
# Before we transform the arr into an array of strings, we iterate through all services to discover
# the devices which might be specified in there
devices_arr = []
for clazz in arr:
if hasattr(clazz, "devices"):
logger.debug("class {clazz} has devices: {clazz.devices}")
for device in clazz.devices:
try:
import_module(device)
devices_arr.append(device)
except ModuleNotFoundError as e:
pass
# Transform into array of strings
arr = [clazz.__module__ for clazz in arr]
# Add the devices
arr.extend(devices_arr)
logger.debug(f"After transforming + devices: {arr}")
# Controller-Packagages from the services are not imported via the service but via the baseclass
# Therefore hiddenimport don't find them. We have to do it here.
cont_arr = [

View file

@ -110,6 +110,10 @@ class AbstractNode(PersistentObject):
def network_parameters(self):
return get_network(self.chain)
@property
def is_testnet(self):
return is_testnet(self.chain)
@property
def is_running(self):
if self.network_info["version"] == 999999:
@ -582,10 +586,6 @@ class Node(AbstractNode):
def chain(self):
return self.info["chain"]
@property
def is_testnet(self):
return is_testnet(self.chain)
@property
def asset_labels(self):
if not self.is_liquid:

View file

@ -25,7 +25,7 @@ from cryptoadvance.specter.services.service_encrypted_storage import (
ServiceUnencryptedStorageManager,
)
from .helpers import clean_psbt, deep_update, is_liquid, is_testnet, get_asset_label
from .helpers import clean_psbt, deep_update, is_liquid, get_asset_label
from .internal_node import InternalNode
from .liquid.rpc import LiquidRPC
from .managers.config_manager import ConfigManager

View file

@ -8,6 +8,8 @@ from embit.descriptor.arguments import AllowedDerivation
from embit.liquid.descriptor import LDescriptor
from flask_babel import lazy_gettext as _
from cryptoadvance.specter.device import Device
from ..key import Key
from ..managers.wallet_manager import WalletManager
from ..server_endpoints import flash
@ -58,8 +60,36 @@ class WalletImporter:
self.unknown_cosigners,
self.unknown_cosigners_types,
) = self.parse_signers(device_manager.devices, self.cosigners_types)
try:
self.check_chain(specter.node)
except Exception as e:
logger.exception(e)
raise SpecterError(f"Invalid chain: {e}")
self.wallet_type = "multisig" if self.descriptor.is_basic_multisig else "simple"
def check_chain(self, node):
cosigner: Device
for cosigner in self.cosigners:
key_chain_list = [key.is_testnet for key in cosigner.keys]
logger.debug(key_chain_list)
if node.is_testnet not in key_chain_list:
raise SpecterError(
f"The device {cosigner} does not have any key for the chain {node.chain}!"
)
for key, label in self.unknown_cosigners:
if key.is_testnet != node.is_testnet:
raise SpecterError(
f"The device {label} has at least one key for the chain {key.metadata['chain'] } whereas your node is on the chain: {node.chain}!"
)
key: Key
for key in self.keys:
if key.is_testnet != node.is_testnet:
raise SpecterError(
f"The key {key} belongs to the chain {key.metadata['chain'] } but your node is on the chain {node.chain}!"
)
def check_descriptor(self):
# Sparrow fix: if all keys have None as allowed derivation - set allowed derivation to [0, None]
if all(
@ -86,6 +116,15 @@ class WalletImporter:
)
def parse_signers(self, devices, cosigners_types):
"""returns:
* keys:
* keys which are already existing in the device_manager
* keys:[<cryptoadvance.specter.key.Key object at 0x7fda8451e0e0>]
# cosigners:
* [<cryptoadvance.specter.devices.generic.GenericDevice object at 0x7fda8451fcd0>]
# unknown_cosigners:[(<cryptoadvance.specter.key.Key object at 0x7fda8451e830>, 'Signer - K'), (<cryptoadvance.specter.key.Key object at 0x7fda94235570>, 'Signer - Tired no WP')]
"""
keys = []
cosigners = []
unknown_cosigners = []
@ -119,7 +158,11 @@ class WalletImporter:
unknown_cosigners_types.append("other")
else:
unknown_cosigners_types.append(cosigners_types[i]["type"])
logger.debug("parse_signers returning:")
logger.debug(f"keys:{keys}")
logger.debug(f"cosigners:{cosigners}")
logger.debug(f"unknown_cosigners:{unknown_cosigners}")
logger.debug(f"unknown_cosigners_types:{unknown_cosigners_types}")
return (keys, cosigners, unknown_cosigners, unknown_cosigners_types)
def create_nonexisting_signers(self, device_manager, request_form):

View file

@ -56,9 +56,12 @@ def test_ServiceManager_get_service_x_dirs(caplog):
os.chdir("../")
def test_ServiceManager_get_service_packages():
def test_ServiceManager_get_service_packages(caplog):
caplog.set_level(logging.DEBUG)
packages = ServiceManager.get_service_packages()
assert "cryptoadvance.specterext.swan.service" in packages
assert "cryptoadvance.specterext.electrum.service" in packages
assert "cryptoadvance.specterext.electrum.devices.electrum" in packages
@pytest.fixture

View file

@ -1,36 +1,89 @@
import json
import logging
import pytest
import time
from cryptoadvance.specter.key import Key
from cryptoadvance.specter.managers.device_manager import DeviceManager
from cryptoadvance.specter.specter_error import SpecterError
from cryptoadvance.specter.user import User
from cryptoadvance.specter.util.descriptor import Descriptor
from cryptoadvance.specter.util.wallet_importer import WalletImporter
from mock import MagicMock, call, patch
# coldcard, trezor import
wallet_coldcardtrezor_json = """
{"label": "MyTestMultisig",
"blockheight": 0,
"descriptor":
"wsh(sortedmulti(1,[fb7c1f11/48h/1h/0h/2h]tpubDExnGppazLhZPNadP8Q5Vgee2QcvbyAf9GvGaEY7ALVJREaG2vdTqv1MHRoDtPaYP3y1DGVx7wrKKhsLhs26GY263uE6Wi3qNbi71AHZ6p7/0/*,[1ef4e492/48h/1h/0h/2h]tpubDFiVCZzdarbyk8kE65tjRhHCambEo8iTx4xkXL8b33BKZj66HWsDnUb3rg4GZz6Mwm6vTNyzRCjYtiScCQJ77ENedb2deDDtcoNQXiUouJQ/0/*))#s0jemlck",
"devices":
[
{"type": "coldcard",
"label": "MyColdcard"},
{"type": "trezor",
"label": "MyTestTrezor"}
]
}
"""
wallet_ledger_json = """
{
"address": "bcrt1q66f7yuxegnzpv9jv5pp6pqfy7smq4ydq643d4e",
"address_index": 7,
"address_type": "bech32",
"alias": "myledger_2",
"blockheight": 8244,
"change_address": "bcrt1qk087hncum850z0ghpss2lm49kcfu6lppngdeem",
"change_descriptor": "wpkh([309c1ac3/84h/1h/0h]tpubDCRGLu8Kjg9dKLUqrVCBZA9TSUkga56X3mnfqWqSVWrooavQdCdYzUdY7peGk7KW7H6gmdJobm5rXr1NQmoGhuPf4aqD3GhgtggYdwMwRom/1/*)#qqulrlpr",
"change_index": 0,
"change_keypool": 300,
"description": "Single (Segwit)",
"devices": [
"myledger"
],
"keypool": 300,
"keys": [
{
"derivation": "m/84h/1h/0h",
"fingerprint": "309c1ac3",
"original": "vpub5YP7DKxoj37DLwukjadqw4d5mRN8hScTfv9vvAzXPbM9x6dF3fTLbueRqNeKDo4RW6LAf6bp9LYi56HdaX81DzaAGJh4BvQUmGxr6peWDsP",
"purpose": "#0 Single Sig (Segwit)",
"type": "wpkh",
"xpub": "tpubDCRGLu8Kjg9dKLUqrVCBZA9TSUkga56X3mnfqWqSVWrooavQdCdYzUdY7peGk7KW7H6gmdJobm5rXr1NQmoGhuPf4aqD3GhgtggYdwMwRom"
}
],
"labels": {},
"name": "MyLedger",
"recv_descriptor": "wpkh([309c1ac3/84h/1h/0h]tpubDCRGLu8Kjg9dKLUqrVCBZA9TSUkga56X3mnfqWqSVWrooavQdCdYzUdY7peGk7KW7H6gmdJobm5rXr1NQmoGhuPf4aqD3GhgtggYdwMwRom/0/*)#35e7723m",
"sigs_required": 1
}
"""
def test_WalletImporter_parse_wallet_data_import():
(
wallet_name,
recv_descriptor,
cosigners_types,
) = WalletImporter.parse_wallet_data_import(json.loads(wallet_coldcardtrezor_json))
assert wallet_name == "MyTestMultisig"
assert recv_descriptor.startswith("wsh(sortedmulti(1,[fb7c1")
assert cosigners_types[0]["type"] == "coldcard"
assert cosigners_types[0]["label"] == "MyColdcard"
assert cosigners_types[1]["type"] == "trezor"
assert cosigners_types[1]["label"] == "MyTestTrezor"
@patch("cryptoadvance.specter.util.wallet_importer.flash", print)
@patch("cryptoadvance.specter.util.wallet_importer._", lambda x: x)
def test_WalletImporter_unit():
specter_mock = MagicMock()
specter_mock.chain = "regtest"
specter_mock.node.chain = "regtest"
specter_mock.node.is_testnet = True
# coldcard, trezor import
wallet_json = """
{"label": "MyTestMultisig",
"blockheight": 0,
"descriptor":
"wsh(sortedmulti(1,[fb7c1f11/48h/1h/0h/2h]tpubDExnGppazLhZPNadP8Q5Vgee2QcvbyAf9GvGaEY7ALVJREaG2vdTqv1MHRoDtPaYP3y1DGVx7wrKKhsLhs26GY263uE6Wi3qNbi71AHZ6p7/0/*,[1ef4e492/48h/1h/0h/2h]tpubDFiVCZzdarbyk8kE65tjRhHCambEo8iTx4xkXL8b33BKZj66HWsDnUb3rg4GZz6Mwm6vTNyzRCjYtiScCQJ77ENedb2deDDtcoNQXiUouJQ/0/*))#s0jemlck",
"devices":
[
{"type": "coldcard",
"label": "MyColdcard"},
{"type": "trezor",
"label": "MyTestTrezor"}
]
}
"""
wallet_json = wallet_coldcardtrezor_json
wallet_importer = WalletImporter(wallet_json, specter_mock)
assert wallet_importer.wallet_name == "MyTestMultisig"
@ -136,6 +189,18 @@ def test_WalletImporter_unit():
)
@patch("cryptoadvance.specter.util.wallet_importer.flash", print)
@patch("cryptoadvance.specter.util.wallet_importer._", lambda x: x)
def test_WalletImporter_unit_fail(caplog):
caplog.set_level(logging.DEBUG)
specter_mock = MagicMock()
specter_mock.node.chain = "main"
wallet_json = wallet_ledger_json
with pytest.raises(SpecterError):
wallet_importer = WalletImporter(wallet_json, specter_mock)
@pytest.mark.slow
def test_WalletImporter_integration(specter_regtest_configured, bitcoin_regtest):
"""