Zeus Export

This commit is contained in:
scgbckbone 2024-04-19 00:33:13 +02:00 committed by doc-hex
parent ab894d174d
commit 524e40512c
6 changed files with 127 additions and 47 deletions

View File

@ -5,7 +5,8 @@
- Enhancement: Allow to specify start index for address explorer export and browsing
- Enhancement: Allow unlimited index for BIP-85 derivations. Needs to be enabled first in `Danger Zone`
- Enhancement: Add `Nunchuk` option to `Export Wallet`
- Enhancement: `View Identity` shows temporary seed active on the top
- Enhancement: Add `Zeus` option to `Export Wallet`
- Enhancement: `View Identity` shows temporary seed active on the top
- Change: `Passphrase` menu item is no longer offered if BIP39 passphrase
already in use. Use `Restore Master` with ability to keep or purge current
passphrase wallet settings.

View File

@ -27,6 +27,7 @@
- Enhancement: Allow to specify start index for address explorer export and browsing
- Enhancement: Add `Nunchuk` option to `Export Wallet`
- Enhancement: Add `Zeus` option to `Export Wallet`
- Enhancement: `View Identity` shows temporary seed active on the top
- Change: `Passphrase` menu item is no longer offered if BIP39 passphrase
already in use. Use `Restore Master` with ability to keep or purge current

View File

@ -8,7 +8,7 @@ import ckcc, pyb, version, uasyncio, sys, uos
from uhashlib import sha256
from uasyncio import sleep_ms
from ubinascii import hexlify as b2a_hex
from utils import imported, pretty_short_delay, problem_file_line, get_filesize
from utils import imported, problem_file_line, get_filesize
from utils import xfp2str, B2A, addr_fmt_label
from ux import ux_show_story, the_ux, ux_confirm, ux_dramatic_pause, ux_aborted
from ux import ux_enter_bip32_index, ux_input_text, import_export_prompt
@ -1079,29 +1079,29 @@ async def electrum_skeleton(*a):
elif ch != 'y':
return
# pick segwit or classic derivation+such
# - Ordering and terminology from similar screen in Electrum.
rv = []
rv.append(MenuItem(addr_fmt_label(AF_CLASSIC), f=electrum_skeleton_step2,
arg=(AF_CLASSIC, account_num)))
rv.append(MenuItem(addr_fmt_label(AF_P2WPKH_P2SH), f=electrum_skeleton_step2,
arg=(AF_P2WPKH_P2SH, account_num)))
rv.append(MenuItem(addr_fmt_label(AF_P2WPKH), f=electrum_skeleton_step2,
arg=(AF_P2WPKH, account_num)))
rv = [
MenuItem(addr_fmt_label(af), f=electrum_skeleton_step2,
arg=(af, account_num))
for af in [AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH]
]
the_ux.push(MenuSystem(rv))
def ss_descriptor_export_story(addition="", background=None):
def ss_descriptor_export_story(addition="", background="", acct=True):
# saves memory being in a function
return ("This saves a ranged xpub descriptor" + addition
+ (background or
'. Choose descriptor and address type for the wallet on next screens.'+PICK_ACCOUNT)
+ background
+ (PICK_ACCOUNT if acct else "")
+ SENSITIVE_NOT_SECRET)
async def ss_descriptor_skeleton(label, _, item):
async def ss_descriptor_skeleton(_0, _1, item):
# Export of descriptor data (wallet)
ch = await ux_show_story(ss_descriptor_export_story(), escape='1')
int_ext, addition = None, ""
allowed_af = [AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH]
if item.arg:
int_ext, allowed_af, ll = item.arg
addition = " for " + ll
ch = await ux_show_story(ss_descriptor_export_story(addition), escape='1')
account_num = 0
if ch == '1':
@ -1109,27 +1109,24 @@ async def ss_descriptor_skeleton(label, _, item):
elif ch != 'y':
return
int_ext = True
ch = await ux_show_story(
"To export receiving and change descriptors in one descriptor (<0;1> notation) press OK, "
"press (1) to export receiving and change descriptors separately.", escape='1')
if ch == "1":
int_ext = False
elif ch != "y":
return
if int_ext is None:
ch = await ux_show_story(
"To export receiving and change descriptors in one descriptor "
"(<0;1> notation) press OK, press (1) to export "
"receiving and change descriptors separately.", escape='1')
if ch == "x": return
int_ext = False if ch == "1" else True
# pick segwit or classic derivation+such
# - Ordering and terminology from similar screen in Electrum.
rv = []
rv.append(MenuItem(addr_fmt_label(AF_CLASSIC), f=descriptor_skeleton_step2,
arg=(AF_CLASSIC, account_num, int_ext)))
rv.append(MenuItem(addr_fmt_label(AF_P2WPKH_P2SH), f=descriptor_skeleton_step2,
arg=(AF_P2WPKH_P2SH, account_num, int_ext)))
rv.append(MenuItem(addr_fmt_label(AF_P2WPKH), f=descriptor_skeleton_step2,
arg=(AF_P2WPKH, account_num, int_ext)))
the_ux.push(MenuSystem(rv))
if len(allowed_af) == 1:
await make_descriptor_wallet_export(allowed_af[0], account_num,
int_ext=int_ext)
else:
rv = [
MenuItem(addr_fmt_label(af), f=descriptor_skeleton_step2,
arg=(af, account_num, int_ext))
for af in allowed_af
]
the_ux.push(MenuSystem(rv))
async def samourai_post_mix_descriptor_export(*a):
name = "POST-MIX"
@ -1150,7 +1147,7 @@ async def samourai_account_descriptor(name, account_num):
ch = await ux_show_story(
ss_descriptor_export_story(
addition=" for Samourai %s account." % name,
background=" ")
acct=False)
)
if ch != 'y':

