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

Add limited support for electrum segwit seeds #513

Merged
merged 22 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c1d320f
Add limited support for electrum segwit seeds
Dec 4, 2023
89f6cec
making suggested changes for electrum derivation path and removing on…
Dec 18, 2023
eaa29ab
derivation path override to make address verifiction work for electru…
Dec 18, 2023
1f0a4dd
since EncodeQR generates the seed internally for xpub export, we have…
Dec 18, 2023
289b382
pass seed to EncodeQR instead of mnemonic and passphrase to simplify
Dec 19, 2023
656110b
Revert "pass seed to EncodeQR instead of mnemonic and passphrase to s…
Dec 19, 2023
f8aca10
export correct xpub version bytes for electrum seeds
Dec 24, 2023
8f0a8fe
Merge branch 'SeedSigner:dev' into PRChanges
BamaHodl Mar 5, 2024
40520f8
integrating PR review recommendations for electrum seed support
Mar 19, 2024
8c9fd5c
display warning screen before user inputs electrum seed to notify som…
Mar 19, 2024
1820997
disabled BIP85 child seeds for electrum seeds since it doesn't apply
Mar 19, 2024
2355c53
updating docs to elaborate on current Electrum seed support and limit…
Mar 19, 2024
ce9113f
fix bad paste in unverified address deriv path
Mar 19, 2024
0fc3ce4
merging main upstream repo into fork
Jun 21, 2024
8a83f32
remove unneccessary include
Jun 21, 2024
c0f1ad6
small cleanup
Jun 21, 2024
b430d50
pass sig_type to BaseXpubQrEncoder so that it can get correct version
Jun 21, 2024
d483864
fixing merge conflict with new test in test_flows_tools.py
Jun 30, 2024
a28f162
Update electrum.md
BamaHodl Jul 8, 2024
4ea2d72
more explicit about which electrum seeds supported in Advanced option…
Jul 8, 2024
46061c5
correcting electrum doc to match change in option name
Jul 8, 2024
75d9f5f
make sure to use the correct list for script_types
Jul 8, 2024
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
15 changes: 15 additions & 0 deletions src/seedsigner/gui/screens/seed_screens.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,21 @@ def __post_init__(self):
)
self.components.append(self.fingerprint_icontl)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our opinionated / slightly non-standard formatting: 3 blank lines between classes or other elements at the top nesting level, 2 blank lines between functions or other elements at nested levels.

We're not fully consistent with that everywhere, but mostly so.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will make the spacing consistent--was just an oversight

class SeedSwitchElectrumModeScreen(ButtonListScreen):
button_data: list = None

def __post_init__(self):
self.show_back_button = False
self.title = "Switch to Electrum?"
self.is_bottom_list: bool = True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Switch" isn't quite the right verb here.

Would "Electrum Seed Detected" fit in the title? If not, the title can just say "Electrum Seed" since the body text will have more info.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same response as below--agree on removing switch language but it is in a tiny doubt if the user meant to enter BIP-39 or electrum. Alternatively we could check if it meets both checksums and only have a confirmation screen in that edge case, and go with the Electrum Seed declaration and warning if it is a valid electrum seed but not a valid BIP-39 checksum. Does that sound good? Confirmation screen if it meets both checks, warning screen if it's electrum-only

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am leaning strongly toward this approach now:

#513 (comment)

