Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export device types and labels #1226

Merged
merged 16 commits into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from 6 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
4 changes: 2 additions & 2 deletions src/cryptoadvance/specter/cli/cli_noded.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,9 @@ def noded(
)
sys.exit(1)
except Exception as e:
if e.startswith("There is already a node running!"):
if str(e).startswith("There is already a node running!"):
echo(f"{e} please reset via:")
echo(f"python3 -m cryptoadvacne.specter {node_impl}d --reset")
echo(f"python3 -m cryptoadvance.specter {node_impl}d --reset")
if not nodocker:
tags_of_image = [
image.split(":")[-1] for image in my_node.btcd_container.image.tags
Expand Down
1 change: 1 addition & 0 deletions src/cryptoadvance/specter/devices/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .device_types import DeviceTypes
from .coldcard import ColdCard
from .trezor import Trezor
from .ledger import Ledger
Expand Down
3 changes: 2 additions & 1 deletion src/cryptoadvance/specter/devices/bitbox02.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from . import DeviceTypes
from .hwi_device import HWIDevice


class BitBox02(HWIDevice):
device_type = "bitbox02"
device_type = DeviceTypes.BITBOX02
name = "BitBox02"
icon = "bitbox02_icon.svg"
supports_hwi_multisig_display_address = True
Expand Down
3 changes: 2 additions & 1 deletion src/cryptoadvance/specter/devices/bitcoin_core.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import shutil
from embit import bip39, bip32, networks
from . import DeviceTypes
from ..device import Device
from ..helpers import alias
from ..util.descriptor import AddChecksum
Expand All @@ -16,7 +17,7 @@


class BitcoinCore(Device):
device_type = "bitcoincore"
device_type = DeviceTypes.BITCOINCORE
name = "Bitcoin Core (hot wallet)"
icon = "bitcoincore_icon.svg"

Expand Down
3 changes: 2 additions & 1 deletion src/cryptoadvance/specter/devices/cobo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import hashlib

# from ..device import Device
from . import DeviceTypes
from .coldcard import ColdCard
from hwilib.psbt import PSBT
from binascii import a2b_base64
Expand All @@ -11,7 +12,7 @@


class Cobo(ColdCard):
device_type = "cobo"
device_type = DeviceTypes.COBO
name = "Cobo Vault"
icon = "cobo_icon.svg"

Expand Down
12 changes: 12 additions & 0 deletions src/cryptoadvance/specter/devices/device_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class DeviceTypes:
BITBOX02 = "bitbox02"
BITCOINCORE = "bitcoincore"
COBO = "cobo"
COLDCARD = "coldcard"
ELECTRUM = "electrum"
ELEMENTSCORE = "elementscore"
GENERICDEVICE = "other"
KEEPKEY = "keepkey"
LEDGER = "ledger"
SPECTERDIY = "specter"
TREZOR = "trezor"
5 changes: 3 additions & 2 deletions src/cryptoadvance/specter/devices/electrum.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from binascii import a2b_base64
from ..util.base43 import b43_encode
from typing import List
from . import DeviceTypes
from ..util.base43 import b43_encode
from ..device import Device


class Electrum(Device):
device_type = "electrum"
device_type = DeviceTypes.ELECTRUM
name = "Electrum"
icon = "electrum_icon.svg"

Expand Down
3 changes: 2 additions & 1 deletion src/cryptoadvance/specter/devices/elements_core.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import os
from embit import bip39
from embit.liquid.slip77 import master_blinding_from_seed
from . import DeviceTypes
from .bitcoin_core import BitcoinCore


class ElementsCore(BitcoinCore):
device_type = "elementscore"
device_type = DeviceTypes.ELEMENTSCORE
name = "Elements Core (hot wallet)"
icon = "elementscore_icon.svg"

Expand Down
3 changes: 2 additions & 1 deletion src/cryptoadvance/specter/devices/generic.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from . import DeviceTypes
from ..device import Device


class GenericDevice(Device):
device_type = "other"
device_type = DeviceTypes.GENERICDEVICE
name = "Other"

sd_card_support = True
Expand Down
3 changes: 2 additions & 1 deletion src/cryptoadvance/specter/devices/keepkey.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from . import DeviceTypes
from .hwi_device import HWIDevice
from .hwi.keepkey import KeepkeyClient


class Keepkey(HWIDevice):
device_type = "keepkey"
device_type = DeviceTypes.KEEPKEY
name = "KeepKey"
icon = "keepkey_icon.svg"

Expand Down
3 changes: 2 additions & 1 deletion src/cryptoadvance/specter/devices/ledger.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from . import DeviceTypes
from .hwi_device import HWIDevice


class Ledger(HWIDevice):
device_type = "ledger"
device_type = DeviceTypes.LEDGER
name = "Ledger"
icon = "ledger_icon.svg"

Expand Down
3 changes: 2 additions & 1 deletion src/cryptoadvance/specter/devices/specter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import hashlib
from . import DeviceTypes
from .sd_card_device import SDCardDevice
from .hwi.specter_diy import enumerate as specter_enumerate, SpecterClient
from ..helpers import to_ascii20
Expand All @@ -10,7 +11,7 @@


class Specter(SDCardDevice):
device_type = "specter"
device_type = DeviceTypes.SPECTERDIY
name = "Specter-DIY"
icon = "specter_icon.svg"

Expand Down
3 changes: 2 additions & 1 deletion src/cryptoadvance/specter/devices/trezor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from . import DeviceTypes
from .hwi_device import HWIDevice
from .hwi.trezor import TrezorClient


class Trezor(HWIDevice):
device_type = "trezor"
device_type = DeviceTypes.TREZOR
name = "Trezor"
icon = "trezor_icon.svg"

Expand Down
61 changes: 47 additions & 14 deletions src/cryptoadvance/specter/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,16 +241,32 @@ def generate_mnemonic(strength=256):
return words


def wallet_type_by_derivation(derivation):
simple_derivation = derivation.replace("'", "").replace("h", "")
if simple_derivation.startswith("m/49"):
return "sh-wpkh"
elif simple_derivation.startswith("m/84"):
return "wpkh"
elif simple_derivation.startswith("m/48") and simple_derivation.endswith("/1"):
return "sh-wsh"
elif simple_derivation.startswith("m/48") and simple_derivation.endswith("/2"):
return "wsh"
else:
raise Exception(f"Unrecognized derivation: {derivation}")
stepansnigirev marked this conversation as resolved.
Show resolved Hide resolved


def parse_wallet_data_import(wallet_data):
"""Parses wallet JSON for import, takes JSON in a supported format
and returns a tuple of wallet name, wallet descriptor, and cosigners types (if known, electrum only for now)
and returns a tuple of wallet name, wallet descriptor, and cosigners types (electrum and newer Specter backups)
Supported formats: Specter, Electrum, Account Map (Fully Noded, Gordian, Sparrow etc.)
"""
cosigners_types = []
# specter format

# Specter-DIY format
if "recv_descriptor" in wallet_data:
wallet_name = wallet_data.get("name", "Imported Wallet")
recv_descriptor = wallet_data.get("recv_descriptor", None)

# Electrum multisig
elif "x1/" in wallet_data:
i = 1
Expand All @@ -260,29 +276,33 @@ def parse_wallet_data_import(wallet_data):
xpubs += "[{}]{}/0/*,".format(
d["derivation"].replace("m", d["root_fingerprint"]), d["xpub"]
)
cosigners_types.append(d["hw_type"])
cosigners_types.append({"type": d["hw_type"], "label": d["label"]})
i += 1
xpubs = xpubs.rstrip(",")
if wallet_data["addresses"]["receiving"][0].startswith("bc") or wallet_data[
"addresses"
]["receiving"][0].startswith("tb"):
wallet_type = "wsh"

if "derivation" in wallet_data["x1/"]:
stepansnigirev marked this conversation as resolved.
Show resolved Hide resolved
wallet_type = wallet_type_by_derivation(wallet_data["x1/"]["derivation"])
else:
wallet_type = "sh-wsh"
raise Exception('"derivation" not found in "x1/" in Electrum backup json')

required_sigs = int(wallet_data.get("wallet_type").split("of")[0])
recv_descriptor = "{}(sortedmulti({}, {}))".format(
wallet_type, required_sigs, xpubs
)
wallet_name = "Electrum {} of {}".format(required_sigs, i - 1)

# Electrum singlesig
elif "keystore" in wallet_data:
wallet_name = wallet_data["keystore"]["label"]
if wallet_data["addresses"]["receiving"][0].startswith("bc") or wallet_data[
"addresses"
]["receiving"][0].startswith("tb"):
wallet_type = "wpkh"

if "derivation" in wallet_data["keystore"]:
wallet_type = wallet_type_by_derivation(
wallet_data["keystore"]["derivation"]
)
else:
wallet_type = "sh-wpkh"
raise Exception(
'"derivation" not found in "keystore" in Electrum backup json'
)
recv_descriptor = "{}({})".format(
wallet_type,
"[{}]{}/0/*,".format(
Expand All @@ -292,10 +312,23 @@ def parse_wallet_data_import(wallet_data):
wallet_data["keystore"]["xpub"],
),
)
cosigners_types = [wallet_data["keystore"]["hw_type"]]
cosigners_types = [
{
"type": wallet_data["keystore"]["hw_type"],
"label": wallet_data["keystore"]["label"],
}
]

# Current Specter backups
else:
# Newer exports are able to reinitialize device types but stay backwards
# compatible with older backups.
if "devices" in wallet_data:
cosigners_types = wallet_data["devices"]

wallet_name = wallet_data.get("label", "Imported Wallet")
recv_descriptor = wallet_data.get("descriptor", None)

return (wallet_name, recv_descriptor, cosigners_types)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ def cleanup_docker_bitcoind(*args):
)
else:
self.rpcconn = rpcconn