View File

@ -139,8 +139,7 @@ async def write_text_file(fname_pattern, body, title, derive, addr_fmt):
from ux import import_export_prompt
choice = await import_export_prompt("%s file" % title, is_import=False,
no_qr=(not version.has_qwerty))
no_qr=(not version.has_qwerty))
if choice == KEY_CANCEL:
return
elif choice == KEY_QR:

View File

@ -171,6 +171,8 @@ WalletExportMenu = [
MenuItem("Bitcoin Core", f=bitcoin_core_skeleton),
MenuItem("Sparrow Wallet", f=named_generic_skeleton, arg="Sparrow"),
MenuItem("Nunchuk", f=named_generic_skeleton, arg="Nunchuk"),
MenuItem("Zeus", f=ss_descriptor_skeleton,
arg=(True, [AF_P2WPKH, AF_P2WPKH_P2SH], "Zeus Wallet")),
MenuItem("Electrum Wallet", f=electrum_skeleton),
MenuItem("Wasabi Wallet", f=wasabi_skeleton),
MenuItem("Unchained", f=unchained_capital_export),

View File

@ -16,7 +16,7 @@ from helpers import xfp2str, slip132undo
from conftest import simulator_fixed_xfp, simulator_fixed_tprv, simulator_fixed_words
from ckcc_protocol.constants import AF_CLASSIC, AF_P2WPKH
from pprint import pprint
from charcodes import KEY_NFC
from charcodes import KEY_NFC, KEY_QR
@pytest.fixture
@ -571,9 +571,8 @@ def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home
@pytest.mark.parametrize("acct_num", [None, 0, 1, (2 ** 31) - 1])
@pytest.mark.parametrize("int_ext", [True, False])
def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, settings_set, need_keypress,
pick_menu_item, way, cap_story, cap_menu, nfc_read_text, int_ext,
microsd_path, settings_get, virtdisk_path, load_export, press_select,
mk4_qr_not_allowed):
pick_menu_item, way, cap_story, cap_menu, int_ext, settings_get,
virtdisk_path, load_export, press_select, mk4_qr_not_allowed):
mk4_qr_not_allowed(way)
settings_set('chain', chain)
@ -585,7 +584,6 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, setting
time.sleep(.1)
_, story = cap_story()
assert "This saves a ranged xpub descriptor" in story
assert "Choose descriptor and address type for the wallet on next screens" in story
assert "Press (1) to enter a non-zero account number" in story
assert "sensitive--in terms of privacy" in story
assert "not compromise your funds directly" in story
@ -646,6 +644,89 @@ def test_generic_descriptor_export(chain, addr_fmt, acct_num, goto_home, setting
assert xpub_target in xpub
@pytest.mark.parametrize("chain", ["BTC", "XTN"])
@pytest.mark.parametrize("way", ["nfc", "qr"])
@pytest.mark.parametrize("addr_fmt", [AF_P2WPKH, AF_P2WPKH_P2SH])
@pytest.mark.parametrize("acct_num", [None, 55])
def test_zeus_descriptor_export(addr_fmt, acct_num, goto_home, need_keypress, pick_menu_item,
way, cap_story, cap_menu, nfc_read_text, settings_get, chain,
virtdisk_path, load_export, press_select, mk4_qr_not_allowed,
settings_set, is_q1, press_cancel, cap_screen_qr, press_nfc):
mk4_qr_not_allowed(way)
settings_set('chain', chain)
chain_num = 1 if chain == "XTN" else 0
goto_home()
pick_menu_item("Advanced/Tools")
pick_menu_item("Export Wallet")
pick_menu_item("Zeus")
time.sleep(.1)
title, story = cap_story()
assert "This saves a ranged xpub descriptor" in story
assert "Press (1) to enter a non-zero account number" in story
assert "sensitive--in terms of privacy" in story
assert "not compromise your funds directly" in story
if isinstance(acct_num, int):
need_keypress("1") # chosse account number
for ch in str(acct_num):
need_keypress(ch) # input num
press_select() # confirm selection
else:
press_select() # confirm story
time.sleep(.1)
menu = cap_menu()
assert len(menu) == 2
if addr_fmt == AF_P2WPKH:
menu_item = "Segwit P2WPKH"
desc_prefix = "wpkh("
bip44_purpose = 84
else:
assert addr_fmt == AF_P2WPKH_P2SH
menu_item = "P2SH-Segwit"
desc_prefix = "sh(wpkh("
bip44_purpose = 49
assert menu_item in menu
pick_menu_item(menu_item)
time.sleep(.1)
title, story = cap_story()
if way == "qr":
assert ("%s to show QR" % (KEY_QR if is_q1 else "(4)")) in story
need_keypress(KEY_QR if is_q1 else "4")
time.sleep(.2)
contents = cap_screen_qr().decode('ascii')
else:
assert ("ress %s to share via NFC" % (KEY_NFC if is_q1 else "(3)")) in story
press_nfc()
time.sleep(.2)
contents = nfc_read_text()
time.sleep(.5)
press_cancel() # exit NFC animation
descriptor = contents.strip()
assert descriptor.startswith(desc_prefix)
desc_obj = Descriptor.parse(descriptor)
assert desc_obj.serialize(int_ext=True) == descriptor
assert desc_obj.addr_fmt == addr_fmt
assert len(desc_obj.keys) == 1
xfp, derive, xpub = desc_obj.keys[0]
assert xfp == settings_get("xfp")
assert derive == f"m/{bip44_purpose}h/{chain_num}h/{acct_num if acct_num is not None else 0}h"
seed = Mnemonic.to_seed(simulator_fixed_words)
node = BIP32Node.from_master_secret(
seed, netcode="BTC" if chain == "BTC" else "XTN"
).subkey_for_path(derive[2:].replace("h", "H"))
xpub_target = node.hwif()
assert xpub_target in xpub
@pytest.mark.parametrize("chain", ["BTC", "XTN", "XRT"])
@pytest.mark.parametrize("account", ["Postmix", "Premix"])
def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_home,
@ -680,7 +761,6 @@ def test_samourai_vs_generic(chain, account, settings_set, pick_menu_item, goto_
_, story = cap_story()
assert "This saves a ranged xpub descriptor" in story
assert in_story in story
assert "Choose an address type for the wallet on the next screen" not in story # NOT
assert "Press 1 to enter a non-zero account number" not in story # NOT
assert "sensitive--in terms of privacy" in story
assert "not compromise your funds directly" in story