In which case there is no ambiguity about what the user is trying to do. Metaphorically: enter via the "Electrum seed" door or gtfo with that Electrum seed! (similarly, no attempt(?) should be made to even check if it's a valid bip39 seed if entered via the Electrum seed flow)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, It's making more sense to me too, especially after learning the collisions are more often than I had initially thought. And in this way it keeps the BIP-39 flow completely unaffected by default even when they happen to collide with a valid Electrum seed (apparently 9/2048 chance which is much higher than I had calculated).
This also removes the need for a new screen altogether possibly, at least during seed entry


super().__post_init__()

self.components.append(TextArea(
text="It appears this is an Electrum-style seed, switch it to be so? Some functions are not supported for Electrum seeds",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't say "It appears". It IS an Electrum seed, as far as we can tell. "switch" still bugs me.

This may be too extreme, but I'm slightly more inclined to a more drastic presentation:

Screenshot 2024-03-03 at 5 17 44 PM

Copy link
Contributor Author

@BamaHodl BamaHodl Mar 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree we can make the warning more extreme and remove the 'switch' wording. But there is a small possibility it is in fact a BIP-39 seed, which is why I put a confirmation screen

screen_y=self.top_nav.height + GUIConstants.COMPONENT_PADDING,
is_text_centered=True,
))


@dataclass
Expand Down
12 changes: 11 additions & 1 deletion src/seedsigner/helpers/embit_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# TODO: PR these directly into `embit`? Or replace with new/existing methods already in `embit`?


def get_standard_derivation_path(network: str = SettingsConstants.MAINNET, wallet_type: str = SettingsConstants.SINGLE_SIG, script_type: str = SettingsConstants.NATIVE_SEGWIT) -> str:
def get_standard_derivation_path(network: str = SettingsConstants.MAINNET, wallet_type: str = SettingsConstants.SINGLE_SIG, script_type: str = SettingsConstants.NATIVE_SEGWIT, is_electrum : bool = False) -> str:
if network == SettingsConstants.MAINNET:
network_path = "0'"
elif network == SettingsConstants.TESTNET:
Expand All @@ -31,6 +31,8 @@ def get_standard_derivation_path(network: str = SettingsConstants.MAINNET, walle

if wallet_type == SettingsConstants.SINGLE_SIG:
if script_type == SettingsConstants.NATIVE_SEGWIT:
if is_electrum:
return f"m/0h"
return f"m/84'/{network_path}/0'"
elif script_type == SettingsConstants.NESTED_SEGWIT:
return f"m/49'/{network_path}/0'"
Expand All @@ -41,6 +43,8 @@ def get_standard_derivation_path(network: str = SettingsConstants.MAINNET, walle

elif wallet_type == SettingsConstants.MULTISIG:
if script_type == SettingsConstants.NATIVE_SEGWIT:
if is_electrum:
return f"m/1h"
return f"m/48'/{network_path}/0'/2'"
elif script_type == SettingsConstants.NESTED_SEGWIT:
return f"m/48'/{network_path}/0'/1'"
Expand All @@ -52,6 +56,12 @@ def get_standard_derivation_path(network: str = SettingsConstants.MAINNET, walle
raise Exception("Unexpected wallet type") # checks that all inputs are from the same wallet


def detect_version(derivation_path: str, network: str = SettingsConstants.MAINNET, wallet_type: str = SettingsConstants.SINGLE_SIG, is_electrum : bool = False) -> str:
embit_network = NETWORKS[SettingsConstants.map_network_to_embit(network)]
if is_electrum:
return embit_network["zpub"] if SettingsConstants.SINGLE_SIG == wallet_type else embit_network["Zpub"]
else:
return embit.bip32.detect_version(derivation_path, default="xpub", network=embit_network)

def get_xpub(seed_bytes, derivation_path: str, embit_network: str = "main") -> HDKey:
root = bip32.HDKey.from_seed(seed_bytes, version=NETWORKS[embit_network]["xprv"])
Expand Down
21 changes: 16 additions & 5 deletions src/seedsigner/models/encode_qr.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from seedsigner.helpers.ur2.ur_encoder import UREncoder
from seedsigner.helpers.ur2.ur import UR
from seedsigner.helpers.qr import QR
from seedsigner.helpers import embit_utils
from seedsigner.models.qr_type import QRType
from seedsigner.models.seed import Seed
from seedsigner.models.settings import SettingsConstants
Expand Down Expand Up @@ -40,6 +41,8 @@ class EncodeQR:
wordlist_language_code: str = SettingsConstants.WORDLIST_LANGUAGE__ENGLISH
bitcoin_address: str = None
signed_message: str = None
is_electrum : bool = False
sig_type : str = SettingsConstants.SINGLE_SIG

def __post_init__(self):
self.qr = QR()
Expand All @@ -66,7 +69,9 @@ def __post_init__(self):
passphrase=self.passphrase,
derivation=self.derivation,
network=self.network,
wordlist_language_code=self.wordlist_language_code
wordlist_language_code=self.wordlist_language_code,
is_electrum=self.is_electrum,
sig_type=self.sig_type
)

elif self.qr_type == QRType.XPUB__UR:
Expand All @@ -76,7 +81,9 @@ def __post_init__(self):
passphrase=self.passphrase,
derivation=self.derivation,
network=self.network,
wordlist_language_code=self.wordlist_language_code
wordlist_language_code=self.wordlist_language_code,
is_electrum=self.is_electrum,
sig_type=self.sig_type
)

elif self.qr_type == QRType.XPUB__SPECTER:
Expand All @@ -86,7 +93,9 @@ def __post_init__(self):
passphrase=self.passphrase,
derivation=self.derivation,
network=self.network,
wordlist_language_code=self.wordlist_language_code
wordlist_language_code=self.wordlist_language_code,
is_electrum=self.is_electrum,
sig_type=self.sig_type
)


Expand Down Expand Up @@ -356,7 +365,7 @@ def next_part(self):


class XpubQrEncoder(BaseQrEncoder):
def __init__(self, seed_phrase, passphrase, derivation, network, wordlist_language_code):
def __init__(self, seed_phrase, passphrase, derivation, network, wordlist_language_code, is_electrum : bool = False, sig_type : str = SettingsConstants.SINGLE_SIG):
self.seed_phrase = seed_phrase
self.passphrase = passphrase
self.derivation = derivation
Expand All @@ -369,10 +378,12 @@ def __init__(self, seed_phrase, passphrase, derivation, network, wordlist_langua
if self.wordlist == None:
raise Exception('Wordlist Required')

version = bip32.detect_version(self.derivation, default="xpub", network=NETWORKS[SettingsConstants.map_network_to_embit(self.network)])
self.seed = Seed(mnemonic=self.seed_phrase,
passphrase=self.passphrase,
wordlist_language_code=wordlist_language_code)
if is_electrum:
self.seed.switch_to_electrum()
version = embit_utils.detect_version(self.derivation, network, sig_type, self.seed.is_electrum)
self.root = bip32.HDKey.from_seed(self.seed.seed_bytes, version=NETWORKS[SettingsConstants.map_network_to_embit(self.network)]["xprv"])
self.fingerprint = self.root.child(0).fingerprint
self.xprv = self.root.derive(self.derivation)
Expand Down
49 changes: 47 additions & 2 deletions src/seedsigner/models/seed.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import unicodedata
import hashlib
import hmac

from binascii import hexlify
from embit import bip39, bip32, bip85
Expand All @@ -25,9 +27,11 @@ def __init__(self,
self._mnemonic: List[str] = unicodedata.normalize("NFKD", " ".join(mnemonic).strip()).split()

self._passphrase: str = ""
self._is_electrum = False
self.set_passphrase(passphrase, regenerate_seed=False)

self.seed_bytes: bytes = None
self.electrum_seed_bytes: bytes = None
self._generate_seed()


Expand All @@ -42,11 +46,42 @@ def get_wordlist(wordlist_language_code: str = SettingsConstants.WORDLIST_LANGUA

def _generate_seed(self) -> bool:
try:
self.seed_bytes = bip39.mnemonic_to_seed(self.mnemonic_str, password=self._passphrase, wordlist=self.wordlist)
self._generate_electrum_seed()
if not self.is_electrum:
# only do regular bip39 if not already confirmed this seed is_electrum (i.e. during passphrase change)
self.seed_bytes = bip39.mnemonic_to_seed(self.mnemonic_str, password=self._passphrase, wordlist=self.wordlist)
except Exception as e:
print(repr(e))
raise InvalidSeedException(repr(e))
# only re-raise if we didn't get valid electrum seed
# we can raise in the electrum confirmation if they didn't mean to enter an electrum seed
if not self.electrum_seed_bytes:
raise InvalidSeedException(repr(e))

def _generate_electrum_seed(self) -> bool:
if len(self._mnemonic) != 12:
return False
s = hmac.digest(b"Seed version", self.mnemonic_str.encode('utf8'), hashlib.sha512).hex()
if s[0] != '0' and s[0] != '1':
return False
length = int(s[0]) + 2
prefix = s[0:length];
# only support Electrum Segwit version for now
if SettingsConstants.ELECTRUM_SEED_SEGWIT == prefix:
self.electrum_seed_bytes=hashlib.pbkdf2_hmac('sha512', self.mnemonic_str.encode('utf-8'), b'electrum' + self._passphrase.encode('utf-8'), iterations = SettingsConstants.ELECTRUM_PBKDF2_ROUNDS)
if self.is_electrum:
# if already is_electrum (passphrase change), make sure to set main seed_bytes
self.seed_bytes = self.electrum_seed_bytes
return True
else:
return False

def switch_to_electrum(self):
self.seed_bytes = self.electrum_seed_bytes
self._is_electrum = True

@property
def is_electrum(self) -> bool:
return self._is_electrum

@property
def mnemonic_str(self) -> str:
Expand Down Expand Up @@ -81,6 +116,8 @@ def passphrase_display(self):
def set_passphrase(self, passphrase: str, regenerate_seed: bool = True):
if passphrase:
self._passphrase = unicodedata.normalize("NFKD", passphrase)
if self.is_electrum:
self._passphrase = Seed.normalize_electrum_passphrase(passphrase)
else:
# Passphrase must always have a string value, even if it's just the empty
# string.
Expand All @@ -90,6 +127,14 @@ def set_passphrase(self, passphrase: str, regenerate_seed: bool = True):
# Regenerate the internal seed since passphrase changes the result
self._generate_seed()

@staticmethod
BamaHodl marked this conversation as resolved.
Show resolved Hide resolved
def normalize_electrum_passphrase(passphrase : str) -> str:
passphrase = unicodedata.normalize('NFKD', passphrase)
# lower
passphrase = passphrase.lower()
# normalize whitespaces
passphrase = u' '.join(passphrase.split())
return passphrase

@property
def wordlist(self) -> List[str]:
Expand Down
5 changes: 5 additions & 0 deletions src/seedsigner/models/settings_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ def map_network_to_embit(cls, network) -> str:
TYPE__ENABLED_DISABLED_PROMPT_REQUIRED,
]

# Electrum seed constants
ELECTRUM_SEED_LEGACY = "01"
ELECTRUM_SEED_SEGWIT = "100"
ELECTRUM_SEED_2FA = "101"
ELECTRUM_PBKDF2_ROUNDS=2048

@dataclass
class SettingsEntry:
Expand Down
55 changes: 43 additions & 12 deletions src/seedsigner/views/seed_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ def run(self):
class SeedFinalizeView(View):
FINALIZE = "Done"
PASSPHRASE = "BIP-39 Passphrase"
CUSTOM_EXTENSION = "Custom Extension"
SWITCH = "Switch"
CANCEL = "Cancel"

def __init__(self):
super().__init__()
Expand All @@ -297,9 +300,26 @@ def __init__(self):


def run(self):
if self.seed.electrum_seed_bytes:
# see if user wants to enter electrum mode since it's a valid electrum seed
button_data = [self.SWITCH, self.CANCEL]
selected_menu_num = self.run_screen(
seed_screens.SeedSwitchElectrumModeScreen,
button_data=button_data
)
if button_data[selected_menu_num] == self.SWITCH:
self.seed.switch_to_electrum()
# recalculate fingerprint
self.fingerprint = self.seed.get_fingerprint(network=self.settings.get_value(SettingsConstants.SETTING__NETWORK))
elif not self.seed.seed_bytes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing an elif button_data[selected_menu_num] == self.CANCEL branch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It falls through and behaves correctly (i.e. tries to treat it as BIP-39), but agree it can be more clear at least with a elif with a comment there

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user select "Cancel", they're explicitly saying, "Oh, never mind, I don't want to load this Electrum seed." So I think the better behavior would be to discard the seed and just return us to Home.

Copy link
Contributor Author

@BamaHodl BamaHodl Mar 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean now. I had intended the flow to be -- if it's valid electrum seed, ask the user if they intended that and if not, then fall through and try to treat it as BIP-39. So in this new case it should have 3 buttons then--treat as Electrum, treat as Bip-39, and cancel (or just remove cancel altogether).

If the preferred way is to do the 2-step (which I'm leaning more to myself), it could be that the user puts the SS in "Electrum mode" in advanced settings, and then we don't even need an extra screen--only support electrum seeds while in electrum mode and only support bip-39 otherwise, i.e. behavior doesn't change at all for bip-39 seeds in default mode even if their seed collides with a valid electrum seed. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only support electrum seeds while in electrum mode and only support bip-39 otherwise

That's too restrictive for me. In my mind, SeedSigner has a core bip39 foundation. Electrum seed support may be added on the side, but it should never override/exclude the main bip39 use cases. That's why I like the explicit "Load Electrum Seed" option; it provides a single, specific path for this one side option.

And if you go through the normal bip39 mechanisms and load a mnemonic that could also be a valid Electrum seed, it doesn't matter, we should never even check the Electrum side in the normal flows. Doesn't exist.

# we only got here because it was a valid electrum seed
# thus if user didn't mean to enter an electrum seed, it's invalid
raise InvalidSeedException(f"Invalid seed entered")

button_data = [self.FINALIZE]
passphrase_button = self.CUSTOM_EXTENSION if self.seed.is_electrum else self.PASSPHRASE
if self.settings.get_value(SettingsConstants.SETTING__PASSPHRASE) != SettingsConstants.OPTION__DISABLED:
button_data.append(self.PASSPHRASE)
button_data.append(passphrase_button)

selected_menu_num = self.run_screen(
seed_screens.SeedFinalizeScreen,
Expand All @@ -311,7 +331,7 @@ def run(self):
seed_num = self.controller.storage.finalize_pending_seed()
return Destination(SeedOptionsView, view_args={"seed_num": seed_num}, clear_history=True)

elif button_data[selected_menu_num] == self.PASSPHRASE:
elif button_data[selected_menu_num] == passphrase_button:
return Destination(SeedAddPassphraseView)


Expand All @@ -323,7 +343,8 @@ def __init__(self):


def run(self):
ret = self.run_screen(seed_screens.SeedAddPassphraseScreen, passphrase=self.seed.passphrase)
passphrase_title="Custom Extension" if self.seed.is_electrum else "BIP-39 Passphrase"
ret = self.run_screen(seed_screens.SeedAddPassphraseScreen, passphrase=self.seed.passphrase, title=passphrase_title)

if ret == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
Expand Down Expand Up @@ -612,7 +633,9 @@ def __init__(self, seed_num: int, sig_type: str):
def run(self):
from .tools_views import ToolsAddressExplorerAddressTypeView
args = {"seed_num": self.seed_num, "sig_type": self.sig_type}
if len(self.settings.get_value(SettingsConstants.SETTING__SCRIPT_TYPES)) == 1:
seed = self.controller.storage.seeds[self.seed_num]
script_types = [SettingsConstants.NATIVE_SEGWIT] if seed.is_electrum else self.settings.get_value(SettingsConstants.SETTING__SCRIPT_TYPES)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This confused me a bit as a user. What's the rationale for not supporting other single sig script types w/Electrum seeds? And if it has to stay limited to Native Segwit, I think a notification screen might be needed (e.g. "Note: Only native segwit is supported for Electrum seeds" w/a "Next" button).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In electrum wallet the versioning indicates the script type. Ascii "100" version means native segwit and only native segwit. I may be able to add legacy seeds eventually as well, but I've only worked on native segwit version so far, so allowing that option wouldn't be useful until supporting legacy seed types. There is no electrum seed version for taproot yet either.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we need to tell the user that right now they're only going to get native segwit addrs. Then later if/when more options are available, it's the normal button list. Consider the user who's trying to get legacy types but doesn't realize the limitation. It'd be pure frustration that the UI just skips straight to native segwit.

if len(script_types) == 1:
# Nothing to select; skip this screen
args["script_type"] = self.settings.get_value(SettingsConstants.SETTING__SCRIPT_TYPES)[0]

Expand Down Expand Up @@ -799,7 +822,8 @@ def run(self):
derivation_path = embit_utils.get_standard_derivation_path(
network=self.settings.get_value(SettingsConstants.SETTING__NETWORK),
wallet_type=self.sig_type,
script_type=self.script_type
script_type=self.script_type,
is_electrum=self.seed.is_electrum
)

if self.settings.get_value(SettingsConstants.SETTING__XPUB_DETAILS) == SettingsConstants.OPTION__DISABLED:
Expand All @@ -814,10 +838,11 @@ def run(self):

try:
embit_network = NETWORKS[SettingsConstants.map_network_to_embit(self.settings.get_value(SettingsConstants.SETTING__NETWORK))]
version = embit.bip32.detect_version(
derivation_path,
default="xpub",
network=embit_network
version = embit_utils.detect_version(
derivation_path,
self.settings.get_value(SettingsConstants.SETTING__NETWORK),
self.sig_type,
self.seed.is_electrum
)
root = embit.bip32.HDKey.from_seed(
self.seed.seed_bytes,
Expand Down Expand Up @@ -845,6 +870,7 @@ def run(self):
dict(seed_num=self.seed_num,
coordinator=self.coordinator,
derivation_path=derivation_path,
sig_type=self.sig_type
)
)

Expand All @@ -854,7 +880,7 @@ def run(self):


class SeedExportXpubQRDisplayView(View):
def __init__(self, seed_num: int, coordinator: str, derivation_path: str):
def __init__(self, seed_num: int, coordinator: str, derivation_path: str, sig_type: str = SettingsConstants.SINGLE_SIG):
super().__init__()
self.seed = self.controller.get_seed(seed_num)

Expand Down Expand Up @@ -884,7 +910,9 @@ def __init__(self, seed_num: int, coordinator: str, derivation_path: str):
network=self.settings.get_value(SettingsConstants.SETTING__NETWORK),
qr_type=qr_type,
qr_density=qr_density,
wordlist_language_code=self.seed.wordlist_language_code
wordlist_language_code=self.seed.wordlist_language_code,
is_electrum=self.seed.is_electrum,
sig_type=sig_type
)


Expand Down Expand Up @@ -1299,7 +1327,7 @@ def run(self):
num_modules_standard = 29
num_modules_compact = 25

if self.settings.get_value(SettingsConstants.SETTING__COMPACT_SEEDQR) != SettingsConstants.OPTION__ENABLED:
if seed.is_electrum or self.settings.get_value(SettingsConstants.SETTING__COMPACT_SEEDQR) != SettingsConstants.OPTION__ENABLED:
# Only configured for standard SeedQR
return Destination(
SeedTranscribeSeedQRWarningView,
Expand Down Expand Up @@ -1650,6 +1678,9 @@ def __init__(self, seed_num: int = None):
else:
self.seed = None
self.address = self.controller.unverified_address["address"]
# override derivation path if it's electrum as bip-39 path was set by default
if self.seed and self.seed.is_electrum:
self.controller.unverified_address["derivation_path"] = 'm/0h'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hard-coding here and the limited Electrum support in embit_util.get_standard_derivation_path() seems pretty awkward, but I don't have an improvement suggestion yet.

self.derivation_path = self.controller.unverified_address["derivation_path"]
self.script_type = self.controller.unverified_address["script_type"]
self.sig_type = self.controller.unverified_address["sig_type"]
Expand Down
1 change: 1 addition & 0 deletions src/seedsigner/views/tools_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ def __init__(self, seed_num: int = None, script_type: str = None, custom_derivat
network=self.settings.get_value(SettingsConstants.SETTING__NETWORK),
wallet_type=SettingsConstants.SINGLE_SIG,
script_type=self.script_type,
is_electrum = self.seed.is_electrum
)

data["derivation_path"] = derivation_path
Expand Down
Loading