Skip to content

Commit 671ea4e

Browse files
committed
fixup! feat(core): add N1W1 backup/recovery demo flows
1 parent cb7ae9f commit 671ea4e

6 files changed

Lines changed: 153 additions & 43 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pytest
2+
3+
from trezorlib.debuglink import DebugSession as Session
4+
from trezorlib.messages import BackupMethod, Capability
5+
6+
7+
8+
def check_support(session: Session, method: BackupMethod) -> None:
9+
if (
10+
method is BackupMethod.N4W1
11+
and Capability.N4W1 not in session.features.capabilities
12+
):
13+
pytest.skip("N4W1 not supported")

tests/device_tests/reset_recovery/test_recovery_slip39_basic.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from trezorlib import device, exceptions, messages
2020
from trezorlib.debuglink import DebugSession as Session
2121

22+
from . import check_support
2223
from ...common import (
2324
MNEMONIC_SLIP39_BASIC_20_3of6,
2425
MNEMONIC_SLIP39_BASIC_20_3of6_SECRET,
@@ -75,9 +76,9 @@ def test_secret(
7576
session: Session, shares: list[str], secret: str, backup_type: messages.BackupType
7677
):
7778
with session.test_ctx as client:
78-
IF = InputFlowSlip39BasicRecovery(session, shares)
79+
IF = InputFlowSlip39BasicRecovery(session, shares, method=messages.BackupMethod.N4W1)
7980
client.set_input_flow(IF.get())
80-
device.recover(session, pin_protection=False, label="label")
81+
device.recover(session, pin_protection=False, label="label", backup_method=messages.BackupMethod.N4W1)
8182

8283
# Workflow successfully ended
8384
assert session.features.pin_protection is False

tests/device_tests/reset_recovery/test_reset_recovery_slip39_basic.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,32 @@
2222
from trezorlib import btc, device, messages
2323
from trezorlib.debuglink import DebugSession as Session
2424
from trezorlib.debuglink import TrezorTestContext as Client
25-
from trezorlib.messages import BackupType
25+
from trezorlib.messages import BackupMethod, BackupType
2626
from trezorlib.tools import parse_path
2727

28+
from . import check_support
2829
from ...common import MOCK_GET_ENTROPY
2930
from ...input_flows import (
3031
InputFlowSlip39BasicRecovery,
3132
InputFlowSlip39BasicResetRecovery,
3233
)
3334
from ...translations import set_language
3435

36+
METHODS = [
37+
BackupMethod.Display,
38+
BackupMethod.N4W1,
39+
]
40+
41+
TEST_PARAMS = [pytest.param(method, id=method.name) for method in METHODS]
42+
3543

3644
@pytest.mark.models("core")
3745
@pytest.mark.setup_client(uninitialized=True)
38-
def test_reset_recovery(client: Client):
46+
@pytest.mark.parametrize("method", TEST_PARAMS)
47+
def test_reset_recovery(client: Client, method: BackupMethod):
3948
session = client.get_seedless_session()
49+
check_support(session, method)
50+
4051
mnemonics = reset(session)
4152
session = client.get_session()
4253
address_before = btc.get_address(session, "Bitcoin", parse_path("m/44h/0h/0h/0/0"))
@@ -48,7 +59,7 @@ def test_reset_recovery(client: Client):
4859
session = client.get_seedless_session()
4960
set_language(session, lang[:2])
5061
selected_mnemonics = share_subset
51-
recover(session, selected_mnemonics)
62+
recover(session, selected_mnemonics, method)
5263
session = client.get_session()
5364
address_after = btc.get_address(
5465
session, "Bitcoin", parse_path("m/44h/0h/0h/0/0")
@@ -85,11 +96,13 @@ def reset(session: Session, strength: int = 128) -> list[str]:
8596
return IF.mnemonics
8697

8798

88-
def recover(session: Session, shares: t.Sequence[str]):
99+
def recover(session: Session, shares: t.Sequence[str], method: BackupMethod):
89100
with session.test_ctx as client:
90-
IF = InputFlowSlip39BasicRecovery(session, shares)
101+
IF = InputFlowSlip39BasicRecovery(session, shares, method=method)
91102
client.set_input_flow(IF.get())
92-
device.recover(session, pin_protection=False, label="label")
103+
device.recover(
104+
session, pin_protection=False, label="label", backup_method=method
105+
)
93106

94107
# Workflow successfully ended
95108
assert session.features.pin_protection is False

tests/device_tests/reset_recovery/test_reset_slip39_basic.py

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# You should have received a copy of the License along with this library.
1515
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
1616

17-
from itertools import combinations
17+
from itertools import combinations, product
1818

1919
import pytest
2020
from shamir_mnemonic import MnemonicError, shamir
@@ -23,8 +23,9 @@
2323
from trezorlib.btc import get_public_node
2424
from trezorlib.debuglink import DebugSession as Session
2525
from trezorlib.exceptions import TrezorFailure
26-
from trezorlib.messages import BackupAvailability, BackupType
26+
from trezorlib.messages import BackupAvailability, BackupMethod, BackupType
2727

28+
from . import check_support
2829
from ...common import EXTERNAL_ENTROPY, MOCK_GET_ENTROPY, generate_entropy
2930
from ...input_flows import (
3031
FlowAdapter,
@@ -40,12 +41,21 @@
4041
try_to_cancel({"backup_device", "setup_device", "success_backup"}),
4142
]
4243

44+
METHODS = [
45+
BackupMethod.Display,
46+
BackupMethod.N4W1,
47+
]
48+
49+
50+
def reset_device(
51+
session: Session, strength: int, adapt_flow: FlowAdapter, method: BackupMethod
52+
):
53+
check_support(session, method)
4354

44-
def reset_device(session: Session, strength: int, adapt_flow: FlowAdapter):
4555
member_threshold = 3
4656

4757
with session.test_ctx as client:
48-
IF = InputFlowSlip39BasicResetRecovery(session)
58+
IF = InputFlowSlip39BasicResetRecovery(session, method)
4959
client.set_input_flow(adapt_flow(session, IF.get()))
5060

5161
# No PIN, no passphrase, don't display random
@@ -56,6 +66,7 @@ def reset_device(session: Session, strength: int, adapt_flow: FlowAdapter):
5666
pin_protection=False,
5767
label="test",
5868
backup_type=BackupType.Slip39_Basic,
69+
backup_method=method,
5970
entropy_check_count=0,
6071
_get_entropy=MOCK_GET_ENTROPY,
6172
)
@@ -80,27 +91,41 @@ def reset_device(session: Session, strength: int, adapt_flow: FlowAdapter):
8091
device.backup(session)
8192

8293

94+
TEST_PARAMS = [
95+
pytest.param(adapt_flow, method, id=f"{adapt_flow.__name__}-{method.name}")
96+
for adapt_flow, method in product(FLOW_ADAPTERS, METHODS)
97+
]
98+
99+
83100
@pytest.mark.setup_client(uninitialized=True)
84-
@pytest.mark.parametrize("adapt_flow", FLOW_ADAPTERS, ids=lambda f: f.__name__)
85-
def test_reset_device_slip39_basic(session: Session, adapt_flow: FlowAdapter):
86-
reset_device(session, 128, adapt_flow)
101+
@pytest.mark.parametrize("adapt_flow,method", TEST_PARAMS)
102+
def test_reset_device_slip39_basic(
103+
session: Session, adapt_flow: FlowAdapter, method: BackupMethod
104+
):
105+
reset_device(session, 128, adapt_flow, method=method)
87106

88107

89-
@pytest.mark.parametrize("adapt_flow", FLOW_ADAPTERS, ids=lambda f: f.__name__)
108+
@pytest.mark.parametrize("adapt_flow,method", TEST_PARAMS)
90109
@pytest.mark.setup_client(uninitialized=True)
91-
def test_reset_device_slip39_basic_256(session: Session, adapt_flow: FlowAdapter):
92-
reset_device(session, 256, adapt_flow)
110+
def test_reset_device_slip39_basic_256(
111+
session: Session, adapt_flow: FlowAdapter, method: BackupMethod
112+
):
113+
reset_device(session, 256, adapt_flow, method=method)
93114

94115

95116
@pytest.mark.setup_client(uninitialized=True)
96-
@pytest.mark.parametrize("adapt_flow", FLOW_ADAPTERS, ids=lambda f: f.__name__)
97-
def test_reset_entropy_check(session: Session, adapt_flow: FlowAdapter):
117+
@pytest.mark.parametrize("adapt_flow,method", TEST_PARAMS)
118+
def test_reset_entropy_check(
119+
session: Session, adapt_flow: FlowAdapter, method: BackupMethod
120+
):
121+
check_support(session, method)
122+
98123
member_threshold = 3
99124

100125
strength = 128 # 20 words
101126

102127
with session.test_ctx as client:
103-
IF = InputFlowSlip39BasicResetRecovery(session)
128+
IF = InputFlowSlip39BasicResetRecovery(session, method)
104129
client.set_input_flow(adapt_flow(session, IF.get()))
105130

106131
# No PIN, no passphrase.
@@ -111,6 +136,7 @@ def test_reset_entropy_check(session: Session, adapt_flow: FlowAdapter):
111136
pin_protection=False,
112137
label="test",
113138
backup_type=BackupType.Slip39_Basic,
139+
backup_method=method,
114140
entropy_check_count=3,
115141
_get_entropy=MOCK_GET_ENTROPY,
116142
)

