Skip to content

Commit 02afbf4

Browse files
integrate taptree (#264)
1 parent f8fad4b commit 02afbf4

File tree

11 files changed

+92
-25
lines changed

11 files changed

+92
-25
lines changed

src/apps/wallets/manager.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from .wallet import WalletError, Wallet
1414
from .commands import DELETE, EDIT
1515
from io import BytesIO
16-
from bcur import bcur_decode_stream, bcur_encode_stream
16+
from bcur import bcur_decode_stream
1717
from helpers import a2b_base64_stream, b2a_base64_stream
1818
import gc
1919
import json
@@ -439,7 +439,7 @@ async def confirm_new_wallet(self, w, show_screen):
439439
"None of the keys belong to the device.\n\n"
440440
"Are you sure you still want to add the wallet?")):
441441
return False
442-
return await show_screen(ConfirmWalletScreen(w.name, w.full_policy, keys, w.is_miniscript))
442+
return await show_screen(ConfirmWalletScreen(w.name, w.full_policy, keys, w.is_complex))
443443

444444
async def showaddr(
445445
self, paths: list, script_type: str, redeem_script=None, show_screen=None
@@ -792,6 +792,7 @@ def sign_psbtview(self, psbtv, out_stream, wallets, sighash):
792792
with open(self.tempdir+"/sigs", "rb") as sig_stream:
793793
psbtv.write_to(out_stream, compress=CompressMode.PARTIAL, extra_input_streams=[sig_stream])
794794

795+
795796
def wipe(self):
796797
"""Deletes all wallets info"""
797798
self.wallets = []

src/apps/wallets/screens.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import lvgl as lv
2-
from gui.common import add_label, add_button, HOR_RES, format_addr, PADDING
2+
from gui.common import add_label, add_button, HOR_RES, format_addr
33
from gui.decorators import on_release
44
from gui.screens import QRAlert, Prompt, Alert
55
from .commands import DELETE, EDIT, MENU
@@ -123,10 +123,10 @@ def update_address(self):
123123

124124

125125
class ConfirmWalletScreen(Prompt):
126-
def __init__(self, name, policy, keys, is_miniscript=True):
126+
def __init__(self, name, policy, keys, is_complex=True):
127127
super().__init__('Add wallet "%s"?' % name, "")
128128
self.policy = add_label("Policy: " + policy, y=75, scr=self)
129-
self.is_miniscript = is_miniscript
129+
self.is_complex = is_complex
130130

131131
lbl = lv.label(self)
132132
lbl.set_text("Canonical xpub SLIP-132 ")
@@ -145,10 +145,12 @@ def fill_message(self):
145145
msg = ""
146146
arg = "slip132" if self.slip_switch.get_state() else "canonical"
147147
for i, k in enumerate(self.keys):
148-
alias = "" if not self.is_miniscript else " (%s)" % chr(65+i)
148+
alias = "" if not self.is_complex else " (%s)" % chr(65+i)
149149
kstr = str(k[arg]).replace("]","]\n")
150150
if k["mine"]:
151151
msg += "#7ED321 My key%s: #\n%s\n\n" % (alias, kstr)
152+
elif k["is_nums"]:
153+
msg += "#00CAF1 NUMS key%s: #\nNobody knows private key\n\n" % alias
152154
elif k["is_private"]:
153155
msg += "#F51E2D Private key%s: #\n%s\n\n" % (alias, kstr)
154156
else:
@@ -157,10 +159,10 @@ def fill_message(self):
157159

158160
# TODO: refactor to remove duplication
159161
class WalletInfoScreen(Alert):
160-
def __init__(self, name, policy, keys, is_miniscript=True):
162+
def __init__(self, name, policy, keys, is_complex=True):
161163
super().__init__(name, "")
162164
self.policy = add_label("Policy: " + policy, y=75, scr=self)
163-
self.is_miniscript = is_miniscript
165+
self.is_complex = is_complex
164166

165167
lbl = lv.label(self)
166168
lbl.set_text("Canonical xpub SLIP-132 ")
@@ -179,10 +181,12 @@ def fill_message(self):
179181
msg = ""
180182
arg = "slip132" if self.slip_switch.get_state() else "canonical"
181183
for i, k in enumerate(self.keys):
182-
alias = "" if not self.is_miniscript else " (%s)" % chr(65+i)
184+
alias = "" if not self.is_complex else " (%s)" % chr(65+i)
183185
kstr = str(k[arg]).replace("]","]\n")
184186
if k["mine"]:
185187
msg += "#7ED321 My key%s: #\n%s\n\n" % (alias, kstr)
188+
elif k["is_nums"]:
189+
msg += "#AAAAAA NUMS key%s: #\nNobody knows private key\n\n" % alias
186190
elif k["is_private"]:
187191
msg += "#F51E2D Private key%s: #\n%s\n\n" % (alias, kstr)
188192
else:

src/apps/wallets/wallet.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
import platform
33
from platform import maybe_mkdir, delete_recursively
44
import json
5-
from embit import ec, hashes, script
5+
from embit import ec, hashes
66
from embit.networks import NETWORKS
77
from embit.psbt import DerivationPath
88
from embit.descriptor import Descriptor
99
from embit.descriptor.checksum import add_checksum
1010
from embit.descriptor.arguments import AllowedDerivation
1111
from embit.transaction import SIGHASH
12-
import hashlib
1312
from .screens import WalletScreen, WalletInfoScreen
1413
from .commands import DELETE, EDIT, MENU, INFO, EXPORT
1514
from gui.screens import Menu, QRAlert, Alert
@@ -57,7 +56,7 @@ async def show(self, network, show_screen):
5756
keys = self.get_key_dicts(network)
5857
for k in keys:
5958
k["mine"] = True if self.keystore and self.keystore.owns(k["key"]) else False
60-
await show_screen(WalletInfoScreen(self.name, self.full_policy, keys, self.is_miniscript))
59+
await show_screen(WalletInfoScreen(self.name, self.full_policy, keys, self.is_complex))
6160
continue
6261
elif cmd == EXPORT:
6362
await self.export_menu(show_screen)
@@ -252,6 +251,7 @@ def get_key_dicts(self, network):
252251
k["slip132"] = k["key"].to_string(self.Networks[network][ver])
253252
ver = canonical_ver.replace("pub", "prv") if k["is_private"] else canonical_ver
254253
k["canonical"] = k["key"].to_string(self.Networks[network][ver])
254+
k["is_nums"] = (k["key"].sec() == ec.NUMS_PUBKEY.sec()) # nothing up my sleeve
255255
return keys
256256

257257
def sign_psbt(self, psbt, sighash=SIGHASH.ALL):
@@ -348,16 +348,25 @@ def full_policy(self):
348348
else:
349349
p = "Legacy\n"
350350
pp = self.descriptor.full_policy
351-
if not self.is_miniscript:
351+
if not self.is_complex:
352352
p += pp
353353
else:
354-
p += "Miniscript:\n"+pp.replace(",",", ")
354+
prefix = "Miniscript:\n" if self.is_miniscript else "\n"
355+
p += prefix+pp.replace(",",", ")
355356
return p
356357

357358
@property
358359
def is_miniscript(self):
359360
return not (self.descriptor.is_basic_multisig or self.descriptor.is_pkh or self.descriptor.is_taproot)
360361

362+
@property
363+
def is_taptree(self):
364+
return bool(self.descriptor.taptree)
365+
366+
@property
367+
def is_complex(self):
368+
return self.is_miniscript or self.is_taptree
369+
361370
def __str__(self):
362371
return "%s&%s" % (self.name, self.descriptor)
363372

test/integration/tests/test_with_rpc.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
from unittest import TestCase, skip
1+
from unittest import TestCase
22
from util.controller import sim
33
from util.rpc import prepare_rpc
44
import random
5-
import time
65
from embit.descriptor import Descriptor
7-
from embit.bip32 import HDKey
86
from embit.networks import NETWORKS
97
from embit.psbt import PSBT, DerivationPath
10-
from embit.transaction import Transaction, TransactionInput, TransactionOutput, SIGHASH
8+
from embit.transaction import Transaction, SIGHASH
119
from embit import ec, bip32
1210
from embit.script import Witness
1311

test/integration/util/__init__.py

Whitespace-only changes.

test/integration/util/misc.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from embit.descriptor import Descriptor
2+
from embit.networks import NETWORKS
3+
from .rpc import prepare_rpc
4+
5+
def create_wallet(wname, d1: str, d2: str, rpc=None):
6+
if rpc is None:
7+
rpc = prepare_rpc()
8+
wdefault = rpc.wallet("")
9+
# to derive addresses
10+
desc1 = Descriptor.from_string(d1)
11+
12+
# recv addr
13+
addr = desc1.derive(0).address(NETWORKS['regtest'])
14+
15+
# to add checksums
16+
d1 = rpc.getdescriptorinfo(d1)["descriptor"]
17+
d2 = rpc.getdescriptorinfo(d2)["descriptor"]
18+
rpc.createwallet(wname, True, True)
19+
w = rpc.wallet(wname)
20+
info = w.getwalletinfo()
21+
# bitcoin core uses descriptor wallets by default so importmulti may fail
22+
use_descriptors = info.get("descriptors", False)
23+
if not use_descriptors:
24+
res = w.importmulti([{
25+
"desc": d1,
26+
"internal": False,
27+
"timestamp": "now",
28+
"watchonly": True,
29+
"range": 10,
30+
},{
31+
"desc": d2,
32+
"internal": True,
33+
"timestamp": "now",
34+
"watchonly": True,
35+
"range": 10,
36+
}],{"rescan": False})
37+
else:
38+
res = w.importdescriptors([{
39+
"desc": d1,
40+
"internal": False,
41+
"timestamp": "now",
42+
"watchonly": True,
43+
"active": True,
44+
},{
45+
"desc": d2,
46+
"internal": True,
47+
"timestamp": "now",
48+
"watchonly": True,
49+
"active": True,
50+
}])
51+
assert all([k["success"] for k in res])
52+
wdefault.sendtoaddress(addr, 0.1)
53+
rpc.mine()
54+
return w

test/tests/test_compatibility.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from unittest import TestCase
2-
from apps.compatibility import *
2+
from apps.compatibility import parse_software_wallet_json, parse_cc_wallet_txt
33
import json
44
from io import BytesIO
55

test/tests/test_keystore.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from unittest import TestCase
22
from keystore import FlashKeyStore
3-
import os, json
3+
import os
44
import platform
55

66
TEST_DIR = "testdir"

test/tests/test_wallets.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
from unittest import TestCase
22
from apps.wallets.wallet import Wallet
33
from embit.descriptor import Key
4-
import os, json
5-
import platform
64

75
TEST_DIR = "testdir"
86

@@ -60,3 +58,7 @@ def test_invalid_keys(self):
6058
Key.parse(k)
6159
print(k)
6260

61+
def test_taptree(self):
62+
d = "tr([73c5da0a/2/2/2]tpubDCPwGho2toLmdSELZ3o8v1D6RUUK7Y5keCjMyrSfE75aX2Mcx4MNEM6MnXDZR87GQ1ot4YNn2GGtiN5SvM12c6cvYMrt6avwtYNcRab2HFv/<0;1>/*,or_b(pk([73c5da0a/1/2/3]tpubDCpEkdSHkygNaquCRtW8Fuo3TchAXFSWUuYB9aryim58T4CWM9vLgt26uUV5wdtuvbSk7rWmQQCpcYhGjbHiBzWCYXeyRMJ98zSBWekaJJm/<0;1>/*),s:pk([73c5da0a/3/2/1]tpubDDrLDbxjL1d5FK8djVqUjD3xL1gkhaTXTL1rHzEavwA2ss4YpF8Qm82cKN89PEBRYk6JVTZULA872LuFGENTGdNYASDCrXKKZkU86A8HLqA/<0;1>/*)))"
63+
w = Wallet.parse(d)
64+
print(w)

0 commit comments

Comments
 (0)