logger.info("Started docker bitcoind")

return

def stop_bitcoind(self):
Expand Down
41 changes: 26 additions & 15 deletions src/cryptoadvance/specter/process_controller/node_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def start_node(
Specify a longer timeout for slower devices (e.g. Raspberry Pi)
"""
if self.check_existing() != None:
logger.warn(f"Reusing existing {self.node_impl}d")
logger.warning(f"Reusing existing {self.node_impl}d")
return self.rpcconn

logger.debug(f"Starting {self.node_impl}d")
Expand Down Expand Up @@ -270,16 +270,22 @@ def wait_for_node(rpcconn, timeout=15):
time.sleep(0.5)
i = i + 1
if i % 10 == 0:
logger.info(f"Timeout reached in {timeout - i/2} seconds")
logger.info(f"Node timeout in {timeout - i/2} seconds")
if i > (2 * timeout):
try:
NodeController.check_node(rpcconn, raise_exception=True)
except Exception as e:
raise ExtProcTimeoutException(
f"Timeout while trying to reach node {rpcconn.render_url(password_mask=True)} because {e}".format(
rpcconn
if "Verifying blocks..." in str(e) or "Loading wallet..." in str(e):
# Timed out too soon while node is still spinning up
logger.info("Giving node more time to restart...")
i = 0
pass
else:
raise ExtProcTimeoutException(
f"Timeout while trying to reach node {rpcconn.render_url(password_mask=True)} because {e}".format(
rpcconn
)
)
)

@staticmethod
def render_rpc_options(rpcconn):
Expand Down Expand Up @@ -379,12 +385,14 @@ def _start_node(
)

# This function is redirecting to the class.member as it needs a fixed parameterlist: (signal_number, stack)
def cleanup_node(signal_number, stack):
def cleanup_node(signal_number=None, stack=None):
self.cleanup_node(cleanup_hard, datadir)

if cleanup_at_exit:
logger.info("Register function cleanup_node for SIGINT and SIGTERM")
# atexit.register(cleanup_bitcoind)
logger.info(
"Register function cleanup_node for atexit, SIGINT, and SIGTERM"
)
atexit.register(cleanup_node)
# This is for CTRL-C --> SIGINT
signal.signal(signal.SIGINT, cleanup_node)
# This is for kill $pid --> SIGTERM
Expand Down Expand Up @@ -414,11 +422,14 @@ def cleanup_node(self, cleanup_hard=None, datadir=None):
f"Cleaning up (signal:{cleanup_hard} (sig_int: {signal.SIGINT}), datadir:{datadir})"
)
if cleanup_hard:
self.node_proc.kill() # might be usefull for e.g. testing. We can't wait for so long
logger.info(
f"Killed {self.node_impl}d with pid {self.node_proc.pid}, Removing {self.datadir}"
)
shutil.rmtree(self.datadir, ignore_errors=True)
try:
self.node_proc.kill() # might be usefull for e.g. testing. We can't wait for so long
logger.info(
f"Killed {self.node_impl}d with pid {self.node_proc.pid}, Removing {self.datadir}"
)
shutil.rmtree(self.datadir, ignore_errors=True)
except Exception as e:
logger.debug(e)
else:
try:
self.node_proc.terminate() # might take a bit longer than kill but it'll preserve block-height
Expand Down Expand Up @@ -469,7 +480,7 @@ def find_node_executable(node_impl):
return f"tests/{node_impl}/bin/{node_impl}d"
else:
# First list files in the folders above:
logger.warn(f"Couldn't find reasonable executable for {node_impl}")
logger.warning(f"Couldn't find reasonable executable for {node_impl}")
# hmmm, maybe we have a bitcoind on the PATH
return which(f"{node_impl}d")

Expand Down
Loading