tests/input_flows.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@
3434
read_and_confirm_mnemonic,
3535
swipe_if_necessary,
3636
)
37-
from .input_flows_helpers import BackupFlow, EthereumFlow, PinFlow, RecoveryFlow
37+
from .input_flows_helpers import (
38+
BackupFlow,
39+
EthereumFlow,
40+
PinFlow,
41+
RecoveryFlow,
42+
n4w1_handle_write,
43+
)
3844

3945
B = messages.ButtonRequestType
4046

@@ -1958,18 +1964,24 @@ def input_flow_common(self) -> BRGeneratorType:
19581964
def load_N_shares(
19591965
debug: DebugLink,
19601966
n: int,
1967+
method: messages.BackupMethod = messages.BackupMethod.Display,
19611968
) -> Generator[None, "messages.ButtonRequest", list[str]]:
19621969
mnemonics: list[str] = []
19631970

19641971
for _ in range(n):
1965-
# Phrase screen
1966-
mnemonic = yield from read_and_confirm_mnemonic(debug)
1967-
assert mnemonic is not None
1968-
mnemonics.append(mnemonic)
1969-
1970-
br = yield # Confirm continue to next
1971-
assert br.code == B.Success
1972-
debug.press_yes()
1972+
if method is messages.BackupMethod.Display:
1973+
# Phrase screen
1974+
mnemonic = yield from read_and_confirm_mnemonic(debug)
1975+
assert mnemonic is not None
1976+
mnemonics.append(mnemonic)
1977+
br = yield # Confirm continue to next
1978+
assert br.code == B.Success
1979+
debug.press_yes()
1980+
elif method is messages.BackupMethod.N4W1:
1981+
assert (yield).name == "backup_write"
1982+
mnemonics.append(n4w1_handle_write(debug).decode())
1983+
else:
1984+
raise RuntimeError
19731985

