parent
a4f8d2b877
commit
1e6ed37632
@ -0,0 +1 @@
|
||||
Added support for repeated backups.
|
@ -0,0 +1,7 @@
|
||||
# Automatically generated by pb2py
|
||||
# fmt: off
|
||||
# isort:skip_file
|
||||
|
||||
NormalRecovery = 0
|
||||
DryRun = 1
|
||||
UnlockRepeatedBackup = 2
|
@ -0,0 +1,208 @@
|
||||
# This file is part of the Trezor project.
|
||||
#
|
||||
# Copyright (C) 2012-2024 SatoshiLabs and contributors
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License version 3
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the License along with this library.
|
||||
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import pytest
|
||||
|
||||
from trezorlib import device, messages
|
||||
|
||||
from .. import buttons
|
||||
from ..common import WITH_MOCK_URANDOM
|
||||
from . import recovery, reset
|
||||
from .common import go_next
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..device_handler import BackgroundDeviceHandler
|
||||
|
||||
|
||||
pytestmark = [pytest.mark.skip_t1b1]
|
||||
|
||||
|
||||
@pytest.mark.setup_client(uninitialized=True)
|
||||
@WITH_MOCK_URANDOM
|
||||
def test_repeated_backup(
|
||||
device_handler: "BackgroundDeviceHandler",
|
||||
):
|
||||
features = device_handler.features()
|
||||
debug = device_handler.debuglink()
|
||||
|
||||
assert features.initialized is False
|
||||
|
||||
device_handler.run(
|
||||
device.reset,
|
||||
strength=128,
|
||||
backup_type=messages.BackupType.Slip39_Basic,
|
||||
pin_protection=False,
|
||||
)
|
||||
|
||||
# confirm new wallet
|
||||
reset.confirm_new_wallet(debug)
|
||||
|
||||
# confirm back up
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# confirm backup warning
|
||||
reset.confirm_read(debug, middle_r=True)
|
||||
|
||||
# let's make a 1-of-1 backup to start with...
|
||||
|
||||
# shares=1
|
||||
reset.set_selection(debug, buttons.RESET_MINUS, 5 - 1)
|
||||
|
||||
# confirm checklist
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# threshold=1
|
||||
reset.set_selection(debug, buttons.RESET_PLUS, 0)
|
||||
|
||||
# confirm checklist
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# confirm backup warning
|
||||
reset.confirm_read(debug, middle_r=True)
|
||||
|
||||
# read words
|
||||
initial_backup_1_of_1 = reset.read_words(debug)
|
||||
|
||||
# confirm words
|
||||
reset.confirm_words(debug, initial_backup_1_of_1)
|
||||
|
||||
# confirm share checked
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# confirm backup done
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# Your backup is done
|
||||
go_next(debug)
|
||||
|
||||
# great ... device is initialized, backup done, and we are not in recovery mode!
|
||||
assert device_handler.result() == "Initialized"
|
||||
features = device_handler.features()
|
||||
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||
assert features.initialized is True
|
||||
assert features.needs_backup is False
|
||||
assert features.no_backup is False
|
||||
assert features.recovery_mode is False
|
||||
|
||||
# run recovery to unlock backup
|
||||
device_handler.run(
|
||||
device.recover,
|
||||
unlock_repeated_backup=True,
|
||||
)
|
||||
|
||||
recovery.confirm_recovery(debug, "recovery__title_unlock_repeated_backup")
|
||||
|
||||
recovery.select_number_of_words(debug, num_of_words=20)
|
||||
recovery.enter_seed(
|
||||
debug,
|
||||
initial_backup_1_of_1,
|
||||
True,
|
||||
"recovery__enter_any_share",
|
||||
"recovery__unlock_repeated_backup",
|
||||
)
|
||||
|
||||
# backup is enabled
|
||||
go_next(debug)
|
||||
|
||||
assert device_handler.result().message == "Backup unlocked"
|
||||
|
||||
# we are now in recovery mode
|
||||
features = device_handler.features()
|
||||
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||
assert features.initialized is True
|
||||
assert features.needs_backup is False
|
||||
assert features.no_backup is False
|
||||
assert features.recovery_mode is True
|
||||
|
||||
# at this point, the backup is unlocked...
|
||||
|
||||
# ... so let's try to do a 2-of-3 backup
|
||||
|
||||
# confirm checklist
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# shares=3
|
||||
reset.set_selection(debug, buttons.RESET_MINUS, 5 - 3)
|
||||
|
||||
# confirm checklist
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# threshold=2
|
||||
reset.set_selection(debug, buttons.RESET_MINUS, 1)
|
||||
|
||||
# confirm checklist
|
||||
reset.confirm_read(debug)
|
||||
|
||||
# confirm backup warning
|
||||
reset.confirm_read(debug, middle_r=True)
|
||||
|
||||
second_backup_2_of_3: list[str] = []
|
||||
for _ in range(3):
|
||||
# read words
|
||||
words = reset.read_words(debug, do_htc=False)
|
||||
|
||||
# confirm words
|
||||
reset.confirm_words(debug, words)
|
||||
|
||||
# confirm share checked
|
||||
reset.confirm_read(debug)
|
||||
|
||||
second_backup_2_of_3.append(" ".join(words))
|
||||
|
||||
# we are not in recovery mode anymore, because we finished the backup process!
|
||||
features = device_handler.features()
|
||||
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||
assert features.initialized is True
|
||||
assert features.needs_backup is False
|
||||
assert features.no_backup is False
|
||||
assert features.recovery_mode is False
|
||||
|
||||
# try to unlock backup again...
|
||||
device_handler.run(
|
||||
device.recover,
|
||||
unlock_repeated_backup=True,
|
||||
)
|
||||
|
||||
recovery.confirm_recovery(debug, "recovery__title_unlock_repeated_backup")
|
||||
|
||||
# ... this time with the 2 shares from the *new* backup, which was a 2-of-3!
|
||||
recovery.select_number_of_words(debug, num_of_words=20)
|
||||
recovery.enter_shares(
|
||||
debug, second_backup_2_of_3[-2:], "recovery__unlock_repeated_backup"
|
||||
)
|
||||
|
||||
assert device_handler.result().message == "Backup unlocked"
|
||||
|
||||
# we are now in recovery mode again!
|
||||
features = device_handler.features()
|
||||
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||
assert features.initialized is True
|
||||
assert features.needs_backup is False
|
||||
assert features.no_backup is False
|
||||
assert features.recovery_mode is True
|
||||
|
||||
# but if we cancel the backup at this point...
|
||||
reset.cancel_backup(debug)
|
||||
|
||||
# ...we are out of recovery mode!
|
||||
features = device_handler.features()
|
||||
assert features.backup_type is messages.BackupType.Slip39_Basic
|
||||
assert features.initialized is True
|
||||
assert features.needs_backup is False
|
||||
assert features.no_backup is False
|
||||
assert features.recovery_mode is False
|
@ -0,0 +1,63 @@
|
||||
# This file is part of the Trezor project.
|
||||
#
|
||||
# Copyright (C) 2012-2024 SatoshiLabs and contributors
|
||||
#
|
||||
# This library is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License version 3
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the License along with this library.
|
||||
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
from trezorlib import device, messages
|
||||
from trezorlib.debuglink import TrezorClientDebugLink as Client
|
||||
from trezorlib.exceptions import TrezorFailure
|
||||
|
||||
from ..common import WITH_MOCK_URANDOM, MNEMONIC_SLIP39_BASIC_20_3of6
|
||||
from ..input_flows import InputFlowSlip39BasicBackup, InputFlowSlip39BasicRecoveryDryRun
|
||||
|
||||
|
||||
@pytest.mark.setup_client(needs_backup=True, mnemonic=MNEMONIC_SLIP39_BASIC_20_3of6)
|
||||
@pytest.mark.skip_t1b1
|
||||
@WITH_MOCK_URANDOM
|
||||
def test_repeated_backup(client: Client):
|
||||
assert client.features.needs_backup is True
|
||||
|
||||
# initial device backup
|
||||
mnemonics = []
|
||||
with client:
|
||||
IF = InputFlowSlip39BasicBackup(client, False)
|
||||
client.set_input_flow(IF.get())
|
||||
device.backup(client)
|
||||
mnemonics = IF.mnemonics
|
||||
|
||||
assert len(mnemonics) == 5
|
||||
|
||||
# cannot backup, since we already just did that!
|
||||
with pytest.raises(TrezorFailure, match=r".*Seed already backed up"):
|
||||
device.backup(client)
|
||||
|
||||
# unlock repeated backup by entering 3 of the 5 shares we have got
|
||||
with client:
|
||||
IF = InputFlowSlip39BasicRecoveryDryRun(client, mnemonics[:3])
|
||||
client.set_input_flow(IF.get())
|
||||
ret = device.recover(client, unlock_repeated_backup=True)
|
||||
assert ret == messages.Success(message="Backup unlocked")
|
||||
|
||||
# we can now perform another backup
|
||||
with client:
|
||||
IF = InputFlowSlip39BasicBackup(client, False)
|
||||
client.set_input_flow(IF.get())
|
||||
device.backup(client)
|
||||
|
||||
# the backup feature is locked again...
|
||||
with pytest.raises(TrezorFailure, match=r".*Seed already backed up"):
|
||||
device.backup(client)
|
Loading…
Reference in new issue