optional full CSV export, default off

This commit is contained in:
scgbckbone 2025-07-23 11:24:03 +02:00
parent 54ccfe6cd6
commit 754c4c3f64
4 changed files with 39 additions and 25 deletions

View File

@ -7,7 +7,7 @@
import chains, stash, version
from ux import ux_show_story, the_ux, ux_enter_bip32_index
from ux import export_prompt_builder, import_export_prompt_decode
from menu import MenuSystem, MenuItem
from menu import MenuSystem, MenuItem, ToggleMenuItem
from public_constants import AFC_BECH32, AFC_BECH32M, AF_P2WPKH, AF_P2TR, AF_CLASSIC
from wallet import MiniScriptWallet
from uasyncio import sleep_ms
@ -199,8 +199,14 @@ class AddressListMenu(MenuSystem):
items.append(MenuItem("Custom Path", menu=self.make_custom))
# if they have miniscript wallets, add those next
for msc in MiniScriptWallet.iter_wallets():
items.append(MenuItem(msc.name, f=self.pick_miniscript, arg=msc))
if MiniScriptWallet.exists():
items.append(ToggleMenuItem('MS Scripts/Derivs', 'aemscsv',
['Default Off', 'Enable'], story=(
"Enable this option to add script(s) and derivations to the CSV export"
" of Miniscript wallets. Default is to only export addresses.")))
for msc in MiniScriptWallet.iter_wallets():
items.append(MenuItem(msc.name, f=self.pick_miniscript, arg=msc))
else:
items.append(MenuItem("Account: %d" % self.account_num, f=self.change_account))

View File

@ -18,6 +18,7 @@ class Tapscript:
def __init__(self, tree):
self.tree = tree # miniscript or (tapscript, tapscript)
self._merkle_root = None
self._processed_tree = None
def iter_leaves(self):
if isinstance(self.tree, Miniscript):
@ -29,8 +30,7 @@ class Tapscript:
@property
def merkle_root(self):
if not self._merkle_root:
_, mr = self.process_tree()
self._merkle_root = mr
self._processed_tree, self._merkle_root = self.process_tree()
return self._merkle_root
def derive(self, idx, key_map, change=False):
@ -60,6 +60,14 @@ class Tapscript:
h = ngu.hash.sha256t(TAP_BRANCH_H, left_h + right_h, True)
return left + right, h
# UNUSED - using above proces tree cached result to dump scripts to CSV
# def script_tree(self):
# if isinstance(self.tree, Miniscript):
# return b2a_hex(chains.tapscript_serialize(self.tree.compile())).decode()
#
# l, r = self.tree
# return "{" + l.script_tree() + "," +r.script_tree() + "}"
@classmethod
def read_from(cls, s):
c = s.read(1)
@ -82,13 +90,6 @@ class Tapscript:
ms = Miniscript.read_from(s, taproot=True)
return cls(ms)
def script_tree(self):
if isinstance(self.tree, Miniscript):
return b2a_hex(chains.tapscript_serialize(self.tree.compile())).decode()
l, r = self.tree
return "{" + l.script_tree() + "," +r.script_tree() + "}"
def to_string(self, external=True, internal=True):
if isinstance(self.tree, Miniscript):
return self.tree.to_string(external, internal)

View File

@ -66,6 +66,7 @@ from utils import call_later_ms
# hmx = (bool) Force display of current XFP in home menu, even w/o tmp seed active
# ccc = (complex) If present, CCC feature is enabled and key details stored here.
# ktrx = (privkey) Key teleport Rx has been started, this will be our keypair
# aemscsv = (bool) opt-in enable more verbose CSV output for miniscript wallets with Derivations and Scripts
# Stored w/ key=00 for access before login
# _skip_pin = hard code a PIN value (dangerous, only for debug)

View File

@ -136,7 +136,6 @@ class MasterSingleSigWallet(WalletABC):
return self._path + '/%d/%d' % (change_idx, idx)
def to_descriptor(self):
from glob import settings
from descriptor import Descriptor, Key
xfp = settings.get('xfp')
xpub = settings.get('xpub')
@ -597,10 +596,15 @@ class MiniScriptWallet(WalletABC):
addr = ch.render_address(d.script_pubkey(compiled_scr=scr))
ders = script = None
if scripts:
# maybe key.origin.to_string() ??
ders = ["[%s]" % str(k.origin) for k in d.keys]
ders = ""
for k in d.keys:
ders += "[%s]; " % str(k.origin)
if d.tapscript:
script = d.tapscript.script_tree()
# DFS ordered list of scripts
script = ""
for leaf_ver, scr, _ in d.tapscript._processed_tree:
script += b2a_hex(chains.tapscript_serialize(scr, leaf_ver)).decode() + "; "
else:
script = b2a_hex(ser_string(scr)).decode()
@ -614,7 +618,7 @@ class MiniScriptWallet(WalletABC):
addrs = []
for idx, addr, *_ in self.yield_addresses(start, n, change, scripts=False):
for idx, addr, *_ in self.yield_addresses(start, n, change):
msg += '.../%d =>\n' % idx # just idx, if derivations or scripts needed - export csv
addrs.append(addr)
msg += show_single_address(addr) + '\n\n'
@ -623,15 +627,17 @@ class MiniScriptWallet(WalletABC):
return msg, addrs
def generate_address_csv(self, start, n, change):
yield '"' + '","'.join(
['Index', 'Payment Address']
) + '"\n'
for idx, addr, ders, script in self.yield_addresses(start, n, change):
scripts = settings.get("aemscsv", False)
header = ['Index', 'Payment Address']
if scripts:
header += ['Script', 'Derivations']
yield '"' + '","'.join(header) + '"\n'
for idx, addr, ders, script in self.yield_addresses(start, n, change, scripts=scripts):
ln = '%d,"%s"' % (idx, addr)
if ders:
ln += ',"%s","' % script
ln += '","'.join(ders)
ln += '"'
if scripts:
ln += ',"%s"' % script
ln += ',"%s"' % ders
ln += '\n'
yield ln