19741986
return mnemonics
19751987

@@ -2114,11 +2126,17 @@ def input_flow_eckhart(self) -> BRGeneratorType:
21142126

21152127

21162128
class InputFlowSlip39BasicResetRecovery(InputFlowBase):
2117-
def __init__(self, client: Client | DebugSession):
2129+
def __init__(
2130+
self,
2131+
client: Client | DebugSession,
2132+
method: messages.BackupMethod = messages.BackupMethod.Display,
2133+
):
21182134
super().__init__(client)
21192135
self.mnemonics: list[str] = []
2136+
self.method = method
21202137

21212138
def input_flow_bolt(self) -> BRGeneratorType:
2139+
assert self.method is messages.BackupMethod.Display
21222140
# 1. Confirm Reset
21232141
# 2. Backup your seed
21242142
# 3. Backup intro
@@ -2138,6 +2156,7 @@ def input_flow_bolt(self) -> BRGeneratorType:
21382156
self.debug.press_yes()
21392157

21402158
def input_flow_caesar(self) -> BRGeneratorType:
2159+
assert self.method is messages.BackupMethod.Display
21412160
yield # Confirm Reset
21422161
self.debug.press_yes()
21432162
yield # Backup your seed
@@ -2169,6 +2188,7 @@ def input_flow_caesar(self) -> BRGeneratorType:
21692188
self.debug.press_yes()
21702189

21712190
def input_flow_delizia(self) -> BRGeneratorType:
2191+
assert self.method is messages.BackupMethod.Display
21722192
# 1. Confirm Reset
21732193
# 2. Wallet Created
21742194
# 3. Backup your seed
@@ -2189,6 +2209,10 @@ def input_flow_delizia(self) -> BRGeneratorType:
21892209
self.debug.press_yes()
21902210

21912211
def input_flow_eckhart(self) -> BRGeneratorType:
2212+
num_screens = {
2213+
messages.BackupMethod.Display: 10,
2214+
messages.BackupMethod.N4W1: 8,
2215+
}[self.method]
21922216
# 1. Confirm Reset
21932217
# 2. Wallet Created
21942218
# 3. Backup your seed
@@ -2199,10 +2223,10 @@ def input_flow_eckhart(self) -> BRGeneratorType:
21992223
# 8. Confirm show seeds
22002224
# 9. Warning
22012225
# 10. Instructions
2202-
yield from click_through(self.debug, screens=10, code=B.ResetDevice)
2226+
yield from click_through(self.debug, screens=num_screens, code=B.ResetDevice)
22032227

22042228
# Mnemonic phrases
2205-
self.mnemonics = yield from load_N_shares(self.debug, 5)
2229+
self.mnemonics = yield from load_N_shares(self.debug, 5, self.method)
22062230

22072231
br = yield # success screen
22082232
assert br.code == B.Success
@@ -2800,18 +2824,22 @@ def __init__(
28002824
client: Client | DebugSession,
28012825
shares: Sequence[str],
28022826
pin: str | None = None,
2827+
method: messages.BackupMethod = messages.BackupMethod.Display,
28032828
):
28042829
super().__init__(client)
28052830
self.shares = shares
28062831
self.pin = pin
28072832
self.word_count = len(shares[0].split(" "))
2833+
self.method = method
28082834

28092835
def input_flow_common(self) -> BRGeneratorType:
28102836
yield from self.REC.confirm_recovery()
28112837
if self.pin is not None:
28122838
yield from self.PIN.setup_new_pin(self.pin)
2813-
yield from self.REC.setup_slip39_recovery(self.word_count)
2814-
yield from self.REC.input_all_slip39_shares(self.shares)
2839+
if self.method is messages.BackupMethod.Display:
2840+
yield from self.REC.setup_slip39_recovery(self.word_count)
2841+
2842+
yield from self.REC.input_all_slip39_shares(self.shares, method=self.method)
28152843
yield from self.REC.success_wallet_recovered()
28162844

28172845

0 commit comments

Comments
 (0)