diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f2c5762d..e35bad9db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,6 +89,7 @@ jobs: apk add gmp-dev curl jq libsodium-dev git xxd procps python3 -m venv tezos_test_env --system-site-package source ./tezos_test_env/bin/activate + python3 -m pip install --upgrade pip -q python3 -m pip install -r ./tests/requirements.txt -q TMP_DIR=$(mktemp -d /tmp/foo-XXXXXX) tar xfz app_${{ matrix.device }}_dbg.tgz -C $TMP_DIR @@ -192,6 +193,7 @@ jobs: apk add gmp-dev curl jq libsodium-dev git xxd procps python3 -m venv tezos_test_env --system-site-package source ./tezos_test_env/bin/activate + python3 -m pip install --upgrade pip -q python3 -m pip install -r ./tests/requirements.txt -q ./tests/integration/run_test_local.sh -T100 -F -m ${{ matrix.device }} tests diff --git a/.github/workflows/coding_style_checks.yml b/.github/workflows/coding_style_checks.yml index ac30e70cf..a8ec73f95 100644 --- a/.github/workflows/coding_style_checks.yml +++ b/.github/workflows/coding_style_checks.yml @@ -23,3 +23,20 @@ jobs: source: './app/src' extensions: 'h,c' version: 15 + + pylint_check: + name: Python Lint Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + + - name: Install pylint + run: pip install pylint -r tests/requirements.txt + + - name: Run pylint with .pylintrc + run: pylint --errors-only --rcfile=tests/integration/.pylintrc tests/integration/nano/ diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 4c051d2fe..863ce0419 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -12,4 +12,4 @@ jobs: python-version: '3.10' - run: python -m pip install pre-commit - run: python -m pip freeze --local - - run: SKIP=clang-format pre-commit run --all-files + - run: SKIP=clang-format,pylint pre-commit run --all-files diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8675de0c6..3d97c6391 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,3 +19,12 @@ repos: language: system files: ^ types: [file, c] + + - id: pylint + name: pylint + description: Run pylint with custom .pylintrc configuration + entry: pylint + language: system + pass_filenames: false + args: ["--errors-only", "--rcfile=tests/integration/.pylintrc", "tests/integration/nano/"] + types: [python] diff --git a/Makefile b/Makefile index 695ccfd00..1e776f0b1 100644 --- a/Makefile +++ b/Makefile @@ -122,6 +122,7 @@ integration_tests_basic_%: app_%.tgz \ apk add gmp-dev curl jq libsodium-dev git xxd procps; \ python3 -m venv tezos_test_env --system-site-package; \ source ./tezos_test_env/bin/activate; \ + python3 -m pip install --upgrade pip -q; \ python3 -m pip install -r tests/requirements.txt -q ; \ python3 -m pytest -n 32 tests/integration/nano/ --tb=no \ --device $* --app \$$TMP_DIR/app.elf \ diff --git a/app/Makefile b/app/Makefile index a7f94e55d..8b8394eb8 100644 --- a/app/Makefile +++ b/app/Makefile @@ -43,7 +43,7 @@ APPNAME = "Tezos Wallet" # Application version APPVERSION_M=3 APPVERSION_N=0 -APPVERSION_P=5 +APPVERSION_P=6 APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) # COMMIT diff --git a/app/src/ui_stream_nbgl.c b/app/src/ui_stream_nbgl.c index ceda988f5..abb476e36 100644 --- a/app/src/ui_stream_nbgl.c +++ b/app/src/ui_stream_nbgl.c @@ -321,7 +321,7 @@ tz_ui_nav_cb(void) tz_parser_state *st = &global.keys.apdu.sign.u.clear.parser_state; // Continue receiving data from the apdu until s->full is true. - while (((s->total < 0) || ((s->current == s->total) && !s->full)) + while (((s->total < 0) || (s->current == s->total)) && !s->full && (st->errno < TZ_ERR_INVALID_TAG)) { PRINTF("tz_ui_nav_cb: Looping...\n"); tz_ui_continue(); diff --git a/scripts/test_swap.sh b/scripts/test_swap.sh index c649edc81..cabcfb261 100755 --- a/scripts/test_swap.sh +++ b/scripts/test_swap.sh @@ -151,7 +151,8 @@ run_tests() { --rm -v "$(realpath .):/app" \ ledger-app-tezos-integration-tests -c \ "cd /app && \ - pip install -r test/python/requirements.txt -q && \ + pip install --upgrade pip -q && \ + pip install -r test/python/requirements.txt -q && \ pip install protobuf==3.20.3 && pytest test/python $*" ) } diff --git a/tests/integration/.pylintrc b/tests/integration/.pylintrc new file mode 100644 index 000000000..db2b77d67 --- /dev/null +++ b/tests/integration/.pylintrc @@ -0,0 +1,13 @@ +[MESSAGES CONTROL] +disable= + broad-exception-caught, + dangerous-default-value, + global-statement, + line-too-long, + protected-access, + redefined-outer-name, + too-few-public-methods, + too-many-arguments, + too-many-instance-attributes, + too-many-positional-arguments, + too-many-statements, diff --git a/tests/integration/app_vars.sh b/tests/integration/app_vars.sh index bc465baea..0fa8945fe 100644 --- a/tests/integration/app_vars.sh +++ b/tests/integration/app_vars.sh @@ -19,6 +19,6 @@ export COMMIT_BYTES=$(printf '%s' "$COMMIT" | xxd -p -c 256) export VERSION_WALLET_TAG="00" export APPVERSION_M=3 export APPVERSION_N=0 -export APPVERSION_P=5 +export APPVERSION_P=6 export APPVERSION=$APPVERSION_M.$APPVERSION_N.$APPVERSION_P export VERSION_BYTES=$(printf "%02x%02x%02x%02x" "$VERSION_WALLET_TAG" "$APPVERSION_M" "$APPVERSION_N" "$APPVERSION_P") diff --git a/tests/integration/nano/conftest.py b/tests/integration/nano/conftest.py index 7f35558a1..52ba34bc4 100644 --- a/tests/integration/nano/conftest.py +++ b/tests/integration/nano/conftest.py @@ -1,11 +1,29 @@ -import pytest +# Copyright 2024 Trilitech +# Copyright 2024 Functori + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Conftest base on `ragger` conftest.""" + from pathlib import Path +from typing import Dict, Generator, List, Union + +import pytest from ragger.firmware import Firmware -from typing import Dict, Callable, Generator, List, Optional, Tuple, Union from utils.app import TezosAppScreen, SpeculosTezosBackend, DEFAULT_SEED -from utils.backend import TezosBackend, APP_KIND +from utils.backend import AppKind FIRMWARES: List[Firmware] = [ Firmware.NANOS, @@ -16,6 +34,7 @@ DEVICES: List[str] = list(map(lambda fw: fw.device, FIRMWARES)) def pytest_addoption(parser): + """Register argparse-style options for pytest.""" parser.addoption("-D", "--device", type=str, choices=DEVICES, @@ -49,6 +68,7 @@ def pytest_addoption(parser): global_log_dir: Union[Path, None] = None def pytest_configure(config): + """Configure pytest.""" global global_log_dir log_dir = config.getoption("log_dir") if log_dir is not None: @@ -57,21 +77,24 @@ def pytest_configure(config): logs : Dict[str, List[pytest.TestReport]] = {} @pytest.hookimpl(tryfirst=True) -def pytest_runtest_logstart(nodeid, location): +def pytest_runtest_logstart(location): + """Called at the start of running the runtest protocol for a single item.""" logs[location[2]] = [] @pytest.hookimpl(tryfirst=True) def pytest_runtest_logreport(report): + """Called at the end of running the runtest protocol for a single test.""" logs[report.head_line].append(report) @pytest.hookimpl(tryfirst=True) -def pytest_runtest_logfinish(nodeid, location): +def pytest_runtest_logfinish(location): + """Called at the end of running the runtest protocol for a single item.""" if global_log_dir is not None: log_dir = global_log_dir / Path(location[0]).stem log_dir.mkdir(parents=True, exist_ok=True) head_line = location[2] log_file = log_dir / f"{head_line.replace(' ', '_')}.log" - with open(log_file, 'w') as writer: + with open(log_file, 'w', encoding="utf-8") as writer: for report in logs[head_line]: writer.write(f"============================== {report.when.capitalize()} {report.outcome} ==============================\n") writer.write(f"{report.longreprtext}\n") @@ -84,11 +107,13 @@ def pytest_runtest_logfinish(nodeid, location): @pytest.fixture(scope="session") def firmware(pytestconfig) -> Firmware : + """Get `firware` for pytest.""" device = pytestconfig.getoption("device") return next(fw for fw in FIRMWARES if fw.device == device) @pytest.fixture(scope="session") -def port(pytestconfig, request, worker_id) -> int : +def port(pytestconfig, worker_id) -> int : + """Get `port` for pytest.""" if worker_id == "master": return pytestconfig.getoption("port") # worker_id = gw0, gw1, ... @@ -96,18 +121,22 @@ def port(pytestconfig, request, worker_id) -> int : @pytest.fixture(scope="session") def display(pytestconfig) -> bool : + """Get `display` for pytest.""" return pytestconfig.getoption("display") @pytest.fixture(scope="session") def golden_run(pytestconfig) -> bool: + """Get `golden_run` for pytest.""" return pytestconfig.getoption("golden_run") @pytest.fixture(scope="session") def app_path(pytestconfig) -> Path: + """Get `app_path` for pytest.""" return Path(pytestconfig.getoption("app")) @pytest.fixture(scope="session") def speculos_args(pytestconfig) -> List[str]: + """Get `app_path` for pytest.""" speculos_args = pytestconfig.getoption("speculos_args") if speculos_args is None: return [] @@ -115,6 +144,7 @@ def speculos_args(pytestconfig) -> List[str]: @pytest.fixture(scope="function") def seed(request) -> str: + """Get `seed` for pytest.""" param = getattr(request, "param", None) return param.get("seed", DEFAULT_SEED) if param else DEFAULT_SEED @@ -125,6 +155,7 @@ def backend(app_path: Path, display: bool, seed: str, speculos_args: List[str]) -> Generator[SpeculosTezosBackend, None, None]: + """Get `backend` for pytest.""" if display: speculos_args += ["--display", "qt"] @@ -143,10 +174,12 @@ def backend(app_path: Path, yield b @pytest.fixture(scope="function") -def app(backend: SpeculosTezosBackend, golden_run: bool): - return TezosAppScreen(backend, APP_KIND.WALLET, golden_run) +def app(backend: SpeculosTezosBackend, golden_run: bool) -> TezosAppScreen: + """Get `app` for pytest.""" + return TezosAppScreen(backend, AppKind.WALLET, golden_run) def requires_device(device): + """Wrapper to run the pytest test only with the provided device.""" return pytest.mark.skipif( f"config.getvalue('device') != '{ device }'", reason=f"Test requires device to be { device }." diff --git a/tests/integration/nano/nanos/__init__.py b/tests/integration/nano/nanos/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration/nano/nanos/test_nanos_regression_batched_ops.py b/tests/integration/nano/nanos/test_nanos_regression_batched_ops.py index 1fe7796c4..3143e874a 100755 --- a/tests/integration/nano/nanos/test_nanos_regression_batched_ops.py +++ b/tests/integration/nano/nanos/test_nanos_regression_batched_ops.py @@ -13,41 +13,44 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os -import sys +"""Check signing batch operation""" -file_path=os.path.abspath(__file__) -dir_path=os.path.dirname(file_path) -root_path=os.path.dirname(dir_path) -sys.path.append(root_path) +from pathlib import Path from conftest import requires_device -from pathlib import Path - -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message - -# Operation (0): Transaction -# Fee: 0.39 XTZ -# Storage limit: 6 -# Amount: 0.02 XTZ -# Destination: tz1cfdVKpBb9VRBdny8BQ5RSK82UudAp2miM -# Entrypoint: jean_bob -# Parameter: {Pair {} (Right -76723569314251090535296646);Pair {Elt Unit (Pair {Left Unit} (Pair (Left 0x03F01167865DC63DFEE0E31251329CEAB660D94606) (Pair 0x0107B21FCA96C5763F67B286752C7AAEFC5931D15A Unit)))} (Right 3120123370638446806591421154959427514880865200209654970345);Pair {} (Left (Some Unit))} -# Operation (1): Transaction -# Fee: 0.65 XTZ -# Storage limit: 2 -# Amount: 0.06 XTZ -# Destination: KT1CYT8oACUcCSNTu2qfgB4fj5bD7szYrpti +from utils.app import TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import OperationGroup, Transaction @requires_device("nanos") -def test_nanos_regression_batched_ops(app): +def test_nanos_regression_batched_ops(app: TezosAppScreen): + """Check signing batch operation""" test_name = Path(__file__).stem app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c001597c45b11b421bb806a0c56c5da5638bf4b1adbf0e617090006a09c010000bac799dfc7f6af2ff0b95f83d023e68c895020baffff086a65616e5f626f620000009a020000009507070200000000050800c6bab5ccc8d891cd8de4b6f7070707020000004b0704030b070702000000040505030b070705050a0000001503f01167865dc63dfee0e31251329ceab660d9460607070a000000150107b21fca96c5763f67b286752c7aaefc5931d15a030b050800a9df9fc1e7eaa7a9c1f7bd87a9ba9cadf5b5b2cd829deea2b7fef9070707020000000005050509030b6c01ee572f02e5be5d097ba17369789582882e8abb8790d627063202e0d403012b704944f5b5fd30eed2ab4385478488e09fe04a0000") + message = OperationGroup([ + Transaction( + source = 'tz1McCh72NRhYmJBcWr3zDrLJAxnfR9swcFh', + fee = 390000, + counter = 9, + gas_limit = 0, + storage_limit = 6, + destination = 'tz1cfdVKpBb9VRBdny8BQ5RSK82UudAp2miM', + amount = 20000, + entrypoint = 'jean_bob', + parameter = [{'prim':'Pair','args':[[],{'prim':'Right','args':[{'int':-76723569314251090535296646}]}]},{'prim':'Pair','args':[[{'prim':'Elt','args':[{'prim':'Unit','args':[]},{'prim':'Pair','args':[[{'prim':'Left','args':[{'prim':'Unit','args':[]}]}],{'prim':'Pair','args':[{'prim':'Left','args':[{'bytes':"03F01167865DC63DFEE0E31251329CEAB660D94606"}]},{'prim':'Pair','args':[{'bytes':"0107B21FCA96C5763F67B286752C7AAEFC5931D15A"},{'prim':'Unit','args':[]}]}]}]}]}],{'prim':'Right','args':[{'int':3120123370638446806591421154959427514880865200209654970345}]},]},{'prim':'Pair','args':[[],{'prim':'Left','args':[{'prim':'Some','args':[{'prim':'Unit','args':[]}]}]}]}] + ), + Transaction( + source = 'tz2W3Tvcm64GjcV2bipUynnEsctLFz5Z6yRa', + fee = 650000, + counter = 6, + gas_limit = 50, + storage_limit = 2, + destination = 'KT1CYT8oACUcCSNTu2qfgB4fj5bD7szYrpti', + amount = 60000 + ) + ]) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/nanosp/__init__.py b/tests/integration/nano/nanosp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration/nano/nanosp/test_nanosp_regression_potential_empty_screen.py b/tests/integration/nano/nanosp/test_nanosp_regression_potential_empty_screen.py index 1e0c7e8cf..9bc45532b 100755 --- a/tests/integration/nano/nanosp/test_nanosp_regression_potential_empty_screen.py +++ b/tests/integration/nano/nanosp/test_nanosp_regression_potential_empty_screen.py @@ -13,42 +13,35 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os -import sys - -file_path=os.path.abspath(__file__) -dir_path=os.path.dirname(file_path) -root_path=os.path.dirname(dir_path) -sys.path.append(root_path) - -from conftest import requires_device +"""Check signing operation that display potentially empty screens""" from pathlib import Path -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from conftest import requires_device -# Operation (0): Transfer ticket -# Fee: 0.01 XTZ -# Storage limit: 4 -# Contents: UNPAIR -# Type: pair "1" 2 -# Ticketer: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Amount: 1 -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT -# Entrypoint: S -# -# S -# S -# S +from utils.app import TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import TransferTicket @requires_device("nanosp") -def test_nanosp_regression_potential_empty_screen(app): +def test_nanosp_regression_potential_empty_screen(app: TezosAppScreen): + """Check signing operation that display potentially empty screens""" test_name = Path(__file__).stem app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000009e00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e02030400000002037a0000000a076501000000013100020000ffdd6102321bc251e4a5190ad5b12b251069d9b4010100000000000000000000000000000000000000000000000008530a0a530a530a53") + message = TransferTicket( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + ticket_contents = {'prim': 'UNPAIR'}, + ticket_ty = {'prim': 'pair', 'args': [{'string': '1'}, {'int': 2}]}, + ticket_ticketer = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + ticket_amount = 1, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + entrypoint = 'S\n\nS\nS\nS' + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/nanosp/test_nanosp_regression_press_right_works_across_apdu_recieves.py b/tests/integration/nano/nanosp/test_nanosp_regression_press_right_works_across_apdu_recieves.py index 0d2a1918a..b84c329cc 100755 --- a/tests/integration/nano/nanosp/test_nanosp_regression_press_right_works_across_apdu_recieves.py +++ b/tests/integration/nano/nanosp/test_nanosp_regression_press_right_works_across_apdu_recieves.py @@ -13,30 +13,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os -import sys - -file_path=os.path.abspath(__file__) -dir_path=os.path.dirname(file_path) -root_path=os.path.dirname(dir_path) -sys.path.append(root_path) - -from conftest import requires_device +"""Check no need to click right two times between APDUs during signing flow""" from pathlib import Path -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from conftest import requires_device -# Expression: {IF_NONE {{SWAP;IF {DIP {{DROP 1;PUSH unit Unit;PUSH bool True;PUSH string ";L\\S?p$-Fq)VDg\n]te\no4v0_8)\""}}} {{DROP 2;PUSH unit Unit;PUSH bool False;PUSH string "Li-%*edF6~?E[5Kmu?dyviwJ^2\"\\d$FyQ>>!>D$g(Qg";PUSH string "*Tx>!>D$g(Qg'}]},{'prim':'PUSH','args':[{'prim':'string'},{'string':'*Tx bytes: path = Path(test_name) / "reject_from_blind" def navigate() -> None: - app.navigate_until_text(Screen_text.Accept_risk, path / "clear") - app.navigate_until_text(Screen_text.Sign_reject, path / "blind") + app.navigate_until_text(ScreenText.ACCEPT_RISK, path / "clear") + app.navigate_until_text(ScreenText.SIGN_REJECT, path / "blind") send_and_navigate( send=expected_failure_send, diff --git a/tests/integration/nano/test_blindsign_too_deep.py b/tests/integration/nano/test_blindsign_too_deep.py index 4af12e15e..971399775 100755 --- a/tests/integration/nano/test_blindsign_too_deep.py +++ b/tests/integration/nano/test_blindsign_too_deep.py @@ -13,33 +13,34 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Check blindsigning on too deep expression""" + from multiprocessing import Process, Queue from pathlib import Path -from utils.app import Screen, Screen_text, DEFAULT_ACCOUNT -from utils.message import Message - -# Expression: {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{42}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +from utils.app import Screen, ScreenText, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import MichelineExpr -def test_blindsign_too_deep(app): +def test_blindsign_too_deep(app: TezosAppScreen): + """Check blindsigning on too deep expression""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - expression = Message.from_bytes("0502000000f702000000f202000000ed02000000e802000000e302000000de02000000d902000000d402000000cf02000000ca02000000c502000000c002000000bb02000000b602000000b102000000ac02000000a702000000a2020000009d02000000980200000093020000008e02000000890200000084020000007f020000007a02000000750200000070020000006b02000000660200000061020000005c02000000570200000052020000004d02000000480200000043020000003e02000000390200000034020000002f020000002a02000000250200000020020000001b02000000160200000011020000000c02000000070200000002002a") + expression = MichelineExpr([[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{'int':42}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]) if app.backend.firmware.device == "nanos": def send(result_queue: Queue) -> None: res = app.backend.sign(DEFAULT_ACCOUNT, expression, with_hash=True) result_queue.put(res) def assert_screen_i(i): - app.assert_screen(f"{str(i).zfill(5)}", path=(Path(test_name) / "clear")) + app.assert_screen(f"{str(i).zfill(5)}", path=Path(test_name) / "clear") result_queue: Queue = Queue() send_process = Process(target=send, args=(result_queue,)) send_process.start() - app.backend.wait_for_text_not_on_screen(Screen_text.Home) + app.backend.wait_for_text_not_on_screen(ScreenText.HOME) for i in range(6): # 'Review operation' @@ -55,7 +56,7 @@ def assert_screen_i(i): assert_screen_i(i+1) def blind_navigate() -> None: - app.navigate_until_text(Screen_text.Sign_accept, Path(test_name) / "blind") + app.navigate_until_text(ScreenText.SIGN_ACCEPT, Path(test_name) / "blind") navigate_process = Process(target=blind_navigate) navigate_process.start() diff --git a/tests/integration/nano/test_blindsign_too_large.py b/tests/integration/nano/test_blindsign_too_large.py index 97c59565a..71c81695d 100755 --- a/tests/integration/nano/test_blindsign_too_large.py +++ b/tests/integration/nano/test_blindsign_too_large.py @@ -13,19 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check blindsigning on too large expression""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Expression: 12345678901234567890123456789012345678901234567890123456789012345678901234567890 +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import MichelineExpr -def test_blindsign_too_large(app): +def test_blindsign_too_large(app: TezosAppScreen): + """Check blindsigning on too large expression""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("050092abf8e3d9e5f8cfd9ae8a9fe5f28ea1d5b5abf1af82dae8a4b68df3d1889eb6f988f5e8d31a") + message = MichelineExpr({'int':12345678901234567890123456789012345678901234567890123456789012345678901234567890}) data = app.blind_sign(DEFAULT_ACCOUNT, message=message, diff --git a/tests/integration/nano/test_change_sign_instruction.py b/tests/integration/nano/test_change_sign_instruction.py index ffd2102e7..3bc36732d 100755 --- a/tests/integration/nano/test_change_sign_instruction.py +++ b/tests/integration/nano/test_change_sign_instruction.py @@ -13,30 +13,43 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.backend import INS, StatusCode -from utils.message import Message +"""Check signing instruction changes behaviour""" -def test_change_sign_instruction(app): +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.backend import Ins, StatusCode +from utils.message import Transaction - app.assert_screen(Screen.Home) +def test_change_sign_instruction(app: TezosAppScreen): + """Check signing instruction changes behaviour""" - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316") - payload=message.bytes + app.assert_screen(Screen.HOME) - app.backend._ask_sign(INS.SIGN_WITH_HASH, DEFAULT_ACCOUNT) + message = Transaction( + source = 'tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu', + fee = 50000, + counter = 8, + gas_limit = 54, + storage_limit = 45, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + amount = 240000, + entrypoint = 'do', + parameter = {'prim': 'CAR'} + ) + payload=bytes(message) + + app.backend._ask_sign(Ins.SIGN_WITH_HASH, DEFAULT_ACCOUNT) with app.expect_apdu_failure(StatusCode.INVALID_INS): - app.backend._continue_sign(INS.SIGN, + app.backend._continue_sign(Ins.SIGN, payload, last=True) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - app.backend._ask_sign(INS.SIGN, DEFAULT_ACCOUNT) + app.backend._ask_sign(Ins.SIGN, DEFAULT_ACCOUNT) with app.expect_apdu_failure(StatusCode.INVALID_INS): - app.backend._continue_sign(INS.SIGN_WITH_HASH, + app.backend._continue_sign(Ins.SIGN_WITH_HASH, payload, last=True) diff --git a/tests/integration/nano/test_ensure_always_clearsign.py b/tests/integration/nano/test_ensure_always_clearsign.py index 5bdca4697..84f00c170 100755 --- a/tests/integration/nano/test_ensure_always_clearsign.py +++ b/tests/integration/nano/test_ensure_always_clearsign.py @@ -13,25 +13,30 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check clear signing never blindsign""" -from utils.app import DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Transaction -# Fee: 0.01 XTZ -# Storage limit: 4 -# Amount: 0 XTZ -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT -# Entrypoint: root -# Data: {pair "[" (pair "Z" (pair "Y" (pair "X" (pair "W" (pair "V" (pair "U" (pair "T" (pair "S" (pair "R" (pair "Q" (pair "P" (pair "O" (pair "N" (pair "M" (pair "L" (pair "K" (pair "J" (pair "I" (pair "H" (pair "G" (pair "F" (pair "E" (pair "D" (pair "C" (pair "B" {})))))))))))))))))))))))));pair 10 (pair 9 (pair 8 (pair 7 (pair 6 (pair 5 (pair 4 (pair 3 (pair 2 (pair 1 {})))))))))} +from utils.app import TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import Transaction -def test_ensure_always_clearsign(app): +def test_ensure_always_clearsign(app: TezosAppScreen): + """Check clear signing never blindsign""" test_name = Path(__file__).stem app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e0203040001000000000000000000000000000000000000000000ff01000001070200000102076501000000015b076501000000015a0765010000000159076501000000015807650100000001570765010000000156076501000000015507650100000001540765010000000153076501000000015207650100000001510765010000000150076501000000014f076501000000014e076501000000014d076501000000014c076501000000014b076501000000014a0765010000000149076501000000014807650100000001470765010000000146076501000000014507650100000001440765010000000143076501000000014202000000000765000a0765000907650008076500070765000607650005076500040765000307650002076500010200000000") + message = Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + amount = 0, + entrypoint = 'root', + parameter = [{'prim':'pair','args':[{'string':"["},{'prim':'pair','args':[{'string':"Z"},{'prim':'pair','args':[{'string':"Y"},{'prim':'pair','args':[{'string':"X"},{'prim':'pair','args':[{'string':"W"},{'prim':'pair','args':[{'string':"V"},{'prim':'pair','args':[{'string':"U"},{'prim':'pair','args':[{'string':"T"},{'prim':'pair','args':[{'string':"S"},{'prim':'pair','args':[{'string':"R"},{'prim':'pair','args':[{'string':"Q"},{'prim':'pair','args':[{'string':"P"},{'prim':'pair','args':[{'string':"O"},{'prim':'pair','args':[{'string':"N"},{'prim':'pair','args':[{'string':"M"},{'prim':'pair','args':[{'string':"L"},{'prim':'pair','args':[{'string':"K"},{'prim':'pair','args':[{'string':"J"},{'prim':'pair','args':[{'string':"I"},{'prim':'pair','args':[{'string':"H"},{'prim':'pair','args':[{'string':"G"},{'prim':'pair','args':[{'string':"F"},{'prim':'pair','args':[{'string':"E"},{'prim':'pair','args':[{'string':"D"},{'prim':'pair','args':[{'string':"C"},{'prim':'pair','args':[{'string':"B"},[]]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]},{'prim':'pair','args':[{'int':10},{'prim':'pair','args':[{'int':9},{'prim':'pair','args':[{'int':8},{'prim':'pair','args':[{'int':7},{'prim':'pair','args':[{'int':6},{'prim':'pair','args':[{'int':5},{'prim':'pair','args':[{'int':4},{'prim':'pair','args':[{'int':3},{'prim':'pair','args':[{'int':2},{'prim':'pair','args':[{'int':1},[]]}]}]}]}]}]}]}]}]}]}] + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_get_pk.py b/tests/integration/nano/test_get_pk.py index 0593b7c53..4c82a0e9e 100755 --- a/tests/integration/nano/test_get_pk.py +++ b/tests/integration/nano/test_get_pk.py @@ -13,29 +13,31 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.account import Account, SIGNATURE_TYPE -from utils.app import Screen +"""Module to test get-public-key instruction.""" -def test_get_pk(app): +from utils.account import Account, SigType +from utils.app import Screen, TezosAppScreen +def test_get_pk(app: TezosAppScreen): + """Test that public keys get from the app are correct.""" accounts = [ Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.ED25519, + SigType.ED25519, "edpkuXX2VdkdXzkN11oLCb8Aurdo1BTAtQiK8ZY9UPj2YMt3AHEpcY"), Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.SECP256K1, + SigType.SECP256K1, "sppk7bVy617DmGvXsMqcwsiLtnedTN2trUi5ugXcNig7en4rHJyunK1"), Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.SECP256R1, + SigType.SECP256R1, "p2pk67fq5pzuMMABZ9RDrooYbLrgmnQbLt8z7PTGM9mskf7LXS5tdBG"), Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.BIP32_ED25519, + SigType.BIP32_ED25519, "edpkumJgSsSxkpiB5hmTq6eZcrmc6BsJtLAhYceFTiziFqje4mongz") ] for account in accounts: - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) data = app.backend.get_public_key(account, with_prompt=False) diff --git a/tests/integration/nano/test_git.py b/tests/integration/nano/test_git.py index 2781b17f0..97c6b9a46 100755 --- a/tests/integration/nano/test_git.py +++ b/tests/integration/nano/test_git.py @@ -13,11 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.app import Screen +"""Module to test git instruction.""" -def test_git(app): +from utils.app import Screen, TezosAppScreen - app.assert_screen(Screen.Home) +def test_git(app: TezosAppScreen): + """Test that the app commit is the same as the current git commit.""" + app.assert_screen(Screen.HOME) data = app.backend.git() diff --git a/tests/integration/nano/test_mixing_command.py b/tests/integration/nano/test_mixing_command.py index b49af6e4b..c2fdf66d3 100755 --- a/tests/integration/nano/test_mixing_command.py +++ b/tests/integration/nano/test_mixing_command.py @@ -13,44 +13,47 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.backend import INS, StatusCode +"""Check that mixing instruction fails""" -def test_mixing_command(app): +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.backend import Ins, StatusCode - app.assert_screen(Screen.Home) +def test_mixing_command(app: TezosAppScreen): + """Check that mixing instruction fails""" - app.backend._ask_sign(INS.SIGN, DEFAULT_ACCOUNT) + app.assert_screen(Screen.HOME) + + app.backend._ask_sign(Ins.SIGN, DEFAULT_ACCOUNT) with app.expect_apdu_failure(StatusCode.UNEXPECTED_STATE): app.backend.version() - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - app.backend._ask_sign(INS.SIGN_WITH_HASH, DEFAULT_ACCOUNT) + app.backend._ask_sign(Ins.SIGN_WITH_HASH, DEFAULT_ACCOUNT) with app.expect_apdu_failure(StatusCode.UNEXPECTED_STATE): - app.backend._ask_sign(INS.SIGN, DEFAULT_ACCOUNT) + app.backend._ask_sign(Ins.SIGN, DEFAULT_ACCOUNT) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - app.backend._ask_sign(INS.SIGN, DEFAULT_ACCOUNT) + app.backend._ask_sign(Ins.SIGN, DEFAULT_ACCOUNT) with app.expect_apdu_failure(StatusCode.UNEXPECTED_STATE): - app.backend._ask_sign(INS.SIGN_WITH_HASH, DEFAULT_ACCOUNT) + app.backend._ask_sign(Ins.SIGN_WITH_HASH, DEFAULT_ACCOUNT) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - app.backend._ask_sign(INS.SIGN, DEFAULT_ACCOUNT) + app.backend._ask_sign(Ins.SIGN, DEFAULT_ACCOUNT) with app.expect_apdu_failure(StatusCode.UNEXPECTED_STATE): app.backend.get_public_key(DEFAULT_ACCOUNT, with_prompt=True) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - app.backend._ask_sign(INS.SIGN, DEFAULT_ACCOUNT) + app.backend._ask_sign(Ins.SIGN, DEFAULT_ACCOUNT) with app.expect_apdu_failure(StatusCode.UNEXPECTED_STATE): app.backend.get_public_key(DEFAULT_ACCOUNT, with_prompt=False) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - app.backend._ask_sign(INS.SIGN, DEFAULT_ACCOUNT) + app.backend._ask_sign(Ins.SIGN, DEFAULT_ACCOUNT) with app.expect_apdu_failure(StatusCode.UNEXPECTED_STATE): app.backend.git() diff --git a/tests/integration/nano/test_parsing_errors.py b/tests/integration/nano/test_parsing_errors.py index 4a01f9609..7b53124f2 100755 --- a/tests/integration/nano/test_parsing_errors.py +++ b/tests/integration/nano/test_parsing_errors.py @@ -13,15 +13,29 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Check parsing error handling""" + from pathlib import Path -from utils.app import Screen, DEFAULT_ACCOUNT +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT from utils.backend import StatusCode -from utils.message import Message +from utils.message import RawMessage + +# Operation (0): Transaction +# Source: tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu +# Fee: 0.05 XTZ +# Gas limit: 54 +# Counter: 8 +# Storage limit: 45 +# Amount: 0.24 XTZ +# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT +# Entrypoint: do +# Parameter: CAR # original bytes : 0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316 -def test_parsing_errors(app): +def test_parsing_errors(app: TezosAppScreen): + """Check parsing error handling""" test_name = Path(__file__).stem def make_path(name: str) -> Path: @@ -29,52 +43,52 @@ def make_path(name: str) -> Path: app.setup_expert_mode() - unknown_magic_bytes_message = Message.from_bytes("0100000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316") + unknown_magic_bytes_message = RawMessage("0100000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316") app.parsing_error_signing(DEFAULT_ACCOUNT, unknown_magic_bytes_message, with_hash=True, path=make_path("unknown_magic_bytes")) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - unknown_operation_message = Message.from_bytes("03000000000000000000000000000000000000000000000000000000000000000001016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316") + unknown_operation_message = RawMessage("03000000000000000000000000000000000000000000000000000000000000000001016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316") app.parsing_error_signing(DEFAULT_ACCOUNT, unknown_operation_message, with_hash=True, path=make_path("unknown_operation")) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - one_byte_removed_inside_message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e010000000000000000000000000000000000000000ff02000000020316") + one_byte_removed_inside_message = RawMessage("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e010000000000000000000000000000000000000000ff02000000020316") app.parsing_error_signing(DEFAULT_ACCOUNT, one_byte_removed_inside_message, with_hash=True, path=make_path("one_byte_removed_inside")) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - one_byte_added_at_the_end_message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff0200000002031645") + one_byte_added_at_the_end_message = RawMessage("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff0200000002031645") app.parsing_error_signing(DEFAULT_ACCOUNT, one_byte_added_at_the_end_message, with_hash=True, path=make_path("one_byte_added_at_the_end")) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - one_byte_added_inside_message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e0100000000000000000000000000000000000000000000ff02000000020316") + one_byte_added_inside_message = RawMessage("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e0100000000000000000000000000000000000000000000ff02000000020316") app.parsing_error_signing(DEFAULT_ACCOUNT, one_byte_added_inside_message, with_hash=True, path=make_path("one_byte_added_inside")) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - wrong_last_packet_message = Message.from_bytes("030000000000000000000000000000000000000000000000000000000000000000ce00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e02030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c63966303966323935326433343532386337333366393436313563666333396263353535363139666335353064643461363762613232303863653865383637616133643133613665663939646662653332633639373461613961323135306432316563613239633333343965353963313362393038316631") + wrong_last_packet_message = RawMessage("030000000000000000000000000000000000000000000000000000000000000000ce00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e02030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c63966303966323935326433343532386337333366393436313563666333396263353535363139666335353064643461363762613232303863653865383637616133643133613665663939646662653332633639373461613961323135306432316563613239633333343965353963313362393038316631") app.hard_failing_signing(DEFAULT_ACCOUNT, wrong_last_packet_message, diff --git a/tests/integration/nano/test_provide_pk.py b/tests/integration/nano/test_provide_pk.py index 3a79ef895..516d4a094 100755 --- a/tests/integration/nano/test_provide_pk.py +++ b/tests/integration/nano/test_provide_pk.py @@ -13,38 +13,41 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Module to test get-public-key instruction.""" + from pathlib import Path -from utils.account import Account, SIGNATURE_TYPE -from utils.app import Screen, DEFAULT_ACCOUNT +from utils.account import Account, SigType +from utils.app import Screen, TezosAppScreen -def test_provide_pk(app): +def test_provide_pk(app: TezosAppScreen): + """Test that public keys get from the app are correct and correctly displayed.""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) accounts = [ ( Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.ED25519, + SigType.ED25519, "edpkuXX2VdkdXzkN11oLCb8Aurdo1BTAtQiK8ZY9UPj2YMt3AHEpcY"), "ed25519" ), ( Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.SECP256K1, + SigType.SECP256K1, "sppk7bVy617DmGvXsMqcwsiLtnedTN2trUi5ugXcNig7en4rHJyunK1"), "secp256k1" ), ( Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.SECP256R1, + SigType.SECP256R1, "p2pk67fq5pzuMMABZ9RDrooYbLrgmnQbLt8z7PTGM9mskf7LXS5tdBG"), "secp256r1" ), ( Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.BIP32_ED25519, + SigType.BIP32_ED25519, "edpkumJgSsSxkpiB5hmTq6eZcrmc6BsJtLAhYceFTiziFqje4mongz"), "bip32_ed25519" ) @@ -52,7 +55,7 @@ def test_provide_pk(app): for (account, kind) in accounts: - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) data = app.provide_public_key(account, test_name + "/" + kind) diff --git a/tests/integration/nano/test_regression_continue_after_reject.py b/tests/integration/nano/test_regression_continue_after_reject.py index 70b587716..ae5aca528 100755 --- a/tests/integration/nano/test_regression_continue_after_reject.py +++ b/tests/integration/nano/test_regression_continue_after_reject.py @@ -13,12 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Check the app still runs after rejects signing""" + from pathlib import Path -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import Transaction -def test_regression_continue_after_reject(app): +def test_regression_continue_after_reject(app: TezosAppScreen): + """Check the app still runs after rejects signing""" test_name = Path(__file__).stem def make_path(name: str) -> Path: @@ -28,9 +31,19 @@ def make_path(name: str) -> Path: app.reject_public_key(DEFAULT_ACCOUNT, make_path("reject_public_key")) - app.assert_screen(Screen.Home) - - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e0203040001000000000000000000000000000000000000000000ff01000001070200000102076501000000015b076501000000015a0765010000000159076501000000015807650100000001570765010000000156076501000000015507650100000001540765010000000153076501000000015207650100000001510765010000000150076501000000014f076501000000014e076501000000014d076501000000014c076501000000014b076501000000014a0765010000000149076501000000014807650100000001470765010000000146076501000000014507650100000001440765010000000143076501000000014202000000000765000a0765000907650008076500070765000607650005076500040765000307650002076500010200000000") + app.assert_screen(Screen.HOME) + + message = Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + amount = 0, + entrypoint = 'root', + parameter = [{'prim':'pair','args':[{'string':"["},{'prim':'pair','args':[{'string':"Z"},{'prim':'pair','args':[{'string':"Y"},{'prim':'pair','args':[{'string':"X"},{'prim':'pair','args':[{'string':"W"},{'prim':'pair','args':[{'string':"V"},{'prim':'pair','args':[{'string':"U"},{'prim':'pair','args':[{'string':"T"},{'prim':'pair','args':[{'string':"S"},{'prim':'pair','args':[{'string':"R"},{'prim':'pair','args':[{'string':"Q"},{'prim':'pair','args':[{'string':"P"},{'prim':'pair','args':[{'string':"O"},{'prim':'pair','args':[{'string':"N"},{'prim':'pair','args':[{'string':"M"},{'prim':'pair','args':[{'string':"L"},{'prim':'pair','args':[{'string':"K"},{'prim':'pair','args':[{'string':"J"},{'prim':'pair','args':[{'string':"I"},{'prim':'pair','args':[{'string':"H"},{'prim':'pair','args':[{'string':"G"},{'prim':'pair','args':[{'string':"F"},{'prim':'pair','args':[{'string':"E"},{'prim':'pair','args':[{'string':"D"},{'prim':'pair','args':[{'string':"C"},{'prim':'pair','args':[{'string':"B"},[]]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]},{'prim':'pair','args':[{'int':10},{'prim':'pair','args':[{'int':9},{'prim':'pair','args':[{'int':8},{'prim':'pair','args':[{'int':7},{'prim':'pair','args':[{'int':6},{'prim':'pair','args':[{'int':5},{'prim':'pair','args':[{'int':4},{'prim':'pair','args':[{'int':3},{'prim':'pair','args':[{'int':2},{'prim':'pair','args':[{'int':1},[]]}]}]}]}]}]}]}]}]}]}] + ) app.reject_signing(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_reject_pk.py b/tests/integration/nano/test_reject_pk.py index 20f1b1740..65251f67f 100755 --- a/tests/integration/nano/test_reject_pk.py +++ b/tests/integration/nano/test_reject_pk.py @@ -13,14 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Check reject pk behaviour""" + from pathlib import Path -from utils.app import Screen, DEFAULT_ACCOUNT +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT -def test_reject_pk(app): +def test_reject_pk(app: TezosAppScreen): + """Check reject pk behaviour""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) app.reject_public_key(DEFAULT_ACCOUNT, test_name) diff --git a/tests/integration/nano/test_sign_ballot.py b/tests/integration/nano/test_sign_ballot.py index 8ea933d43..30128cc30 100755 --- a/tests/integration/nano/test_sign_ballot.py +++ b/tests/integration/nano/test_sign_ballot.py @@ -13,23 +13,25 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing ballot""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Ballot -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Period: 32 -# Proposal: ProtoALphaALphaALphaALphaALphaALphaALpha61322gcLUGH -# Ballot: yay +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import Ballot -def test_sign_ballot(app): +def test_sign_ballot(app: TezosAppScreen): + """Check signing ballot""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000000600ffdd6102321bc251e4a5190ad5b12b251069d9b4000000200bcd7b2cadcd87ecb0d5c50330fb59feed7432bffecede8a09a2b86cfb33847b00") + message = Ballot( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + proposal = 'ProtoALphaALphaALphaALphaALphaALphaALpha61322gcLUGH', + ballot = 'yay', + period = 32 + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_complex_operation.py b/tests/integration/nano/test_sign_complex_operation.py index 029397d80..0ccf3ddf5 100644 --- a/tests/integration/nano/test_sign_complex_operation.py +++ b/tests/integration/nano/test_sign_complex_operation.py @@ -13,46 +13,48 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path - -from utils.app import Screen, Screen_text, DEFAULT_ACCOUNT -from utils.backend import StatusCode -from utils.message import Message +"""Check signing complex operation""" -# Operation (0): Transaction -# Fee: 0.05 XTZ -# Storage limit: 45 -# Amount: 0.24 XTZ -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT -# Entrypoint: do -# Parameter: CAR +from pathlib import Path -## Operation (0): Origination -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 0.5 XTZ -# Storage limit: 4 -# Balance: 1 XTZ -# Delegate: None -# Code: UNPACK mutez -# Storage: or key chest -## Operation (1): Transfer ticket -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 0.01 XTZ -# Storage limit: 5 -# Contents: None -# Type: option nat -# Ticketer: tz1TmFPVZsGQ8MnrBJtnECJgkFUwLa6EWYDm -# Amount: 7 -# Destination: tz3eydffbLkjdVb8zx42BvxpGV87zaRnqL3r -# Entrypoint: default +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import ( + OperationGroup, + Origination, + TransferTicket +) -def test_sign_complex_operation(app): +def test_sign_complex_operation(app: TezosAppScreen): + """Check signing complex operation""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006d00ffdd6102321bc251e4a5190ad5b12b251069d9b4a0c21e040304c0843d0000000004050d036a000000060764035c038d9e00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e05040500000002030600000004056303620000591e842444265757d6a65e3670ca18b5e662f9c0070002cc8e146741cf31fc00123b8c26baf95c57421a3c0000000764656661756c74") + message = OperationGroup([ + Origination( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 500000, + counter = 4, + gas_limit = 3, + storage_limit = 4, + code = {'prim': 'UNPACK', 'args': [{'prim': 'mutez'}]}, + storage = {'prim': 'or', 'args': [{'prim': 'key'}, {'prim': 'chest'}]}, + balance = 1000000 + ), + TransferTicket( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 5, + gas_limit = 4, + storage_limit = 5, + ticket_contents = {'prim': 'None'}, + ticket_ty = {'prim': 'option', 'args': [{'prim': 'nat'}]}, + ticket_ticketer = 'tz1TmFPVZsGQ8MnrBJtnECJgkFUwLa6EWYDm', + ticket_amount = 7, + destination = 'tz3eydffbLkjdVb8zx42BvxpGV87zaRnqL3r' + ) + ]) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_delegation.py b/tests/integration/nano/test_sign_delegation.py index 1e6b26788..daa85df88 100755 --- a/tests/integration/nano/test_sign_delegation.py +++ b/tests/integration/nano/test_sign_delegation.py @@ -13,22 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing delegation""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Delegation -# Fee: 0.2 XTZ -# Storage limit: 889 -# Delegate: tz1TmFPVZsGQ8MnrBJtnECJgkFUwLa6EWYDm +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import Delegation -def test_sign_delegation(app): +def test_sign_delegation(app: TezosAppScreen): + """Check signing delegation""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006e01774d99da021b92d8c3dfc2e814c7658440319be2c09a0cf40509f906ff00591e842444265757d6a65e3670ca18b5e662f9c0") + message = Delegation( + source = 'tz2KC42yW9FXFMJpkUooae2NFYQsM5do3E8H', + fee = 200000, + counter = 756, + gas_limit = 9, + storage_limit = 889, + delegate = 'tz1TmFPVZsGQ8MnrBJtnECJgkFUwLa6EWYDm' + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_failing_noop.py b/tests/integration/nano/test_sign_failing_noop.py index 4f4ec271a..5bd24dc61 100755 --- a/tests/integration/nano/test_sign_failing_noop.py +++ b/tests/integration/nano/test_sign_failing_noop.py @@ -13,20 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing failing noop""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Failing noop -# Message: 396630396632393532643334353238633733336639343631356366633339626335353536313966633535306464346136376261323230386365386538363761613364313361366566393964666265333263363937346161396132313530643231656361323963333334396535396331336239303831663163313162343430616334643334353564656462653465653064653135613861663632306434633836323437643964313332646531626236646132336435666639643864666664613232626139613834 +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import FailingNoop -def test_sign_failing_noop(app): +def test_sign_failing_noop(app: TezosAppScreen): + """Check signing failing noop""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("03000000000000000000000000000000000000000000000000000000000000000011000000c6396630396632393532643334353238633733336639343631356366633339626335353536313966633535306464346136376261323230386365386538363761613364313361366566393964666265333263363937346161396132313530643231656361323963333334396535396331336239303831663163313162343430616334643334353564656462653465653064653135613861663632306434633836323437643964313332646531626236646132336435666639643864666664613232626139613834") + message = FailingNoop("9f09f2952d34528c733f94615cfc39bc555619fc550dd4a67ba2208ce8e867aa3d13a6ef99dfbe32c6974aa9a2150d21eca29c3349e59c13b9081f1c11b440ac4d3455dedbe4ee0de15a8af620d4c86247d9d132de1bb6da23d5ff9d8dffda22ba9a84") data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_increase_paid_storage.py b/tests/integration/nano/test_sign_increase_paid_storage.py index 2ffe79c74..db4869180 100755 --- a/tests/integration/nano/test_sign_increase_paid_storage.py +++ b/tests/integration/nano/test_sign_increase_paid_storage.py @@ -13,23 +13,28 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing increase paid storage""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Increase paid storage -# Fee: 0.01 XTZ -# Storage limit: 4 -# Amount: 5 -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import IncreasePaidStorage -def test_sign_increase_paid_storage(app): +def test_sign_increase_paid_storage(app: TezosAppScreen): + """Check signing increase paid storage""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) - - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000007100ffdd6102321bc251e4a5190ad5b12b251069d9b4904e0203040501000000000000000000000000000000000000000000") + app.assert_screen(Screen.HOME) + + message = IncreasePaidStorage( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + amount = 5, + destination = "KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT" + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_micheline_without_hash.py b/tests/integration/nano/test_sign_micheline_without_hash.py index de8fb0c5c..5b8dee986 100755 --- a/tests/integration/nano/test_sign_micheline_without_hash.py +++ b/tests/integration/nano/test_sign_micheline_without_hash.py @@ -13,19 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing micheline wihout getting hash""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Expression: {"CACA";"POPO";"BOUDIN"} +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import MichelineExpr -def test_sign_micheline_without_hash(app): +def test_sign_micheline_without_hash(app: TezosAppScreen): + """Check signing micheline wihout getting hash""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("05020000001d0100000004434143410100000004504f504f0100000006424f5544494e") + message = MichelineExpr([{'string': 'CACA'}, {'string': 'POPO'}, {'string': 'BOUDIN'}]) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_origination.py b/tests/integration/nano/test_sign_origination.py index 3f559a398..49da3f640 100755 --- a/tests/integration/nano/test_sign_origination.py +++ b/tests/integration/nano/test_sign_origination.py @@ -13,25 +13,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing origination""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Origination -# Fee: 0.01 XTZ -# Storage limit: 4 -# Balance: 0.5 XTZ -# Delegate: Field unset -# Code: UNPAIR -# Storage: pair "1" 2 +from utils.app import TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import Origination -def test_sign_origination(app): +def test_sign_origination(app: TezosAppScreen): + """Check signing origination""" test_name = Path(__file__).stem app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006d00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e020304a0c21e0000000002037a0000000a07650100000001310002") + message = Origination( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + code = {'prim': 'UNPAIR'}, + storage = {'prim': 'pair', 'args': [{'string': '1'}, {'int': 2}]}, + balance = 500000 + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_proposals.py b/tests/integration/nano/test_sign_proposals.py index 816ebdcfb..ae29ca1f5 100755 --- a/tests/integration/nano/test_sign_proposals.py +++ b/tests/integration/nano/test_sign_proposals.py @@ -13,23 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing proposals""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Proposals -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Period: 32 -# Proposal (0): ProtoALphaALphaALphaALphaALphaALphaALpha61322gcLUGH -# Proposal (1): ProtoALphaALphaALphaALphaALphaALphaALphabc2a7ebx6WB +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import Proposals -def test_sign_proposals(app): +def test_sign_proposals(app: TezosAppScreen): + """Check signing proposals""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000000500ffdd6102321bc251e4a5190ad5b12b251069d9b400000020000000400bcd7b2cadcd87ecb0d5c50330fb59feed7432bffecede8a09a2b86cfb33847b0bcd7b2cadcd87ecb0d5c50330fb59feed7432bffecede8a09a2b86dac301a2d") + message = Proposals( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + proposals = [ + 'ProtoALphaALphaALphaALphaALphaALphaALpha61322gcLUGH', + 'ProtoALphaALphaALphaALphaALphaALphaALphabc2a7ebx6WB' + ], + period = 32 + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_register_global_constant.py b/tests/integration/nano/test_sign_register_global_constant.py index c9294f607..4ea36859e 100755 --- a/tests/integration/nano/test_sign_register_global_constant.py +++ b/tests/integration/nano/test_sign_register_global_constant.py @@ -13,22 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing register global constant""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Register global constant -# Fee: 0.01 XTZ -# Storage limit: 4 -# Value: Pair "1" 2 +from utils.app import TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import RegisterGlobalConstant -def test_sign_register_global_constant(app): +def test_sign_register_global_constant(app: TezosAppScreen): + """Check signing register global constant""" test_name = Path(__file__).stem app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006f00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e0203040000000a07070100000001310002") + message = RegisterGlobalConstant( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + value = {'prim': 'Pair', 'args': [{'string': '1'}, {'int': 2}]} + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_reveal.py b/tests/integration/nano/test_sign_reveal.py index 19796786a..ec5cbd15c 100755 --- a/tests/integration/nano/test_sign_reveal.py +++ b/tests/integration/nano/test_sign_reveal.py @@ -13,22 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing reveal""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Reveal -# Fee: 0.01 XTZ -# Storage limit: 4 -# Public key: edpkuXX2VdkdXzkN11oLCb8Aurdo1BTAtQiK8ZY9UPj2YMt3AHEpcY +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import Reveal -def test_sign_reveal(app): +def test_sign_reveal(app: TezosAppScreen): + """Check signing reveal""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006b00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e02030400747884d9abdf16b3ab745158925f567e222f71225501826fa83347f6cbe9c393") + message = Reveal( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + public_key = 'edpkuXX2VdkdXzkN11oLCb8Aurdo1BTAtQiK8ZY9UPj2YMt3AHEpcY' + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_sc_rollup_add_messages.py b/tests/integration/nano/test_sign_sc_rollup_add_messages.py index e84c4ec36..b89e97533 100755 --- a/tests/integration/nano/test_sign_sc_rollup_add_messages.py +++ b/tests/integration/nano/test_sign_sc_rollup_add_messages.py @@ -13,24 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing smart rollup add message""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): SR: send messages -# Fee: 0.01 XTZ -# Storage limit: 4 -# Message (0): 012345 -# Message (1): 67 -# Message (2): 89abcdef +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import ScRollupAddMessage -def test_sign_sc_rollup_add_messages(app): +def test_sign_sc_rollup_add_messages(app: TezosAppScreen): + """Check signing smart rollup add message""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("030000000000000000000000000000000000000000000000000000000000000000c900ffdd6102321bc251e4a5190ad5b12b251069d9b4904e020304000000140000000301234500000001670000000489abcdef") + message = ScRollupAddMessage( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + message = [bytes.fromhex('012345'), bytes.fromhex('67'), bytes.fromhex('89abcdef')] + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_sc_rollup_execute_outbox_message.py b/tests/integration/nano/test_sign_sc_rollup_execute_outbox_message.py index 0e61d14e9..5b80577e8 100755 --- a/tests/integration/nano/test_sign_sc_rollup_execute_outbox_message.py +++ b/tests/integration/nano/test_sign_sc_rollup_execute_outbox_message.py @@ -13,24 +13,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing smart rollup execute outbox message""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): SR: execute outbox message -# Fee: 0.01 XTZ -# Storage limit: 4 -# Rollup: sr163Lv22CdE8QagCwf48PWDTquk6isQwv57 -# Commitment: src12UJzB8mg7yU6nWPzicH7ofJbFjyJEbHvwtZdfRXi8DQHNp1LY8 -# Output proof: 396630396632393532643334353238633733336639343631356366633339626335353536313966633535306464346136376261323230386365386538363761613364313361366566393964666265333263363937346161396132313530643231656361323963333334396535396331336239303831663163313162343430616334643334353564656462653465653064653135613861663632306434633836323437643964313332646531626236646132336435666639643864666664613232626139613834 +from utils.app import TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import ScRollupExecuteOutboxMessage -def test_sign_sc_rollup_execute_outbox_message(app): +def test_sign_sc_rollup_execute_outbox_message(app: TezosAppScreen): + """Check signing smart rollup execute outbox message""" test_name = Path(__file__).stem app.setup_expert_mode() - message = Message.from_bytes("030000000000000000000000000000000000000000000000000000000000000000ce00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e02030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c6396630396632393532643334353238633733336639343631356366633339626335353536313966633535306464346136376261323230386365386538363761613364313361366566393964666265333263363937346161396132313530643231656361323963333334396535396331336239303831663163313162343430616334643334353564656462653465653064653135613861663632306434633836323437643964313332646531626236646132336435666639643864666664613232626139613834") + message = ScRollupExecuteOutboxMessage( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + rollup = 'sr163Lv22CdE8QagCwf48PWDTquk6isQwv57', + cemented_commitment = 'src12UJzB8mg7yU6nWPzicH7ofJbFjyJEbHvwtZdfRXi8DQHNp1LY8', + output_proof = b'9f09f2952d34528c733f94615cfc39bc555619fc550dd4a67ba2208ce8e867aa3d13a6ef99dfbe32c6974aa9a2150d21eca29c3349e59c13b9081f1c11b440ac4d3455dedbe4ee0de15a8af620d4c86247d9d132de1bb6da23d5ff9d8dffda22ba9a84' + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_sc_rollup_originate.py b/tests/integration/nano/test_sign_sc_rollup_originate.py index dfe6f40fa..f10eacdda 100755 --- a/tests/integration/nano/test_sign_sc_rollup_originate.py +++ b/tests/integration/nano/test_sign_sc_rollup_originate.py @@ -13,31 +13,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing smart rollup originate""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path +from typing import List, Optional -# Operation (0): SR: originate -# Fee: 0.01 XTZ -# Storage limit: 4 -# Kind: arith -# Kernel: 396630396632393532643334353238633733336639343631356366633339626335353536313966633535306464346136376261323230386365386538363761613364313361366566393964666265333263363937346161396132313530643231656361323963333334396535396331336239303831663163313162343430616334643334353564656462653465653064653135613861663632306434633836323437643964313332646531626236646132336435666639643864666664613232626139613834 -# Parameters: Pair "1" 2 -# Whitelist (0): tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Whitelist (1): tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W -# Whitelist (2): tz3XMQscBFM9vPmpbYMavMmwxRMUWvWGZMQQ +from utils.app import TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import ScRollupOriginate -def test_sign_sc_rollup_originate(app): +def test_sign_sc_rollup_originate(app: TezosAppScreen): + """Check signing smart rollup originate""" test_name = Path(__file__).stem app.setup_expert_mode() - sc_rollup_originate_with_missing_white_list = "030000000000000000000000000000000000000000000000000000000000000000c800ffdd6102321bc251e4a5190ad5b12b251069d9b4904e02030400000000c63966303966323935326433343532386337333366393436313563666333396263353535363139666335353064643461363762613232303863653865383637616133643133613665663939646662653332633639373461613961323135306432316563613239633333343965353963313362393038316631633131623434306163346433343535646564626534656530646531356138616636323064346338363234376439643133326465316262366461323364356666396438646666646132326261396138340000000a07070100000001310002" - - def check_sign(name: str, whitelist: bytes): + def check_sign(name: str, whitelist: Optional[List[str]]): - message = Message.from_bytes(sc_rollup_originate_with_missing_white_list + whitelist) + message = ScRollupOriginate( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + pvm_kind = "arith", + kernel = '396630396632393532643334353238633733336639343631356366633339626335353536313966633535306464346136376261323230386365386538363761613364313361366566393964666265333263363937346161396132313530643231656361323963333334396535396331336239303831663163313162343430616334643334353564656462653465653064653135613861663632306434633836323437643964313332646531626236646132336435666639643864666664613232626139613834', + parameters_ty = {'prim': 'Pair', 'args': [{'string': '1'}, {'int': 2}]}, + whitelist = whitelist + ) data = app.sign(DEFAULT_ACCOUNT, message, @@ -50,17 +52,12 @@ def check_sign(name: str, whitelist: bytes): with_hash=True, data=data) - # None - check_sign("no_whitelist", "00") - - # Some [] - check_sign("no_whitelist", "ff00000000") - - # Some [ - # tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa; - # tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W; - # tz3XMQscBFM9vPmpbYMavMmwxRMUWvWGZMQQ - # ] - check_sign("with_whitelist", "ff0000003f00ffdd6102321bc251e4a5190ad5b12b251069d9b401f6552df4f5ff51c3d13347cab045cfdb8b9bd8030278eb8b6ab9a768579cd5146b480789650c83f28e") + check_sign("no_whitelist", None) + check_sign("no_whitelist", []) + check_sign("with_whitelist", [ + 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + 'tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W', + 'tz3XMQscBFM9vPmpbYMavMmwxRMUWvWGZMQQ' + ]) app.quit() diff --git a/tests/integration/nano/test_sign_set_consensus_key.py b/tests/integration/nano/test_sign_set_consensus_key.py index 684e8f036..d5bf5be6c 100755 --- a/tests/integration/nano/test_sign_set_consensus_key.py +++ b/tests/integration/nano/test_sign_set_consensus_key.py @@ -13,22 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing set consensus key""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Set consensus key -# Fee: 0.01 XTZ -# Storage limit: 4 -# Public key: edpkuXX2VdkdXzkN11oLCb8Aurdo1BTAtQiK8ZY9UPj2YMt3AHEpcY +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import UpdateConsensusKey -def test_sign_set_consensus_key(app): +def test_sign_set_consensus_key(app: TezosAppScreen): + """Check signing set consensus key""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000007200c921d4487c90b4472da6cc566a58d79f0d991dbf904e02030400747884d9abdf16b3ab745158925f567e222f71225501826fa83347f6cbe9c393") + message = UpdateConsensusKey( + source = 'tz1dyX3B1CFYa2DfdFLyPtiJCfQRUgPVME6E', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + pk = "edpkuXX2VdkdXzkN11oLCb8Aurdo1BTAtQiK8ZY9UPj2YMt3AHEpcY" + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_set_deposit_limit.py b/tests/integration/nano/test_sign_set_deposit_limit.py index 97ca4ddae..4b2943f5c 100755 --- a/tests/integration/nano/test_sign_set_deposit_limit.py +++ b/tests/integration/nano/test_sign_set_deposit_limit.py @@ -13,22 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing set deposit limit""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Set deposit limit -# Fee: 0.06 XTZ -# Storage limit: 4 -# Staking limit: 0.02 XTZ +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import SetDepositLimit -def test_sign_set_deposit_limit(app): +def test_sign_set_deposit_limit(app: TezosAppScreen): + """Check signing set deposit limit""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) - message = Message.from_bytes("03000000000000000000000000000000000000000000000000000000000000000070027c252d3806e6519ed064026bdb98edf866117331e0d40304f80204ffa09c01") + message = SetDepositLimit( + source = 'tz3XeTwXXJeWNgVR3LqMcyBDdnxjbZ7TeEGH', + fee = 60000, + counter = 4, + gas_limit = 376, + storage_limit = 4, + limit = 20000 + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_too_long_operation.py b/tests/integration/nano/test_sign_too_long_operation.py index d19261707..a884081ce 100644 --- a/tests/integration/nano/test_sign_too_long_operation.py +++ b/tests/integration/nano/test_sign_too_long_operation.py @@ -13,22 +13,34 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Check signing too long operation behaviour""" + from pathlib import Path from typing import Callable -from utils.app import Screen, Screen_text, DEFAULT_ACCOUNT -from utils.message import Message +from utils.app import ScreenText, TezosAppScreen, DEFAULT_ACCOUNT from utils.backend import StatusCode +from utils.message import ( + Message, + Proposals, + OperationGroup, + Reveal, + Transaction, + Delegation, + RegisterGlobalConstant, + SetDepositLimit, + ScRollupAddMessage +) test_path = Path(Path(__file__).stem) -def _sign_too_long(app, msg: str, navigate: Callable[[], None]): +def _sign_too_long(app: TezosAppScreen, + message: Message, + navigate: Callable[[], None]): app.setup_expert_mode() app.setup_blindsign_on() - message = Message.from_bytes(msg) - data = app._sign( DEFAULT_ACCOUNT, message, @@ -43,25 +55,27 @@ def _sign_too_long(app, msg: str, navigate: Callable[[], None]): app.quit() -def _sign_decodable_too_long(app, msg: str, path: Path): +def _sign_decodable_too_long(app: TezosAppScreen, + message: Message, + path: Path): + """Sign a decodable too long message""" def navigate() -> None: - app.navigate_until_text(Screen_text.Accept_risk, path / "clear_n_too_long_warning") - app.navigate_until_text(Screen_text.Sign_accept, path / "summary") + app.navigate_until_text(ScreenText.ACCEPT_RISK, path / "clear_n_too_long_warning") + app.navigate_until_text(ScreenText.SIGN_ACCEPT, path / "summary") - _sign_too_long(app, msg, navigate) + _sign_too_long(app, message, navigate) def _reject_too_long( app, - msg: str, + message: Message, status_code: StatusCode, navigate: Callable[[], None]): + """Reject a too long message""" app.setup_expert_mode() app.setup_blindsign_on() - message = Message.from_bytes(msg) - app._failing_signing( DEFAULT_ACCOUNT, message, @@ -76,213 +90,269 @@ def _reject_too_long( basic_test_path = test_path / "basic" -## Operation (0): Reveal -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 1 XTZ -# Storage limit: 4 -# Public key: p2pk66m3NQsd4n6LJWe9WMwx9WHeXwKmBaMwXX92WkMQCR99zmwk2PM -## Operation (1): Transaction -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 2 XTZ -# Storage limit: 7 -# Amount: 3 XTZ -# Destination: tz3XMQscBFM9vPmpbYMavMmwxRMUWvWGZMQQ -# Entrypoint: update_config -# Parameter: Pair 5 True -## Operation (2): Delegation -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 3 XTZ -# Storage limit: 5 -# Delegate: tz2W3Tvcm64GjcV2bipUynnEsctLFz5Z6yRa -## Operation (3): SR: send messages -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 4 XTZ -# Storage limit: 6 -# Message (0): 6d65737361676530 -# Message (1): 6d65737361676531 -# Message (2): 6d65737361676532 -# Message (3): 6d65737361676533 -# Message (4): 6d65737361676534 -# Message (5): 6d65737361676535 -# Message (6): 6d65737361676536 -# Message (7): 6d65737361676537 -# Message (8): 6d65737361676538 -# Message (9): 6d65737361676539 -# Message (10): 6d6573736167653130 -# Message (11): 6d6573736167653131 -# Message (12): 6d6573736167653132 -# Message (13): 6d6573736167653133 -# Message (14): 6d6573736167653134 -# Message (15): 6d6573736167653135 -# Message (16): 6d6573736167653136 -# Message (17): 6d6573736167653137 -# Message (18): 6d6573736167653138 -# Message (19): 6d6573736167653139 -## Operation (4): Set deposit limit -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 1 XTZ -# Storage limit: 3 -# Staking limit: 10 XTZ -basic_operation = "0300000000000000000000000000000000000000000000000000000000000000006b00ffdd6102321bc251e4a5190ad5b12b251069d9b4c0843d0b0104020320182716513907b6bab33f905396d031931c07e01bddd780780c1a56b9c086da6c00ffdd6102321bc251e4a5190ad5b12b251069d9b480897a0c0107c08db701000278eb8b6ab9a768579cd5146b480789650c83f28effff0d7570646174655f636f6e6669670000000607070005030a6e00ffdd6102321bc251e4a5190ad5b12b251069d9b4c08db7010d0105ff01ee572f02e5be5d097ba17369789582882e8abb87c900ffdd6102321bc251e4a5190ad5b12b251069d9b48092f4010e0106000000fa000000086d65737361676530000000086d65737361676531000000086d65737361676532000000086d65737361676533000000086d65737361676534000000086d65737361676535000000086d65737361676536000000086d65737361676537000000086d65737361676538000000086d65737361676539000000096d6573736167653130000000096d6573736167653131000000096d6573736167653132000000096d6573736167653133000000096d6573736167653134000000096d6573736167653135000000096d6573736167653136000000096d6573736167653137000000096d6573736167653138000000096d65737361676531397000ffdd6102321bc251e4a5190ad5b12b251069d9b4c0843d0f0103ff80ade204" - -def test_sign_basic_too_long_operation(app): - _sign_decodable_too_long(app, basic_operation, basic_test_path / "accept") - -def test_reject_basic_too_long_operation_at_warning(app): +BASIC_OPERATION = OperationGroup([ + Reveal( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 1000000, + counter = 11, + gas_limit = 1, + storage_limit = 4, + public_key = 'p2pk66m3NQsd4n6LJWe9WMwx9WHeXwKmBaMwXX92WkMQCR99zmwk2PM' + ), + Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 2000000, + counter = 12, + gas_limit = 1, + storage_limit = 7, + destination = 'tz3XMQscBFM9vPmpbYMavMmwxRMUWvWGZMQQ', + amount = 3000000, + entrypoint = 'update_config', + parameter = {'prim': 'Pair', 'args': [ {'int': 5}, {'prim': 'True'} ]} + ), + Delegation( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 3000000, + counter = 13, + gas_limit = 1, + storage_limit = 5, + delegate = 'tz2W3Tvcm64GjcV2bipUynnEsctLFz5Z6yRa' + ), + ScRollupAddMessage( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 4000000, + counter = 14, + gas_limit = 1, + storage_limit = 6, + message = [ + bytes.fromhex('6d65737361676530'), + bytes.fromhex('6d65737361676531'), + bytes.fromhex('6d65737361676532'), + bytes.fromhex('6d65737361676533'), + bytes.fromhex('6d65737361676534'), + bytes.fromhex('6d65737361676535'), + bytes.fromhex('6d65737361676536'), + bytes.fromhex('6d65737361676537'), + bytes.fromhex('6d65737361676538'), + bytes.fromhex('6d65737361676539'), + bytes.fromhex('6d6573736167653130'), + bytes.fromhex('6d6573736167653131'), + bytes.fromhex('6d6573736167653132'), + bytes.fromhex('6d6573736167653133'), + bytes.fromhex('6d6573736167653134'), + bytes.fromhex('6d6573736167653135'), + bytes.fromhex('6d6573736167653136'), + bytes.fromhex('6d6573736167653137'), + bytes.fromhex('6d6573736167653138'), + bytes.fromhex('6d6573736167653139') + ] + ), + SetDepositLimit( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 1000000, + counter = 15, + gas_limit = 1, + storage_limit = 3, + limit = 10000000 + ) +]) + +def test_sign_basic_too_long_operation(app: TezosAppScreen): + """Check sign too long operation""" + _sign_decodable_too_long(app, BASIC_OPERATION, basic_test_path / "accept") + +def test_reject_basic_too_long_operation_at_warning(app: TezosAppScreen): + """Check reject too long operation at warning""" path = basic_test_path / "reject_at_too_large_warning" def navigate() -> None: - app.navigate_until_text(Screen_text.Sign_reject, path / "clear_n_too_long_warning") + app.navigate_until_text(ScreenText.SIGN_REJECT, path / "clear_n_too_long_warning") - _reject_too_long(app, basic_operation, StatusCode.REJECT, navigate) + _reject_too_long(app, BASIC_OPERATION, StatusCode.REJECT, navigate) -def test_reject_basic_too_long_operation_at_summary(app): +def test_reject_basic_too_long_operation_at_summary(app: TezosAppScreen): + """Check reject too long operation at summary""" path = basic_test_path / "reject_at_summary" def navigate() -> None: - app.navigate_until_text(Screen_text.Accept_risk, path / "clear_n_too_long_warning") - app.navigate_until_text(Screen_text.Sign_reject, path / "summary") + app.navigate_until_text(ScreenText.ACCEPT_RISK, path / "clear_n_too_long_warning") + app.navigate_until_text(ScreenText.SIGN_REJECT, path / "summary") - _reject_too_long(app, basic_operation, StatusCode.REJECT, navigate) + _reject_too_long(app, BASIC_OPERATION, StatusCode.REJECT, navigate) ### Different kind of too long operation ### -## Operation (0): Transaction -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 0 XTZ -# Storage limit: 0 -# Amount: 10 XTZ -# Destination: tz1er74kx433vTtpYddGsf3dDt5piBZeeHyQ -## Operation (1): Transaction -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 1 XTZ -# Storage limit: 1 -# Amount: 1 XTZ -# Destination: tz2PPZ2WN4j92Rdx4NM7oW3HAp3x825HUyac -## Operation (2): Transaction -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 2 XTZ -# Storage limit: 2 -# Amount: 2 XTZ -# Destination: tz1Kp8NCAN5WWwvkWkMmQQXMRe68iURmoQ8w -## Operation (3): Transaction -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 3 XTZ -# Storage limit: 3 -# Amount: 3 XTZ -# Destination: tz3fLwHKthqhTPK6Lar6CTXN1WbDETw1YpGB -## Operation (4): Transaction -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 4 XTZ -# Storage limit: 4 -# Amount: 4 XTZ -# Destination: tz3eydffbLkjdVb8zx42BvxpGV87zaRnqL3r -## Operation (5): Transaction -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 5 XTZ -# Storage limit: 5 -# Amount: 5 XTZ -# Destination: tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu -def test_sign_too_long_operation_with_only_transactions(app): - msg="0300000000000000000000000000000000000000000000000000000000000000006c00ffdd6102321bc251e4a5190ad5b12b251069d9b4000b010080ade2040000d2b3082b0fe03f0f7f39915cdba50e9d9b8ab057006c00ffdd6102321bc251e4a5190ad5b12b251069d9b4c0843d0c0101c0843d0001a55ae1858c1201514c97aa9122e77d3c4197a714006c00ffdd6102321bc251e4a5190ad5b12b251069d9b480897a0d010280897a000001e8e5519a315280a374c8765107979a6049de27006c00ffdd6102321bc251e4a5190ad5b12b251069d9b4c08db7010e0103c08db7010002d09584de879c4bbd3f494ed01b82e06a81e8e176006c00ffdd6102321bc251e4a5190ad5b12b251069d9b48092f4010f01048092f4010002cc8e146741cf31fc00123b8c26baf95c57421a3c006c00ffdd6102321bc251e4a5190ad5b12b251069d9b4c096b102100105c096b10200016e8874874d31c3fbd636e924d5a036a43ec8faa700" - _sign_decodable_too_long(app, msg, test_path / "only_transactions") - -## Operation (0): Proposals -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Period: 32 -# Proposal (0): ProtoDemoNoopsDemoNoopsDemoNoopsDemoNoopsDemo6XBoYp -# Proposal (1): ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im -# Proposal (2): PrihK96nBAFSxVL1GLJTVhu9YnzkMFiBeuJRPA8NwuZVZCE1L6i -# Proposal (3): Ps9mPmXaRzmzk35gbAYNCAw6UXdE2qoABTHbN2oEEc1qM7CwT9P -# Proposal (4): PsBabyM1eUXZseaJdmXFApDSBqj8YBfwELoxZHHW77EMcAbbwAS -# Proposal (5): PsCARTHAGazKbHtnKfLzQg3kms52kSRpgnDY982a9oYsSXRLQEb -# Proposal (6): PsDELPH1Kxsxt8f9eWbxQeRxkjfbxoqM52jvs5Y5fBxWWh4ifpo -# Proposal (7): PtEdo2ZkT9oKpimTah6x2embF25oss54njMuPzkJTEi5RqfdZFA -# Proposal (8): PsFLorenaUUuikDWvMDr6fGBRG8kt3e3D3fHoXK1j1BFRxeSH4i -# Proposal (9): PtGRANADsDU8R9daYKAgWnQYAJ64omN1o3KMGVCykShA97vQbvV -# Proposal (10): PtHangz2aRngywmSRGGvrcTyMbbdpWdpFKuS4uMWxg2RaH9i1qx -# Proposal (11): Psithaca2MLRFYargivpo7YvUr7wUDqyxrdhC5CQq78mRvimz6A -# Proposal (12): PtJakart2xVj7pYXJBXrqHgd82rdkLey5ZeeGwDgPp9rhQUbSqY -# Proposal (13): PtKathmankSpLLDALzWw7CGD2j2MtyveTwboEYokqUCP4a1LxMg -# Proposal (14): PtLimaPtLMwfNinJi9rCfDPWea8dFgTZ1MeJ9f1m2SRic6ayiwW -# Proposal (15): PtMumbai2TmsJHNGRkD8v8YDbtao7BLUC3wjASn1inAKLFCjaH1 -# Proposal (16): PtNairobiyssHuh87hEhfVBGCVrK3WnS8Z2FT4ymB5tAa4r1nQf -# Proposal (17): ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH -# Proposal (18): PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ -# Proposal (19): ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK -def test_sign_too_long_operation_without_fee_or_amount(app): - msg="0300000000000000000000000000000000000000000000000000000000000000000500ffdd6102321bc251e4a5190ad5b12b251069d9b400000020000002800bcd7db2d718ba94e85bd262681049852e1f58512aa552124330d657845c73b70bcd7ffca03f57e38453f0d3e84c302403c05357448b4c2daef8b3a8be3c69c1000000000000000000000000000000000000000000000000000000000000000038ecdef0cd08640f318a9b055f6b0d0c9ae030913a871d9b9d86fb846317da213d0b4bacb5c3e152a167da26fefc266bd3a0e14fc4e41e6c53623bf482833da23e5e3a606afab74a59ca09e333633e2770b6492c5e594455b71e9a2f0ea92afb40cab83d3f37a64da26b57ad3d0432ae881293a25169ada387bfc74a1cbf9e6ec7ad4f7a000e28e9eefc58de8ea1172de843242bd2e688779953d3416a44640b4596285c6871691e25196c6a8d26d90a3ac91375731e3926103c517a13a0ba56cbb944f74244ea2681981f25995f8ebba8ff6cee8c036892fe901cb760c4e39ece5f061e34b5a21feab8dbdfe755ef17e70c9f565464f067ac5e7c02be830a488424520cf9bbf0a42770204d95dcc1f11e404fdb3e90c84850c4cfdb50c5c4b9d0a3f07b8adfcf61f5ca60f244ca9a876e76cbad9140980f6c88d0bf900ac6d8d2ea9f23a1a1011091841b12e32ce2f8c3facff27feee58bb7c9e90567d11425d57ed88be5a69815e39386a33f7dcad391f5f507e03b376e499272c86c6cf2a7d8325f11da2ff36934a586439f085655a833f2ff6a12d15e83b951fb684326e0d9b8c2314cc05ffa3fc655a98bb87155be4cf7ce67fee6b594ea9302e8655df20bf44c7d64e3d7da27d925d10af535fb36cef0ad41930c7929773f4731eba137dbff6586a04802d3f513c65a444d9d4debe42b17e9e7273f8f6c118ea3f4e06e0bcd7b2cadcd87ecb0d5c50330fb59feed7432bffecede8a09a2b86d1527c6a5" - _sign_decodable_too_long(app, msg, test_path / "without_fee_or_amount") +def test_sign_too_long_operation_with_only_transactions(app: TezosAppScreen): + """Check sign too long operation that contains only transaction""" + message = OperationGroup([ + Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 0, + counter = 11, + gas_limit = 1, + storage_limit = 0, + destination = 'tz1er74kx433vTtpYddGsf3dDt5piBZeeHyQ', + amount = 10000000 + ), + Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 1000000, + counter = 12, + gas_limit = 1, + storage_limit = 1, + destination = 'tz2PPZ2WN4j92Rdx4NM7oW3HAp3x825HUyac', + amount = 1000000 + ), + Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 2000000, + counter = 13, + gas_limit = 1, + storage_limit = 2, + destination = 'tz1Kp8NCAN5WWwvkWkMmQQXMRe68iURmoQ8w', + amount = 2000000 + ), + Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 3000000, + counter = 14, + gas_limit = 1, + storage_limit = 3, + destination = 'tz3fLwHKthqhTPK6Lar6CTXN1WbDETw1YpGB', + amount = 3000000 + ), + Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 4000000, + counter = 15, + gas_limit = 1, + storage_limit = 4, + destination = 'tz3eydffbLkjdVb8zx42BvxpGV87zaRnqL3r', + amount = 4000000 + ), + Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 5000000, + counter = 16, + gas_limit = 1, + storage_limit = 5, + destination = 'tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu', + amount = 5000000 + ) + ]) + _sign_decodable_too_long(app, message, test_path / "only_transactions") + +def test_sign_too_long_operation_without_fee_or_amount(app: TezosAppScreen): + """Check sign too long operation that doesn't have fees or amount""" + message = Proposals( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + proposals = [ + 'ProtoDemoNoopsDemoNoopsDemoNoopsDemoNoopsDemo6XBoYp', + 'ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im', + 'PrihK96nBAFSxVL1GLJTVhu9YnzkMFiBeuJRPA8NwuZVZCE1L6i', + 'Ps9mPmXaRzmzk35gbAYNCAw6UXdE2qoABTHbN2oEEc1qM7CwT9P', + 'PsBabyM1eUXZseaJdmXFApDSBqj8YBfwELoxZHHW77EMcAbbwAS', + 'PsCARTHAGazKbHtnKfLzQg3kms52kSRpgnDY982a9oYsSXRLQEb', + 'PsDELPH1Kxsxt8f9eWbxQeRxkjfbxoqM52jvs5Y5fBxWWh4ifpo', + 'PtEdo2ZkT9oKpimTah6x2embF25oss54njMuPzkJTEi5RqfdZFA', + 'PsFLorenaUUuikDWvMDr6fGBRG8kt3e3D3fHoXK1j1BFRxeSH4i', + 'PtGRANADsDU8R9daYKAgWnQYAJ64omN1o3KMGVCykShA97vQbvV', + 'PtHangz2aRngywmSRGGvrcTyMbbdpWdpFKuS4uMWxg2RaH9i1qx', + 'Psithaca2MLRFYargivpo7YvUr7wUDqyxrdhC5CQq78mRvimz6A', + 'PtJakart2xVj7pYXJBXrqHgd82rdkLey5ZeeGwDgPp9rhQUbSqY', + 'PtKathmankSpLLDALzWw7CGD2j2MtyveTwboEYokqUCP4a1LxMg', + 'PtLimaPtLMwfNinJi9rCfDPWea8dFgTZ1MeJ9f1m2SRic6ayiwW', + 'PtMumbai2TmsJHNGRkD8v8YDbtao7BLUC3wjASn1inAKLFCjaH1', + 'PtNairobiyssHuh87hEhfVBGCVrK3WnS8Z2FT4ymB5tAa4r1nQf', + 'ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH', + 'PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ', + 'ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK' + ], + period = 32 + ) + _sign_decodable_too_long(app, message, test_path / "without_fee_or_amount") ### Too long operation containing a too large number ### too_large_test_path = test_path / "too_large" -# Operation (0): SR: send messages -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 4 XTZ -# Storage limit: 6 -# Message (0): 6d65737361676530 -# Message (1): 6d65737361676531 -# Message (2): 6d65737361676532 -# Message (3): 6d65737361676533 -# Message (4): 6d65737361676534 -# Message (5): 6d65737361676535 -# Message (6): 6d65737361676536 -# Message (7): 6d65737361676537 -# Message (8): 6d65737361676538 -# Message (9): 6d65737361676539 -# Message (10): 6d6573736167653130 -# Message (11): 6d6573736167653131 -# Message (12): 6d6573736167653132 -# Message (13): 6d6573736167653133 -# Message (14): 6d6573736167653134 -# Message (15): 6d6573736167653135 -# Message (16): 6d6573736167653136 -# Message (17): 6d6573736167653137 -# Message (18): 6d6573736167653138 -# Message (19): 6d6573736167653139 -# Message (20): 6d6573736167653230 -# Message (21): 6d6573736167653231 -# Message (22): 6d6573736167653232 -# Message (23): 6d6573736167653233 -# Message (24): 6d6573736167653234 -# Message (25): 6d6573736167653235 -# Message (26): 6d6573736167653236 -# Message (27): 6d6573736167653237 -# Message (28): 6d6573736167653238 -# Message (29): 6d6573736167653239 -## Operation (1): Register global constant -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 5 XTZ -# Storage limit: 3 -# Value: 115792089237316195423570985008687907853269984665640564039457584007913129639936 -operation_with_too_large = "030000000000000000000000000000000000000000000000000000000000000000c900ffdd6102321bc251e4a5190ad5b12b251069d9b48092f4010b01060000017c000000086d65737361676530000000086d65737361676531000000086d65737361676532000000086d65737361676533000000086d65737361676534000000086d65737361676535000000086d65737361676536000000086d65737361676537000000086d65737361676538000000086d65737361676539000000096d6573736167653130000000096d6573736167653131000000096d6573736167653132000000096d6573736167653133000000096d6573736167653134000000096d6573736167653135000000096d6573736167653136000000096d6573736167653137000000096d6573736167653138000000096d6573736167653139000000096d6573736167653230000000096d6573736167653231000000096d6573736167653232000000096d6573736167653233000000096d6573736167653234000000096d6573736167653235000000096d6573736167653236000000096d6573736167653237000000096d6573736167653238000000096d65737361676532396f00ffdd6102321bc251e4a5190ad5b12b251069d9b4c096b1020c0103000000260080808080808080808080808080808080808080808080808080808080808080808080808020" - -def test_sign_too_long_operation_with_too_large(app): +OPERATION_WITH_TOO_LARGE = OperationGroup([ + ScRollupAddMessage( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 4000000, + counter = 11, + gas_limit = 1, + storage_limit = 6, + message = [ + bytes.fromhex('6d65737361676530'), + bytes.fromhex('6d65737361676531'), + bytes.fromhex('6d65737361676532'), + bytes.fromhex('6d65737361676533'), + bytes.fromhex('6d65737361676534'), + bytes.fromhex('6d65737361676535'), + bytes.fromhex('6d65737361676536'), + bytes.fromhex('6d65737361676537'), + bytes.fromhex('6d65737361676538'), + bytes.fromhex('6d65737361676539'), + bytes.fromhex('6d6573736167653130'), + bytes.fromhex('6d6573736167653131'), + bytes.fromhex('6d6573736167653132'), + bytes.fromhex('6d6573736167653133'), + bytes.fromhex('6d6573736167653134'), + bytes.fromhex('6d6573736167653135'), + bytes.fromhex('6d6573736167653136'), + bytes.fromhex('6d6573736167653137'), + bytes.fromhex('6d6573736167653138'), + bytes.fromhex('6d6573736167653139'), + bytes.fromhex('6d6573736167653230'), + bytes.fromhex('6d6573736167653231'), + bytes.fromhex('6d6573736167653232'), + bytes.fromhex('6d6573736167653233'), + bytes.fromhex('6d6573736167653234'), + bytes.fromhex('6d6573736167653235'), + bytes.fromhex('6d6573736167653236'), + bytes.fromhex('6d6573736167653237'), + bytes.fromhex('6d6573736167653238'), + bytes.fromhex('6d6573736167653239') + ] + ), + RegisterGlobalConstant( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 5000000, + counter = 12, + gas_limit = 1, + storage_limit = 3, + value = {'int': 115792089237316195423570985008687907853269984665640564039457584007913129639936} + ) +]) + +def test_sign_too_long_operation_with_too_large(app: TezosAppScreen): + """Check sign too long operation that will also fail the parsing""" path = too_large_test_path / "accept" def navigate() -> None: - app.navigate_until_text(Screen_text.Accept_risk, path / "clear_n_too_large_warning") - app.navigate_until_text(Screen_text.Sign_accept, path / "blindsigning") + app.navigate_until_text(ScreenText.ACCEPT_RISK, path / "clear_n_too_large_warning") + app.navigate_until_text(ScreenText.SIGN_ACCEPT, path / "blindsigning") - _sign_too_long(app, operation_with_too_large, navigate) + _sign_too_long(app, OPERATION_WITH_TOO_LARGE, navigate) -def test_reject_too_long_operation_with_too_large_at_too_large_warning(app): +def test_reject_too_long_operation_with_too_large_at_too_large_warning(app: TezosAppScreen): + """Check reject too long operation that will also fail the parsing at too large warning""" path = too_large_test_path / "reject_at_too_large_warning" def navigate() -> None: - app.navigate_until_text(Screen_text.Sign_reject, path / "clear_n_too_large_warning") + app.navigate_until_text(ScreenText.SIGN_REJECT, path / "clear_n_too_large_warning") - _reject_too_long(app, operation_with_too_large, StatusCode.PARSE_ERROR, navigate) + _reject_too_long(app, OPERATION_WITH_TOO_LARGE, StatusCode.PARSE_ERROR, navigate) -def test_reject_too_long_operation_with_too_large_at_blindsigning(app): +def test_reject_too_long_operation_with_too_large_at_blindsigning(app: TezosAppScreen): + """Check reject too long operation that will also fail the parsing at blindsigning""" path = too_large_test_path / "reject_at_blindsigning" def navigate() -> None: - app.navigate_until_text(Screen_text.Accept_risk, path / "clear_n_too_large_warning") - app.navigate_until_text(Screen_text.Sign_reject, path / "blindsigning") + app.navigate_until_text(ScreenText.ACCEPT_RISK, path / "clear_n_too_large_warning") + app.navigate_until_text(ScreenText.SIGN_REJECT, path / "blindsigning") - _reject_too_long(app, operation_with_too_large, StatusCode.REJECT, navigate) + _reject_too_long(app, OPERATION_WITH_TOO_LARGE, StatusCode.REJECT, navigate) diff --git a/tests/integration/nano/test_sign_transaction.py b/tests/integration/nano/test_sign_transaction.py index a62c42b2e..787c6ed8d 100755 --- a/tests/integration/nano/test_sign_transaction.py +++ b/tests/integration/nano/test_sign_transaction.py @@ -13,28 +13,33 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Check signing transaction""" + from pathlib import Path -from utils.app import Screen, Screen_text, DEFAULT_ACCOUNT +from utils.app import Screen, ScreenText, TezosAppScreen, DEFAULT_ACCOUNT from utils.backend import StatusCode -from utils.message import Message +from utils.message import Transaction test_path = Path(Path(__file__).stem) -# Operation (0): Transaction -# Source: tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu -# Fee: 0.05 XTZ -# Storage limit: 45 -# Amount: 0.24 XTZ -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT -# Entrypoint: do -# Parameter: CAR -def test_sign_transaction(app): +def test_sign_transaction(app: TezosAppScreen): + """Check signing transaction""" path = test_path / "basic" app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316") + message = Transaction( + source = 'tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu', + fee = 50000, + counter = 8, + gas_limit = 54, + storage_limit = 45, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + amount = 240000, + entrypoint = 'do', + parameter = {'prim': 'CAR'} + ) data = app.sign(DEFAULT_ACCOUNT, message, @@ -49,20 +54,23 @@ def test_sign_transaction(app): app.quit() -# Operation (0): Transaction -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 0.01 XTZ -# Storage limit: 4 -# Amount: 0 XTZ -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT -# Entrypoint: root -# Data: {pair "[" (pair "Z" (pair "Y" (pair "X" (pair "W" (pair "V" (pair "U" (pair "T" (pair "S" (pair "R" (pair "Q" (pair "P" (pair "O" (pair "N" (pair "M" (pair "L" (pair "K" (pair "J" (pair "I" (pair "H" (pair "G" (pair "F" (pair "E" (pair "D" (pair "C" (pair "B" {})))))))))))))))))))))))));pair 10 (pair 9 (pair 8 (pair 7 (pair 6 (pair 5 (pair 4 (pair 3 (pair 2 (pair 1 {})))))))))} -def test_reject_transaction(app): +def test_reject_transaction(app: TezosAppScreen): + """Check reject transaction""" path = test_path / "reject" app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e0203040001000000000000000000000000000000000000000000ff01000001070200000102076501000000015b076501000000015a0765010000000159076501000000015807650100000001570765010000000156076501000000015507650100000001540765010000000153076501000000015207650100000001510765010000000150076501000000014f076501000000014e076501000000014d076501000000014c076501000000014b076501000000014a0765010000000149076501000000014807650100000001470765010000000146076501000000014507650100000001440765010000000143076501000000014202000000000765000a0765000907650008076500070765000607650005076500040765000307650002076500010200000000") + message = Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + amount = 0, + entrypoint = 'root', + parameter = [{'prim':'pair','args':[{'string':"["},{'prim':'pair','args':[{'string':"Z"},{'prim':'pair','args':[{'string':"Y"},{'prim':'pair','args':[{'string':"X"},{'prim':'pair','args':[{'string':"W"},{'prim':'pair','args':[{'string':"V"},{'prim':'pair','args':[{'string':"U"},{'prim':'pair','args':[{'string':"T"},{'prim':'pair','args':[{'string':"S"},{'prim':'pair','args':[{'string':"R"},{'prim':'pair','args':[{'string':"Q"},{'prim':'pair','args':[{'string':"P"},{'prim':'pair','args':[{'string':"O"},{'prim':'pair','args':[{'string':"N"},{'prim':'pair','args':[{'string':"M"},{'prim':'pair','args':[{'string':"L"},{'prim':'pair','args':[{'string':"K"},{'prim':'pair','args':[{'string':"J"},{'prim':'pair','args':[{'string':"I"},{'prim':'pair','args':[{'string':"H"},{'prim':'pair','args':[{'string':"G"},{'prim':'pair','args':[{'string':"F"},{'prim':'pair','args':[{'string':"E"},{'prim':'pair','args':[{'string':"D"},{'prim':'pair','args':[{'string':"C"},{'prim':'pair','args':[{'string':"B"},[]]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]},{'prim':'pair','args':[{'int':10},{'prim':'pair','args':[{'int':9},{'prim':'pair','args':[{'int':8},{'prim':'pair','args':[{'int':7},{'prim':'pair','args':[{'int':6},{'prim':'pair','args':[{'int':5},{'prim':'pair','args':[{'int':4},{'prim':'pair','args':[{'int':3},{'prim':'pair','args':[{'int':2},{'prim':'pair','args':[{'int':1},[]]}]}]}]}]}]}]}]}]}]}] + ) app.reject_signing(DEFAULT_ACCOUNT, message, @@ -71,18 +79,21 @@ def test_reject_transaction(app): app.quit() -# Operation (0): Transaction -# Source: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Fee: 0.5 XTZ -# Storage limit: 4 -# Amount: 0.01 XTZ -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT -def test_sign_simple_transaction(app): +def test_sign_simple_transaction(app: TezosAppScreen): + """Check sign not complex transaction""" path = test_path / "simple" app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c00ffdd6102321bc251e4a5190ad5b12b251069d9b4a0c21e020304904e0100000000000000000000000000000000000000000000") + message = Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 500000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + amount = 10000 + ) data = app.sign(DEFAULT_ACCOUNT, message, @@ -97,44 +108,49 @@ def test_sign_simple_transaction(app): app.quit() -# Operation (0): Transaction -# Source: tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu -# Fee: 0.05 XTZ -# Storage limit: 45 -# Amount: 0.24 XTZ -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT -# Entrypoint: do -# Parameter: CAR -def test_too_complex_transaction(app): +def test_too_complex_transaction(app: TezosAppScreen): + """Check sign complex transaction""" path = test_path / "complex" - app.assert_screen(Screen.Home) - - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316") + app.assert_screen(Screen.HOME) + + message = Transaction( + source = 'tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu', + fee = 50000, + counter = 8, + gas_limit = 54, + storage_limit = 45, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + amount = 240000, + entrypoint = 'do', + parameter = {'prim': 'CAR'} + ) app._failing_signing(DEFAULT_ACCOUNT, message, with_hash=True, navigate=(lambda: app.navigate_until_text( - Screen_text.Back_home, + ScreenText.BACK_HOME, path)), status_code=StatusCode.REJECT) app.quit() -# Operation (0): Transaction -# Source: tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W -# Fee: 0.04 XTZ -# Storage limit: 2 -# Amount: 1000 XTZ -# Destination: tz2CJBeWWLsUDjVUDqGZL6od3DeBCNzYXrXk -# Entrypoint: stake -# Parameter: Unit -def test_sign_stake_transaction(app): +def test_sign_stake_transaction(app: TezosAppScreen): + """Check sign stake""" path = test_path / "stake" app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c01f6552df4f5ff51c3d13347cab045cfdb8b9bd803c0b8020031028094ebdc0300012bad922d045c068660fabe19576f8506a1fa8fa3ff0600000002030b") + message = Transaction( + source = 'tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W', + fee = 40000, + counter = 0, + gas_limit = 49, + storage_limit = 2, + destination = 'tz2CJBeWWLsUDjVUDqGZL6od3DeBCNzYXrXk', + amount = 1000000000, + entrypoint = 'stake', + ) data = app.sign(DEFAULT_ACCOUNT, message, @@ -149,20 +165,22 @@ def test_sign_stake_transaction(app): app.quit() -# Operation (0): Transaction -# Source: tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W -# Fee: 0.04 XTZ -# Storage limit: 2 -# Amount: 500 XTZ -# Destination: tz2CJBeWWLsUDjVUDqGZL6od3DeBCNzYXrXk -# Entrypoint: unstake -# Parameter: Unit -def test_sign_unstake_transaction(app): +def test_sign_unstake_transaction(app: TezosAppScreen): + """Check sign unstake""" path = test_path / "unstake" app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c01f6552df4f5ff51c3d13347cab045cfdb8b9bd803c0b80200310280cab5ee0100012bad922d045c068660fabe19576f8506a1fa8fa3ff0700000002030b") + message = Transaction( + source = 'tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W', + fee = 40000, + counter = 0, + gas_limit = 49, + storage_limit = 2, + destination = 'tz2CJBeWWLsUDjVUDqGZL6od3DeBCNzYXrXk', + amount = 500000000, + entrypoint = 'unstake' + ) data = app.sign(DEFAULT_ACCOUNT, message, @@ -177,20 +195,22 @@ def test_sign_unstake_transaction(app): app.quit() -# Operation (0): Transaction -# Source: tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W -# Fee: 0.04 XTZ -# Storage limit: 2 -# Amount: 0 XTZ -# Destination: tz2CJBeWWLsUDjVUDqGZL6od3DeBCNzYXrXk -# Entrypoint: finalize_unstake -# Parameter: Unit -def test_sign_finalize_unstake_transaction(app): +def test_sign_finalize_unstake_transaction(app: TezosAppScreen): + """Check sign finalize_unstake""" path = test_path / "finalize_unstake" app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c01f6552df4f5ff51c3d13347cab045cfdb8b9bd803c0b8020031020000012bad922d045c068660fabe19576f8506a1fa8fa3ff0800000002030b") + message = Transaction( + source = 'tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W', + fee = 40000, + counter = 0, + gas_limit = 49, + storage_limit = 2, + destination = 'tz2CJBeWWLsUDjVUDqGZL6od3DeBCNzYXrXk', + amount = 0, + entrypoint = 'finalize_unstake' + ) data = app.sign(DEFAULT_ACCOUNT, message, @@ -205,20 +225,31 @@ def test_sign_finalize_unstake_transaction(app): app.quit() -# Operation (0): Transaction -# Source: tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W -# Fee: 0.04 XTZ -# Storage limit: 2 -# Amount: 0 XTZ -# Destination: tz2CJBeWWLsUDjVUDqGZL6od3DeBCNzYXrXk -# Entrypoint: set_delegate_parameters -# Parameter: Pair 4000000 (Pair 20000000 Unit) -def test_sign_set_delegate_parameters_transaction(app): +def test_sign_set_delegate_parameters_transaction(app: TezosAppScreen): + """Check sign set delegate parameters""" path = test_path / "delegate_parameters" app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c01f6552df4f5ff51c3d13347cab045cfdb8b9bd803c0b8020031020000012bad922d045c068660fabe19576f8506a1fa8fa3ff090000001007070080a4e80307070080b48913030b") + message = Transaction( + source = 'tz2WmivuMG8MMRKMEmzKRMMxMApxZQWYNS4W', + fee = 40000, + counter = 0, + gas_limit = 49, + storage_limit = 2, + destination = 'tz2CJBeWWLsUDjVUDqGZL6od3DeBCNzYXrXk', + amount = 0, + entrypoint = 'delegate_parameters', + parameter = {'prim': 'Pair', + 'args': [ + {'int': 4000000}, + {'prim': 'Pair', + 'args': [ + {'int': 20000000}, + {'prim': 'Unit'} + ]} + ]} + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_transfer_ticket.py b/tests/integration/nano/test_sign_transfer_ticket.py index 547bc29c9..0712588b1 100755 --- a/tests/integration/nano/test_sign_transfer_ticket.py +++ b/tests/integration/nano/test_sign_transfer_ticket.py @@ -13,27 +13,31 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing transfer ticket""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Transfer ticket -# Fee: 0.01 XTZ -# Storage limit: 4 -# Contents: UNPAIR -# Type: pair "1" 2 -# Ticketer: tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa -# Amount: 1 -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT -# Entrypoint: default +from utils.app import TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import TransferTicket -def test_sign_transfer_ticket(app): +def test_sign_transfer_ticket(app: TezosAppScreen): + """Check signing transfer ticket""" test_name = Path(__file__).stem app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000009e00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e02030400000002037a0000000a076501000000013100020000ffdd6102321bc251e4a5190ad5b12b251069d9b401010000000000000000000000000000000000000000000000000764656661756c74") + message = TransferTicket( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + ticket_contents = {'prim': 'UNPAIR'}, + ticket_ty = {'prim': 'pair', 'args': [{'string': '1'}, {'int': 2}]}, + ticket_ticketer = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + ticket_amount = 1, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT' + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_with_another_seed.py b/tests/integration/nano/test_sign_with_another_seed.py index 793b0a0cb..44cdc39b1 100755 --- a/tests/integration/nano/test_sign_with_another_seed.py +++ b/tests/integration/nano/test_sign_with_another_seed.py @@ -13,33 +13,38 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pytest +"""Check signing using another seed than [zebra*24]""" from pathlib import Path -from utils.account import Account, SIGNATURE_TYPE -from utils.app import Screen -from utils.message import Message +import pytest -# Operation (0): Transaction -# Fee: 0.05 XTZ -# Storage limit: 45 -# Amount: 0.24 XTZ -# Destination: KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT -# Entrypoint: do -# Parameter: CAR +from utils.account import Account, SigType +from utils.app import TezosAppScreen +from utils.message import Transaction @pytest.mark.parametrize("seed", ["around dignity equal spread between young lawsuit interest climb wide that panther rather mom snake scene ecology reunion ice illegal brush"]) -def test_sign_with_another_seed(app): +def test_sign_with_another_seed(app: TezosAppScreen): + """Check signing using another seed than [zebra*24]""" test_name = Path(__file__).stem app.setup_expert_mode() account = Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.ED25519, + SigType.ED25519, "edpkupntwMyERpYniuK1GDWquPaPU1wYsQgMirJPLGmC4Y5dMUsQNo") - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316") + message = Transaction( + source = 'tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu', + fee = 50000, + counter = 8, + gas_limit = 54, + storage_limit = 45, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + amount = 240000, + entrypoint = 'do', + parameter = {'prim': 'CAR'} + ) data = app.sign(account, message, diff --git a/tests/integration/nano/test_sign_with_long_hash.py b/tests/integration/nano/test_sign_with_long_hash.py index b5cfb84f7..7c4540986 100755 --- a/tests/integration/nano/test_sign_with_long_hash.py +++ b/tests/integration/nano/test_sign_with_long_hash.py @@ -13,25 +13,30 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing transaction with a long destination hash""" -from utils.app import Screen, DEFAULT_ACCOUNT -from utils.message import Message +from pathlib import Path -# Operation (0): Transaction -# Fee: 0.01 XTZ -# Storage limit: 4 -# Amount: 0 XTZ -# Destination: KT1GW4QHn66m7WWWMWCMNaWmGYpCRbg5ahwU -# Entrypoint: root -# Parameter: 0 +from utils.app import TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import Transaction -def test_sign_with_long_hash(app): +def test_sign_with_long_hash(app: TezosAppScreen): + """Check signing transaction with a long destination hash""" test_name = Path(__file__).stem app.setup_expert_mode() - message = Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c00ffdd6102321bc251e4a5190ad5b12b251069d9b4904e020304000156dcfb211fa76c525fd7c4566c09a5e3e4d5b81000ff01000000020000") + message = Transaction( + source = 'tz1ixvCiPJYyMjsp2nKBVaq54f6AdbV8hCKa', + fee = 10000, + counter = 2, + gas_limit = 3, + storage_limit = 4, + destination = 'KT1GW4QHn66m7WWWMWCMNaWmGYpCRbg5ahwU', + amount = 0, + entrypoint = 'root', + parameter = {'int': 0} + ) data = app.sign(DEFAULT_ACCOUNT, message, diff --git a/tests/integration/nano/test_sign_with_small_packet.py b/tests/integration/nano/test_sign_with_small_packet.py index eacd9e713..ee02faaca 100755 --- a/tests/integration/nano/test_sign_with_small_packet.py +++ b/tests/integration/nano/test_sign_with_small_packet.py @@ -13,13 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Check signing using small packet instead of full size packets""" + from pathlib import Path from utils.account import Account -from utils.app import send_and_navigate, Screen, Screen_text, DEFAULT_ACCOUNT -from utils.message import Message +from utils.app import send_and_navigate, Screen, ScreenText, TezosAppScreen, DEFAULT_ACCOUNT +from utils.message import Message, Transaction -def test_sign_with_small_packet(app): +def test_sign_with_small_packet(app: TezosAppScreen): + """Check signing using small packet instead of full size packets""" test_name = Path(__file__).stem app.setup_expert_mode() @@ -29,11 +32,11 @@ def check_sign_with_small_packet( message: Message, path: str) -> None: - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) data = send_and_navigate( - send=(lambda: app.backend.sign(account, message, apdu_size=10)), - navigate=(lambda: app.navigate_until_text(Screen_text.Sign_accept, path))) + send=lambda: app.backend.sign(account, message, apdu_size=10), + navigate=lambda: app.navigate_until_text(ScreenText.SIGN_ACCEPT, path)) app.checker.check_signature( account, @@ -41,9 +44,21 @@ def check_sign_with_small_packet( with_hash=False, data=data) + message = Transaction( + source = 'tz2JPgTWZZpxZZLqHMfS69UAy1UHm4Aw5iHu', + fee = 50000, + counter = 8, + gas_limit = 54, + storage_limit = 45, + destination = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT', + amount = 240000, + entrypoint = 'do', + parameter = {'prim': 'CAR'} + ) + check_sign_with_small_packet( account=DEFAULT_ACCOUNT, - message=Message.from_bytes("0300000000000000000000000000000000000000000000000000000000000000006c016e8874874d31c3fbd636e924d5a036a43ec8faa7d0860308362d80d30e01000000000000000000000000000000000000000000ff02000000020316"), + message=message, path=test_name) app.quit() diff --git a/tests/integration/nano/test_tz1_bip25519_sign_micheline_basic.py b/tests/integration/nano/test_tz1_bip25519_sign_micheline_basic.py index f3b59c7c6..f021bb746 100755 --- a/tests/integration/nano/test_tz1_bip25519_sign_micheline_basic.py +++ b/tests/integration/nano/test_tz1_bip25519_sign_micheline_basic.py @@ -13,24 +13,25 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing with bip25519""" -from utils.account import Account, SIGNATURE_TYPE -from utils.app import Screen -from utils.message import Message +from pathlib import Path -# Expression: {"CACA";"POPO";"BOUDIN"} +from utils.account import Account, SigType +from utils.app import Screen, TezosAppScreen +from utils.message import MichelineExpr -def test_tz1_bip25519_sign_micheline_basic(app): +def test_tz1_bip25519_sign_micheline_basic(app: TezosAppScreen): + """Check signing with bip25519""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) account = Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.BIP32_ED25519, + SigType.BIP32_ED25519, "edpkumJgSsSxkpiB5hmTq6eZcrmc6BsJtLAhYceFTiziFqje4mongz") - message = Message.from_bytes("05020000001d0100000004434143410100000004504f504f0100000006424f5544494e") + message = MichelineExpr([{'string': 'CACA'}, {'string': 'POPO'}, {'string': 'BOUDIN'}]) data = app.sign(account, message, diff --git a/tests/integration/nano/test_tz1_ed25519_sign_micheline_basic.py b/tests/integration/nano/test_tz1_ed25519_sign_micheline_basic.py index c377f660d..6bc992ed8 100755 --- a/tests/integration/nano/test_tz1_ed25519_sign_micheline_basic.py +++ b/tests/integration/nano/test_tz1_ed25519_sign_micheline_basic.py @@ -13,24 +13,25 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing with ed25519""" -from utils.account import Account, SIGNATURE_TYPE -from utils.app import Screen -from utils.message import Message +from pathlib import Path -# Expression: {"CACA";"POPO";"BOUDIN"} +from utils.account import Account, SigType +from utils.app import Screen, TezosAppScreen +from utils.message import MichelineExpr -def test_tz1_ed25519_sign_micheline_basic(app): +def test_tz1_ed25519_sign_micheline_basic(app: TezosAppScreen): + """Check signing with ed25519""" test_name = Path(__file__).stem - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) account = Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.ED25519, + SigType.ED25519, "edpkuXX2VdkdXzkN11oLCb8Aurdo1BTAtQiK8ZY9UPj2YMt3AHEpcY") - message = Message.from_bytes("05020000001d0100000004434143410100000004504f504f0100000006424f5544494e") + message = MichelineExpr([{'string': 'CACA'}, {'string': 'POPO'}, {'string': 'BOUDIN'}]) data = app.sign(account, message, diff --git a/tests/integration/nano/test_tz2_sign_micheline_basic.py b/tests/integration/nano/test_tz2_sign_micheline_basic.py index 4d05c0b31..7ca2efd20 100755 --- a/tests/integration/nano/test_tz2_sign_micheline_basic.py +++ b/tests/integration/nano/test_tz2_sign_micheline_basic.py @@ -13,21 +13,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing with secp256k1""" -from utils.account import Account, SIGNATURE_TYPE -from utils.message import Message +from pathlib import Path -# Expression: {"CACA";"POPO";"BOUDIN"} +from utils.account import Account, SigType +from utils.app import TezosAppScreen +from utils.message import MichelineExpr -def test_tz2_sign_micheline_basic(app): +def test_tz2_sign_micheline_basic(app: TezosAppScreen): + """Check signing with secp256k1""" test_name = Path(__file__).stem account = Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.SECP256K1, + SigType.SECP256K1, "sppk7bVy617DmGvXsMqcwsiLtnedTN2trUi5ugXcNig7en4rHJyunK1") - message = Message.from_bytes("05020000001d0100000004434143410100000004504f504f0100000006424f5544494e") + message = MichelineExpr([{'string': 'CACA'}, {'string': 'POPO'}, {'string': 'BOUDIN'}]) data = app.sign(account, message, diff --git a/tests/integration/nano/test_tz3_sign_micheline_basic.py b/tests/integration/nano/test_tz3_sign_micheline_basic.py index c488d93ce..9bd28aa3b 100755 --- a/tests/integration/nano/test_tz3_sign_micheline_basic.py +++ b/tests/integration/nano/test_tz3_sign_micheline_basic.py @@ -13,21 +13,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pathlib import Path +"""Check signing with p256""" -from utils.account import Account, SIGNATURE_TYPE -from utils.message import Message +from pathlib import Path -# Expression: {"CACA";"POPO";"BOUDIN"} +from utils.account import Account, SigType +from utils.app import TezosAppScreen +from utils.message import MichelineExpr -def test_tz3_sign_micheline_basic(app): +def test_tz3_sign_micheline_basic(app: TezosAppScreen): + """Check signing with p256""" test_name = Path(__file__).stem account = Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.SECP256R1, + SigType.SECP256R1, "p2pk67fq5pzuMMABZ9RDrooYbLrgmnQbLt8z7PTGM9mskf7LXS5tdBG") - message = Message.from_bytes("05020000001d0100000004434143410100000004504f504f0100000006424f5544494e") + message = MichelineExpr([{'string': 'CACA'}, {'string': 'POPO'}, {'string': 'BOUDIN'}]) data = app.sign(account, message, diff --git a/tests/integration/nano/test_unimplemented_commands.py b/tests/integration/nano/test_unimplemented_commands.py index aae16d49a..90e50aa11 100755 --- a/tests/integration/nano/test_unimplemented_commands.py +++ b/tests/integration/nano/test_unimplemented_commands.py @@ -13,25 +13,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.app import Screen -from utils.backend import INS, StatusCode +"""Check unimplemented commands""" -def test_unimplemented_commands(app): +from utils.app import Screen, TezosAppScreen +from utils.backend import Ins, StatusCode +def test_unimplemented_commands(app: TezosAppScreen): + """Check unimplemented commands""" for ins in \ - [INS.AUTHORIZE_BAKING, \ - INS.SIGN_UNSAFE, \ - INS.RESET, \ - INS.QUERY_AUTH_KEY, \ - INS.QUERY_MAIN_HWM, \ - INS.SETUP, \ - INS.QUERY_ALL_HWM, \ - INS.DEAUTHORIZE, \ - INS.QUERY_AUTH_KEY_WITH_CURVE, \ - INS.HMAC, \ + [Ins.AUTHORIZE_BAKING, \ + Ins.SIGN_UNSAFE, \ + Ins.RESET, \ + Ins.QUERY_AUTH_KEY, \ + Ins.QUERY_MAIN_HWM, \ + Ins.SETUP, \ + Ins.QUERY_ALL_HWM, \ + Ins.DEAUTHORIZE, \ + Ins.QUERY_AUTH_KEY_WITH_CURVE, \ + Ins.HMAC, \ 0xff]: - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) with app.expect_apdu_failure(StatusCode.INVALID_INS): app.backend._exchange(ins) diff --git a/tests/integration/nano/test_version.py b/tests/integration/nano/test_version.py index 588a170da..c81640e53 100755 --- a/tests/integration/nano/test_version.py +++ b/tests/integration/nano/test_version.py @@ -13,11 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.app import Screen +"""Module to test version instruction.""" -def test_version(app): +from utils.app import Screen, TezosAppScreen - app.assert_screen(Screen.Home) +def test_version(app: TezosAppScreen): + """Test that the app version is the same as the current version.""" + app.assert_screen(Screen.HOME) data = app.backend.version() diff --git a/tests/integration/nano/test_wrong_apdu_length.py b/tests/integration/nano/test_wrong_apdu_length.py index 170965a45..a5c444cd6 100755 --- a/tests/integration/nano/test_wrong_apdu_length.py +++ b/tests/integration/nano/test_wrong_apdu_length.py @@ -13,32 +13,34 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.account import SIGNATURE_TYPE -from utils.app import Screen -from utils.backend import CLA, INDEX, INS, StatusCode +"""Check wrong apdu length behaviour""" -def test_wrong_apdu_length(app): +from utils.account import SigType +from utils.app import Screen, TezosAppScreen +from utils.backend import Cla, Index, Ins, StatusCode - app.assert_screen(Screen.Home) +def test_wrong_apdu_length(app: TezosAppScreen): + """Check wrong apdu length behaviour""" + app.assert_screen(Screen.HOME) raw = \ - int(CLA.DEFAULT).to_bytes(1, 'big') + \ - int(INS.VERSION).to_bytes(1, 'big') + \ - int(INDEX.FIRST).to_bytes(1, 'big') + \ - int(SIGNATURE_TYPE.ED25519).to_bytes(1, 'big') + \ + int(Cla.DEFAULT).to_bytes(1, 'big') + \ + int(Ins.VERSION).to_bytes(1, 'big') + \ + int(Index.FIRST).to_bytes(1, 'big') + \ + int(SigType.ED25519).to_bytes(1, 'big') + \ int(0x00).to_bytes(1, 'big') + \ int(0x00).to_bytes(1, 'big') # right size = 0x01 with app.expect_apdu_failure(StatusCode.WRONG_LENGTH_FOR_INS): app.backend.exchange_raw(raw) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) raw = \ - int(CLA.DEFAULT).to_bytes(1, 'big') + \ - int(INS.VERSION).to_bytes(1, 'big') + \ - int(INDEX.FIRST).to_bytes(1, 'big') + \ - int(SIGNATURE_TYPE.ED25519).to_bytes(1, 'big') + \ + int(Cla.DEFAULT).to_bytes(1, 'big') + \ + int(Ins.VERSION).to_bytes(1, 'big') + \ + int(Index.FIRST).to_bytes(1, 'big') + \ + int(SigType.ED25519).to_bytes(1, 'big') + \ int(0x01).to_bytes(1, 'big') # right size = 0x00 with app.expect_apdu_failure(StatusCode.WRONG_LENGTH_FOR_INS): diff --git a/tests/integration/nano/test_wrong_class.py b/tests/integration/nano/test_wrong_class.py index f7d5978bb..7d79bd076 100755 --- a/tests/integration/nano/test_wrong_class.py +++ b/tests/integration/nano/test_wrong_class.py @@ -13,31 +13,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.account import SIGNATURE_TYPE -from utils.app import Screen -from utils.backend import INDEX, INS, StatusCode +"""Check wrong apdu class behaviour""" -def test_wrong_class(app): +from utils.account import SigType +from utils.app import Screen, TezosAppScreen +from utils.backend import Index, Ins, StatusCode - app.assert_screen(Screen.Home) +def test_wrong_class(app: TezosAppScreen): + """Check wrong apdu class behaviour""" + app.assert_screen(Screen.HOME) raw = \ int(0x00).to_bytes(1, 'big') + \ - int(INS.VERSION).to_bytes(1, 'big') + \ - int(INDEX.FIRST).to_bytes(1, 'big') + \ - int(SIGNATURE_TYPE.ED25519).to_bytes(1, 'big') + \ + int(Ins.VERSION).to_bytes(1, 'big') + \ + int(Index.FIRST).to_bytes(1, 'big') + \ + int(SigType.ED25519).to_bytes(1, 'big') + \ int(0x00).to_bytes(1, 'big') with app.expect_apdu_failure(StatusCode.CLASS): app.backend.exchange_raw(raw) - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) raw = \ int(0x81).to_bytes(1, 'big') + \ - int(INS.VERSION).to_bytes(1, 'big') + \ - int(INDEX.FIRST).to_bytes(1, 'big') + \ - int(SIGNATURE_TYPE.ED25519).to_bytes(1, 'big') + \ + int(Ins.VERSION).to_bytes(1, 'big') + \ + int(Index.FIRST).to_bytes(1, 'big') + \ + int(SigType.ED25519).to_bytes(1, 'big') + \ int(0x00).to_bytes(1, 'big') with app.expect_apdu_failure(StatusCode.CLASS): diff --git a/tests/integration/nano/test_wrong_derivation_path.py b/tests/integration/nano/test_wrong_derivation_path.py index b601cf286..a14cd5e95 100755 --- a/tests/integration/nano/test_wrong_derivation_path.py +++ b/tests/integration/nano/test_wrong_derivation_path.py @@ -13,22 +13,25 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.account import Account, SIGNATURE_TYPE -from utils.app import Screen -from utils.backend import INS, StatusCode +"""Check wrong derivation path behaviour""" -def test_wrong_derivation_path(app): +from utils.account import Account, SigType +from utils.app import Screen, TezosAppScreen +from utils.backend import Ins, StatusCode + +def test_wrong_derivation_path(app: TezosAppScreen): + """Check wrong derivation path behaviour""" wrong_number_index_account = Account( bytes.fromhex("058000002c800006c18000000080000000"), - SIGNATURE_TYPE.ED25519, + SigType.ED25519, "__unused__") wrong_length_account = Account( bytes.fromhex("048000002c800006c180000000800000"), - SIGNATURE_TYPE.ED25519, + SigType.ED25519, "__unused__") too_much_index_account = Account( bytes.fromhex("0b8000002c800006c1800000008000000080000000800000008000000080000000800000008000000080000000"), - SIGNATURE_TYPE.ED25519, + SigType.ED25519, "__unused__") for account in [wrong_number_index_account, @@ -36,10 +39,10 @@ def test_wrong_derivation_path(app): too_much_index_account]: for sender in [lambda account: app.backend.get_public_key(account, with_prompt=False), lambda account: app.backend.get_public_key(account, with_prompt=True), - lambda account: app.backend._ask_sign(INS.SIGN, account), - lambda account: app.backend._ask_sign(INS.SIGN_WITH_HASH, account)]: + lambda account: app.backend._ask_sign(Ins.SIGN, account), + lambda account: app.backend._ask_sign(Ins.SIGN_WITH_HASH, account)]: - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) with app.expect_apdu_failure(StatusCode.WRONG_LENGTH_FOR_INS): sender(account) diff --git a/tests/integration/nano/test_wrong_derivation_type.py b/tests/integration/nano/test_wrong_derivation_type.py index d105ca8b1..12b967ab4 100755 --- a/tests/integration/nano/test_wrong_derivation_type.py +++ b/tests/integration/nano/test_wrong_derivation_type.py @@ -13,19 +13,22 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Check wrong derivation type behaviour""" + from utils.account import Account -from utils.app import Screen -from utils.backend import INS, StatusCode +from utils.app import Screen, TezosAppScreen +from utils.backend import Ins, StatusCode -def test_wrong_derivation_type(app): +def test_wrong_derivation_type(app: TezosAppScreen): + """Check wrong derivation type behaviour""" account = Account("m/44'/1729'/0'/0'", 0x04, "__unused__") for sender in [lambda account: app.backend.get_public_key(account, with_prompt=False), lambda account: app.backend.get_public_key(account, with_prompt=True), - lambda account: app.backend._ask_sign(INS.SIGN, account), - lambda account: app.backend._ask_sign(INS.SIGN_WITH_HASH, account)]: + lambda account: app.backend._ask_sign(Ins.SIGN, account), + lambda account: app.backend._ask_sign(Ins.SIGN_WITH_HASH, account)]: - app.assert_screen(Screen.Home) + app.assert_screen(Screen.HOME) with app.expect_apdu_failure(StatusCode.WRONG_PARAM): sender(account) diff --git a/tests/integration/nano/test_wrong_index.py b/tests/integration/nano/test_wrong_index.py index a3b47ede8..d26e0fc72 100755 --- a/tests/integration/nano/test_wrong_index.py +++ b/tests/integration/nano/test_wrong_index.py @@ -13,16 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -from utils.app import DEFAULT_ACCOUNT, Screen -from utils.backend import INDEX, INS, StatusCode +"""Check wrong apdu index behaviour""" -def test_wrong_index(app): - for ins in [INS.GET_PUBLIC_KEY, - INS.PROMPT_PUBLIC_KEY]: - for index in [INDEX.OTHER, - INDEX.LAST]: +from utils.app import Screen, TezosAppScreen, DEFAULT_ACCOUNT +from utils.backend import Index, Ins, StatusCode - app.assert_screen(Screen.Home) +def test_wrong_index(app: TezosAppScreen): + """Check wrong apdu index behaviour""" + for ins in [Ins.GET_PUBLIC_KEY, + Ins.PROMPT_PUBLIC_KEY]: + for index in [Index.OTHER, + Index.LAST]: + + app.assert_screen(Screen.HOME) with app.expect_apdu_failure(StatusCode.WRONG_PARAM): app.backend._exchange(ins, diff --git a/tests/integration/nano/utils/account.py b/tests/integration/nano/utils/account.py index 8d6e1549f..39e3d074e 100644 --- a/tests/integration/nano/utils/account.py +++ b/tests/integration/nano/utils/account.py @@ -12,33 +12,46 @@ # See the License for the specific language governing permissions and # limitations under the License. -import base58 +"""Module providing an account interface.""" + from enum import IntEnum +from typing import Union + +import base58 from pytezos import pytezos from ragger.bip import pack_derivation_path -from typing import Union -class SIGNATURE_TYPE(IntEnum): + +class SigType(IntEnum): + """Class representing signature type.""" + ED25519 = 0x00 SECP256K1 = 0x01 SECP256R1 = 0x02 BIP32_ED25519 = 0x03 class Signature: + """Class representing signature.""" GENERIC_SIGNATURE_PREFIX = bytes.fromhex("04822b") # sig(96) + value: bytes + def __init__(self, value: bytes): value = Signature.GENERIC_SIGNATURE_PREFIX + value - self.value: bytes = base58.b58encode_check(value) + self.value = base58.b58encode_check(value) def __repr__(self) -> str: return self.value.hex() @classmethod - def from_tlv(self, tlv: Union[bytes, bytearray]) -> 'Signature': - if isinstance(tlv, bytes): tlv = bytearray(tlv) - # See: https://developers.ledger.com/docs/embedded-app/crypto-api/lcx__ecdsa_8h/#cx_ecdsa_sign + def from_tlv(cls, tlv: Union[bytes, bytearray]) -> 'Signature': + """Get Signature from tlv.""" + + if isinstance(tlv, bytes): + tlv = bytearray(tlv) + # See: + # https://developers.ledger.com/docs/embedded-app/crypto-api/lcx__ecdsa_8h/#cx_ecdsa_sign # TLV: 30 || L || 02 || Lr || r || 02 || Ls || s header_tag_index = 0 # Remove the unwanted parity information set here. @@ -64,38 +77,45 @@ def from_tlv(self, tlv: Union[bytes, bytearray]) -> 'Signature': s = tlv[s_index : s_index + s_len] # Sometimes \x00 are added or removed # A size adjustment is required here. - def adjust_size(bytes, size): - return bytes[-size:].rjust(size, b'\x00') - return Signature(adjust_size(r, 32) + adjust_size(s, 32)) + def adjust_size(raw, size): + return raw[-size:].rjust(size, b'\x00') + return cls(adjust_size(r, 32) + adjust_size(s, 32)) class Account: + """Class representing account.""" + + path: bytes + sig_type: Union[SigType, int] + public_key: str + def __init__(self, path: Union[str, bytes], - sig_type: SIGNATURE_TYPE, + sig_type: Union[SigType, int], public_key: str): - self.path: bytes = \ + self.path = \ pack_derivation_path(path) if isinstance(path, str) \ else path - self.sig_type: SIGNATURE_TYPE = sig_type - self.public_key: str = public_key + self.sig_type = sig_type + self.public_key = public_key def __repr__(self) -> str: return self.public_key @property def base58_decoded(self) -> bytes: + """Decodes public_key from base58 encoding.""" # Get the public_key without prefix public_key = base58.b58decode_check(self.public_key) if self.sig_type in [ - SIGNATURE_TYPE.ED25519, - SIGNATURE_TYPE.BIP32_ED25519 + SigType.ED25519, + SigType.BIP32_ED25519 ]: prefix = bytes.fromhex("0d0f25d9") # edpk(54) - elif self.sig_type == SIGNATURE_TYPE.SECP256K1: + elif self.sig_type == SigType.SECP256K1: prefix = bytes.fromhex("03fee256") # sppk(55) - elif self.sig_type == SIGNATURE_TYPE.SECP256R1: + elif self.sig_type == SigType.SECP256R1: prefix = bytes.fromhex("03b28b7f") # p2pk(55) else: assert False, \ @@ -106,8 +126,8 @@ def base58_decoded(self) -> bytes: public_key = public_key[len(prefix):] if self.sig_type in [ - SIGNATURE_TYPE.SECP256K1, - SIGNATURE_TYPE.SECP256R1 + SigType.SECP256K1, + SigType.SECP256R1 ]: assert public_key[0] in [0x02, 0x03], \ "Expected a prefix kind of 0x02 or 0x03 but got {public_key[0]}" @@ -116,6 +136,7 @@ def base58_decoded(self) -> bytes: return public_key def check_public_key(self, data: bytes) -> None: + """Checks if the data correspond to the public_key.""" # `data` should be: # length + kind + pk @@ -138,12 +159,15 @@ def check_public_key(self, data: bytes) -> None: def check_signature(self, signature: Union[bytes, Signature], message: Union[str, bytes]): - if isinstance(message, str): message = bytes.fromhex(message) + """Checks if signature correspond to a signature of message sign by the account.""" + + if isinstance(message, str): + message = bytes.fromhex(message) if isinstance(signature, bytes): signature = Signature(signature) \ if self.sig_type in [ - SIGNATURE_TYPE.ED25519, - SIGNATURE_TYPE.BIP32_ED25519 + SigType.ED25519, + SigType.BIP32_ED25519 ] \ else Signature.from_tlv(signature) ctxt = pytezos.using(key=self.public_key) diff --git a/tests/integration/nano/utils/app.py b/tests/integration/nano/utils/app.py index 4f2eda098..d2fbedf69 100644 --- a/tests/integration/nano/utils/app.py +++ b/tests/integration/nano/utils/app.py @@ -13,39 +13,48 @@ # See the License for the specific language governing permissions and # limitations under the License. -import argparse -import git -import time +"""Tezos app backend.""" + from contextlib import contextmanager from enum import Enum +from io import BytesIO from multiprocessing import Process, Queue from pathlib import Path -from requests.exceptions import ConnectionError +import time +from typing import Callable, Generator, List, Optional, Tuple, Union + +import requests +import git from ragger.backend import SpeculosBackend from ragger.error import ExceptionRAPDU from ragger.navigator import NavInsID, NanoNavigator -from typing import Callable, Generator, List, Optional, Tuple, Union from .message import Message -from .account import Account, SIGNATURE_TYPE -from .backend import StatusCode, TezosBackend, APP_KIND +from .account import Account, SigType +from .backend import StatusCode, TezosBackend, AppKind -version: Tuple[int, int, int] = (3, 0, 5) +version: Tuple[int, int, int] = (3, 0, 6) class TezosAPDUChecker: + """Helper to check APDU received.""" - def __init__(self, app_kind: APP_KIND): + app_kind: AppKind + + def __init__(self, app_kind: AppKind): self.app_kind = app_kind @property def commit(self) -> bytes: + """Current commit.""" repo = git.Repo(".") commit = repo.head.commit.hexsha[:8] - if repo.is_dirty(): commit += "*" + if repo.is_dirty(): + commit += "*" return bytes.fromhex(commit.encode('utf-8').hex() + "00") @property def version_bytes(self) -> bytes: + """Current version in bytes.""" return \ self.app_kind.to_bytes(1, byteorder='big') + \ version[0].to_bytes(1, byteorder='big') + \ @@ -53,16 +62,19 @@ def version_bytes(self) -> bytes: version[2].to_bytes(1, byteorder='big') def check_commit(self, commit: bytes) -> None: + """Check if the commit is the current commit.""" assert commit == self.commit, \ f"Expected commit {self.commit.hex()} but got {commit.hex()}" - def check_version(self, version: bytes) -> None: - assert version == self.version_bytes, \ - f"Expected version {self.version_bytes.hex()} but got {version.hex()}" + def check_version(self, raw_version: bytes) -> None: + """Check if the version is the current version.""" + assert raw_version == self.version_bytes, \ + f"Expected version {self.version_bytes.hex()} but got {raw_version.hex()}" def check_public_key(self, account: Account, public_key: bytes) -> None: + """Check that public_key is the account public key.""" account.check_public_key(public_key) def check_signature(self, @@ -70,17 +82,19 @@ def check_signature(self, message: Message, with_hash: bool, data: bytes) -> None: + """Check that data is a signature of message from account.""" if with_hash: assert data.startswith(message.hash), \ f"Expected a starting hash {message.hash.hex()} but got {data.hex()}" data = data[len(message.hash):] - account.check_signature(data, message.bytes) + account.check_signature(data, bytes(message)) MAX_ATTEMPTS = 50 def with_retry(f, attempts=MAX_ATTEMPTS): + """Try f until it succeeds or has been executed attempts times.""" while True: try: return f() @@ -93,6 +107,7 @@ def with_retry(f, attempts=MAX_ATTEMPTS): time.sleep(0.5) def run_simultaneously(processes: List[Process]) -> None: + """Executes multiples process at the same time.""" for process in processes: process.start() @@ -102,6 +117,7 @@ def run_simultaneously(processes: List[Process]) -> None: assert process.exitcode == 0, "Should have terminate successfully" def send_and_navigate(send: Callable[[], bytes], navigate: Callable[[], None]) -> bytes: + """Sends APDU and navigates.""" def _send(result_queue: Queue) -> None: res = send() @@ -114,6 +130,7 @@ def _send(result_queue: Queue) -> None: return result_queue.get() class SpeculosTezosBackend(TezosBackend, SpeculosBackend): + """Class representing Tezos app running on Speculos.""" # speculos can be slow to start up in a slow environment. # Here, we expect a little more @@ -121,7 +138,7 @@ def __enter__(self) -> "SpeculosTezosBackend": try: super().__enter__() return self - except Exception as e: + except Exception: process = self._client.process try: with_retry(self._client._wait_until_ready, attempts=5) @@ -139,50 +156,69 @@ def __enter__(self) -> "SpeculosTezosBackend": return self class Screen(str, Enum): - Home = "home" - Version = "version" - Settings = "settings" - Settings_expert_mode_disabled = "settings_expert_mode_disabled" - Settings_expert_mode_enabled = "settings_expert_mode_enabled" - Settings_blindsign_on = "settings_blindsign_on" - Settings_blindsign_off = "settings_blindsign_off" - Settings_back = "back" - Quit = "quit" - -class Screen_text(str, Enum): - Home = "Application" - Public_key_approve = "Approve" - Public_key_reject = "Reject" - Sign_accept = "Accept" - Sign_reject = "Reject" - Accept_risk = "Accept risk" - Back_home = "Home" - Blindsign = "blindsign" - Blindsign_nanos = "Blindsign" - - def blindsign(backend: SpeculosTezosBackend) -> "Screen_text": + """Class representing common, known app screens.""" + + HOME = "home" + VERSION = "version" + SETTINGS = "settings" + SETTINGS_EXPERT_MODE_DISABLED = "settings_expert_mode_disabled" + SETTINGS_EXPERT_MODE_ENABLED = "settings_expert_mode_enabled" + SETTINGS_BLINDSIGN_ON = "settings_blindsign_on" + SETTINGS_BLINDSIGN_OFF = "settings_blindsign_off" + SETTINGS_BACK = "back" + QUIT = "quit" + + def __str__(self) -> str: + return self.value + +class ScreenText(str, Enum): + """Class representing common, known app screen's text.""" + + HOME = "Application" + PUBLIC_KEY_APPROVE = "Approve" + PUBLIC_KEY_REJECT = "Reject" + SIGN_ACCEPT = "Accept" + SIGN_REJECT = "Reject" + ACCEPT_RISK = "Accept risk" + BACK_HOME = "Home" + BLINDSIGN = "blindsign" + BLINDSIGN_NANOS = "Blindsign" + + @classmethod + def blindsign(cls, backend: SpeculosTezosBackend) -> "ScreenText": + """Get blindsign text depending on device.""" if backend.firmware.device == "nanos": - return Screen_text.Blindsign_nanos - else: - return Screen_text.Blindsign + return cls.BLINDSIGN_NANOS + + return cls.BLINDSIGN class TezosAppScreen(): + """Class representing Tezos app management.""" + + backend: SpeculosTezosBackend + checker: TezosAPDUChecker + path: Path + snapshots_dir: Path + tmp_snapshots_dir: Path + snapshotted: List[str] + golden_run: bool + navigator: NanoNavigator def __init__(self, backend: SpeculosTezosBackend, - app_kind: APP_KIND, + app_kind: AppKind, golden_run: bool): self.backend = backend self.checker = TezosAPDUChecker(app_kind) - self.path: Path = Path(__file__).resolve().parent.parent - self.snapshots_dir: Path = self.path / "snapshots" / backend.firmware.name - self.tmp_snapshots_dir: Path = self.path / "snapshots-tmp" / backend.firmware.name + self.path = Path(__file__).resolve().parent.parent + self.snapshots_dir = self.path / "snapshots" / backend.firmware.name + self.tmp_snapshots_dir = self.path / "snapshots-tmp" / backend.firmware.name if not self.snapshots_dir.is_dir() and golden_run: self.snapshots_dir.mkdir(parents=True) if not self.tmp_snapshots_dir.is_dir(): self.tmp_snapshots_dir.mkdir(parents=True) - self.snapshotted: List[str] = [] + self.snapshotted = [] self.golden_run = golden_run self.navigator = NanoNavigator(backend, backend.firmware, golden_run) @@ -194,14 +230,19 @@ def __enter__(self) -> "TezosAppScreen": def __exit__(self, *args): self.backend.__exit__(*args) - def assert_screen(self, screen: Union[str, Screen], path: Optional[Union[str, Path]] = None) -> None: + def assert_screen(self, + screen: Union[str, Screen], + path: Optional[Union[str, Path]] = None) -> None: + """Check if the screen is the one expected.""" golden_run = self.golden_run and screen not in self.snapshotted if golden_run: self.snapshotted = self.snapshotted + [screen] input(f"Press ENTER to snapshot {screen}") - snapshots_dir = self.snapshots_dir if path is None else self.snapshots_dir / path - tmp_snapshots_dir = self.tmp_snapshots_dir if path is None else self.tmp_snapshots_dir / path + snapshots_dir = self.snapshots_dir if path is None \ + else self.snapshots_dir / path + tmp_snapshots_dir = self.tmp_snapshots_dir if path is None \ + else self.tmp_snapshots_dir / path if not snapshots_dir.is_dir() and golden_run: snapshots_dir.mkdir(parents=True) @@ -218,59 +259,65 @@ def check(): golden_run=golden_run) with_retry(check) - self.backend._last_screenshot = path + self.backend._last_screenshot = BytesIO(self.backend._client.get_screenshot()) def setup_expert_mode(self) -> None: - self.assert_screen(Screen.Home) + """Enable expert-mode from home screen.""" + self.assert_screen(Screen.HOME) self.backend.right_click() - self.assert_screen(Screen.Version) + self.assert_screen(Screen.VERSION) self.backend.right_click() - self.assert_screen(Screen.Settings) + self.assert_screen(Screen.SETTINGS) self.backend.both_click() - self.assert_screen(Screen.Settings_expert_mode_disabled) + self.assert_screen(Screen.SETTINGS_EXPERT_MODE_DISABLED) self.backend.both_click() - self.assert_screen(Screen.Settings_expert_mode_enabled) + self.assert_screen(Screen.SETTINGS_EXPERT_MODE_ENABLED) self.backend.left_click() - self.assert_screen(Screen.Settings_back) + self.assert_screen(Screen.SETTINGS_BACK) self.backend.both_click() - self.assert_screen(Screen.Home) + self.assert_screen(Screen.HOME) def setup_blindsign_on(self) -> None: - self.assert_screen(Screen.Home) + """Enable blindsign from home screen.""" + self.assert_screen(Screen.HOME) self.backend.right_click() - self.assert_screen(Screen.Version) + self.assert_screen(Screen.VERSION) self.backend.right_click() - self.assert_screen(Screen.Settings) + self.assert_screen(Screen.SETTINGS) self.backend.both_click() # expert_mode screen self.backend.right_click() - self.assert_screen(Screen.Settings_blindsign_off) + self.assert_screen(Screen.SETTINGS_BLINDSIGN_OFF) self.backend.both_click() - self.assert_screen(Screen.Settings_blindsign_on) + self.assert_screen(Screen.SETTINGS_BLINDSIGN_ON) self.backend.right_click() - self.assert_screen(Screen.Settings_back) + self.assert_screen(Screen.SETTINGS_BACK) self.backend.both_click() - self.assert_screen(Screen.Home) + self.assert_screen(Screen.HOME) def _quit(self) -> None: - self.assert_screen(Screen.Quit) + """Ensure quiting exit the app.""" + self.assert_screen(Screen.QUIT) try: self.backend.both_click() assert False, "Must have lost connection with speculos" - except ConnectionError: + except requests.exceptions.ConnectionError: pass def quit(self) -> None: - self.assert_screen(Screen.Home) + """Quit the app from home screen.""" + self.assert_screen(Screen.HOME) self.backend.right_click() - self.assert_screen(Screen.Version) + self.assert_screen(Screen.VERSION) self.backend.right_click() - self.assert_screen(Screen.Settings) + self.assert_screen(Screen.SETTINGS) self.backend.right_click() self._quit() - def navigate_until_text(self, text: Screen_text, path: Union[str, Path]) -> None: - if isinstance(path, str): path = Path(path) + def navigate_until_text(self, text: ScreenText, path: Union[str, Path]) -> None: + """Click right until the expected text is displayed, then both click.""" + if isinstance(path, str): + path = Path(path) self.navigator.\ navigate_until_text_and_compare(NavInsID.RIGHT_CLICK, [NavInsID.BOTH_CLICK], @@ -281,6 +328,7 @@ def navigate_until_text(self, text: Screen_text, path: Union[str, Path]) -> None @contextmanager def expect_apdu_failure(self, code: StatusCode) -> Generator[None, None, None]: + """Expect the body to fail with code.""" try: yield assert False, f"Expect fail with { code } but succeed" @@ -293,6 +341,7 @@ def _failing_send(self, send: Callable[[], bytes], navigate: Callable[[], None], status_code: StatusCode) -> None: + """Expect that send and navigates fails with status_code.""" def expected_failure_send() -> bytes: with self.expect_apdu_failure(status_code): send() @@ -305,19 +354,19 @@ def expected_failure_send() -> bytes: def provide_public_key(self, account: Account, path: Union[str, Path]) -> bytes: - + """Get the account's public key from the app after approving it.""" return send_and_navigate( - send=(lambda: self.backend.get_public_key(account, with_prompt=True)), - navigate=(lambda: self.navigate_until_text(Screen_text.Public_key_approve, path))) + send=lambda: self.backend.get_public_key(account, with_prompt=True), + navigate=lambda: self.navigate_until_text(ScreenText.PUBLIC_KEY_APPROVE, path)) def reject_public_key(self, account: Account, path: Union[str, Path]) -> None: - + """Reject the account's public key.""" self._failing_send( send=(lambda: self.backend.get_public_key(account, with_prompt=True)), navigate=(lambda: self.navigate_until_text( - Screen_text.Public_key_reject, + ScreenText.PUBLIC_KEY_REJECT, path)), status_code=StatusCode.REJECT) @@ -326,7 +375,7 @@ def _sign(self, message: Message, with_hash: bool, navigate: Callable[[], None]) -> bytes: - + """Requests to sign the message with account and navigates.""" return send_and_navigate( send=(lambda: self.backend.sign(account, message, with_hash)), navigate=navigate) @@ -336,24 +385,25 @@ def sign(self, message: Message, with_hash: bool, path: Union[str, Path]) -> bytes: - + """Sign the message with account.""" return self._sign( account, message, with_hash, - navigate=(lambda: self.navigate_until_text(Screen_text.Sign_accept, path))) + navigate=lambda: self.navigate_until_text(ScreenText.SIGN_ACCEPT, path)) def blind_sign(self, account: Account, message: Message, with_hash: bool, path: Union[str, Path]) -> bytes: - - if isinstance(path, str): path = Path(path) + """Blindsign the message with account.""" + if isinstance(path, str): + path = Path(path) def navigate() -> None: - self.navigate_until_text(Screen_text.Accept_risk, path / "clear") - self.navigate_until_text(Screen_text.Sign_accept, path / "blind") + self.navigate_until_text(ScreenText.ACCEPT_RISK, path / "clear") + self.navigate_until_text(ScreenText.SIGN_ACCEPT, path / "blind") return send_and_navigate( send=(lambda: self.backend.sign(account, message, with_hash)), @@ -365,7 +415,7 @@ def _failing_signing(self, with_hash: bool, navigate: Callable[[], None], status_code: StatusCode) -> None: - + """Expect requests signing and navigate fails with status_code.""" self._failing_send( send=(lambda: self.backend.sign(account, message, with_hash)), navigate=navigate, @@ -376,12 +426,13 @@ def reject_signing(self, message: Message, with_hash: bool, path: Union[str, Path]) -> None: + """Request and reject signing the message.""" self._failing_signing( account, message, with_hash, navigate=(lambda: self.navigate_until_text( - Screen_text.Sign_reject, + ScreenText.SIGN_REJECT, path)), status_code=StatusCode.REJECT) @@ -391,11 +442,12 @@ def hard_failing_signing(self, with_hash: bool, status_code: StatusCode, path: Union[str, Path]) -> None: + """Expect the signing request to hard fail.""" self._failing_signing(account, message, with_hash, navigate=(lambda: self.navigate_until_text( - Screen_text.Home, + ScreenText.HOME, path)), status_code=status_code) @@ -404,16 +456,17 @@ def parsing_error_signing(self, message: Message, with_hash: bool, path: Union[str, Path]) -> None: + """Expect the signing request to fail with parsing error.""" self._failing_signing(account, message, with_hash, navigate=(lambda: self.navigate_until_text( - Screen_text.Sign_reject, + ScreenText.SIGN_REJECT, path)), status_code=StatusCode.PARSE_ERROR) -DEFAULT_SEED = ('zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra') +DEFAULT_SEED = 'zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra zebra' DEFAULT_ACCOUNT = Account("m/44'/1729'/0'/0'", - SIGNATURE_TYPE.ED25519, + SigType.ED25519, "edpkuXX2VdkdXzkN11oLCb8Aurdo1BTAtQiK8ZY9UPj2YMt3AHEpcY") diff --git a/tests/integration/nano/utils/backend.py b/tests/integration/nano/utils/backend.py index 7746b9dcc..c76cd0cea 100644 --- a/tests/integration/nano/utils/backend.py +++ b/tests/integration/nano/utils/backend.py @@ -12,18 +12,25 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Tezos app backend.""" + from enum import IntEnum +from typing import Union + from ragger.backend.interface import BackendInterface, RAPDU from ragger.error import ExceptionRAPDU -from typing import Union -from .account import Account, SIGNATURE_TYPE +from .account import Account, SigType from .message import Message -class CLA(IntEnum): +class Cla(IntEnum): + """Class representing APDU class.""" + DEFAULT = 0x80 -class INS(IntEnum): +class Ins(IntEnum): + """Class representing instruction.""" + VERSION = 0x00 AUTHORIZE_BAKING = 0x01 GET_PUBLIC_KEY = 0x02 @@ -41,13 +48,17 @@ class INS(IntEnum): HMAC = 0x0e SIGN_WITH_HASH = 0x0f -class INDEX(IntEnum): +class Index(IntEnum): + """Class representing packet index.""" + FIRST = 0x00 OTHER = 0x01 LAST = 0x80 OTHER_LAST = 0x81 class StatusCode(IntEnum): + """Class representing the status code.""" + SECURITY = 0x6982 HID_REQUIRED = 0x6983 REJECT = 0x6985 @@ -66,26 +77,30 @@ class StatusCode(IntEnum): MEMORY_ERROR = 0x9200 PARSE_ERROR = 0x9405 -class APP_KIND(IntEnum): +class AppKind(IntEnum): + """Class representing the kind of app.""" + WALLET = 0x00 BAKING = 0x01 MAX_APDU_SIZE: int = 235 class TezosBackend(BackendInterface): + """Class representing the backen of the tezos app.""" def _exchange(self, - ins: INS, - index: INDEX = INDEX.FIRST, - sig_type: Union[SIGNATURE_TYPE, None] = None, + ins: Union[Ins, int], + index: Union[Index, int] = Index.FIRST, + sig_type: Union[SigType, int, None] = None, payload: bytes = b'') -> bytes: + """Override of `exchange` for the tezos app.""" - assert len(payload) <= MAX_APDU_SIZE, f"Apdu too large" + assert len(payload) <= MAX_APDU_SIZE, f"Apdu too large {len(payload)}" # Set to a non-existent value to ensure that p2 is unused p2: int = sig_type if sig_type is not None else 0xff - rapdu: RAPDU = self.exchange(CLA.DEFAULT, + rapdu: RAPDU = self.exchange(Cla.DEFAULT, ins, p1=index, p2=p2, @@ -97,28 +112,36 @@ def _exchange(self, return rapdu.data def git(self) -> bytes: - return self._exchange(INS.GIT) + """Requests the app commit.""" + return self._exchange(Ins.GIT) def version(self) -> bytes: - return self._exchange(INS.VERSION) + """Requests the app version.""" + return self._exchange(Ins.VERSION) def get_public_key(self, account: Account, with_prompt: bool = False) -> bytes: - - ins = INS.PROMPT_PUBLIC_KEY if with_prompt else INS.GET_PUBLIC_KEY - + """Requests the public key according to the account. + Use `with_prompt` ask user confirmation + """ + ins = Ins.PROMPT_PUBLIC_KEY if with_prompt else Ins.GET_PUBLIC_KEY return self._exchange(ins, sig_type=account.sig_type, payload=account.path) - def _ask_sign(self, ins: INS, account: Account) -> None: + def _ask_sign(self, ins: Ins, account: Account) -> None: + """Prepare to sign with the account.""" data: bytes = self._exchange(ins, sig_type=account.sig_type, payload=account.path) - assert not data - - def _continue_sign(self, ins: INS, payload: bytes, last: bool) -> bytes: - index: INDEX = INDEX.OTHER - if last: index = INDEX(index | INDEX.LAST) + assert not data, f"No data expected but got {data.hex()}" + + def _continue_sign(self, ins: Ins, payload: bytes, last: bool) -> bytes: + """Sends payload to sign. + Use `last` when sending the last packet + """ + index: Index = Index.OTHER + if last: + index = Index(index | Index.LAST) return self._exchange(ins, index, payload=payload) def sign(self, @@ -126,21 +149,21 @@ def sign(self, message: Message, with_hash: bool = False, apdu_size: int = MAX_APDU_SIZE) -> bytes: - msg = message.bytes + """Requests the signature of a message.""" + msg = bytes(message) assert msg, "Do not sign empty message" - ins = INS.SIGN_WITH_HASH if with_hash else INS.SIGN + ins = Ins.SIGN_WITH_HASH if with_hash else Ins.SIGN self._ask_sign(ins, account) - while(msg): + while msg: payload = msg[:apdu_size] msg = msg[apdu_size:] last = not msg data = self._continue_sign(ins, payload, last) if last: return data - else: - assert not data + assert not data, f"No data expected but got {data.hex()}" assert False, "We should have already returned" diff --git a/tests/integration/nano/utils/message.py b/tests/integration/nano/utils/message.py index 0062cc168..209e06ccf 100644 --- a/tests/integration/nano/utils/message.py +++ b/tests/integration/nano/utils/message.py @@ -12,28 +12,785 @@ # See the License for the specific language governing permissions and # limitations under the License. -from hashlib import blake2b -from typing import Union +"""Implemenation of sent messages.""" -class Message: +from abc import ABC, abstractmethod +from enum import IntEnum +from typing import Any, Dict, List, Optional, Union - HASH_SIZE = 32 +from pytezos.block.forge import forge_int_fixed +from pytezos.crypto.key import blake2b_32 +from pytezos.michelson.forge import ( + forge_address, + forge_array, + forge_base58, + forge_bool, + forge_int32, + forge_micheline, + forge_nat, + forge_public_key, +) +from pytezos.operation.content import ContentMixin, format_mutez +import pytezos.operation.forge as forge_operation +from pytezos.operation.forge import reserved_entrypoints, forge_tag +from pytezos.rpc.kind import operation_tags - def __init__(self, value: bytes): - self.value: bytes = value - - @classmethod - def from_bytes(self, value: Union[str, bytes]) -> 'Message': - if isinstance(value, str): value = bytes.fromhex(value) - return Message(value) +class Message(ABC): + """Class representing a message.""" @property def hash(self) -> bytes: - return blake2b( - self.value, - digest_size=Message.HASH_SIZE - ).digest() + """hash of the message.""" + return blake2b_32(bytes(self)).digest() - @property - def bytes(self) -> bytes: - return self.value + @abstractmethod + def __bytes__(self) -> bytes: + raise NotImplementedError + +class RawMessage(Message): + """Class representing a raw message.""" + + _value: bytes + + def __init__(self, value: Union[str, bytes]): + self._value = value if isinstance(value, bytes) else \ + bytes.fromhex(value) + + def __bytes__(self) -> bytes: + return self._value + + +Micheline = Union[List, Dict] + +class Default: + """Class holding default values.""" + BLOCK_HASH: str = 'BKiHLREqU3JkXfzEDYAkmmfX48gBDtYhMrpA98s7Aq4SzbUAB6M' + PROTOCOL_HASH: str = 'PrihK96nBAFSxVL1GLJTVhu9YnzkMFiBeuJRPA8NwuZVZCE1L6i' + ED25519_PUBLIC_KEY_HASH: str = 'tz1Ke2h7sDdakHJQh8WX4Z372du1KChsksyU' + ORIGINATED_ADDRESS: str = 'KT18amZmM5W7qDWVt2pH6uj7sCEd3kbzLrHT' + ORIGINATED_SMART_ROLLUP_ADDRESS: str = 'sr163Lv22CdE8QagCwf48PWDTquk6isQwv57' + SMART_ROLLUP_COMMITMENT_HASH: str = 'src12UJzB8mg7yU6nWPzicH7ofJbFjyJEbHvwtZdfRXi8DQHNp1LY8' + ED25519_PUBLIC_KEY: str = 'edpkteDwHwoNPB18tKToFKeSCykvr1ExnoMV5nawTJy9Y9nLTfQ541' + ENTRYPOINT: str = 'default' + BALLOT: str = 'yay' + SMART_ROLLUP_KIND: str = 'arith' + + class DefaultMicheline: + """Class holding Micheline default values.""" + VALUE: Micheline = {'prim': 'Unit'} + TYPE: Micheline = {'prim': 'unit'} + CODE: Micheline = [{'prim': 'CDR'}, {'prim': 'NIL', 'args': [{'prim': 'operation'}]}, {'prim': 'PAIR'}] + +class Watermark(IntEnum): + """Class hodling messages watermark.""" + MANAGER_OPERATION = 0x03 + MICHELINE_EXPRESSION = 0x05 + + +class MichelineExpr(Message): + """Class representing a tezos micheline expression.""" + + expr: Micheline + + def __init__(self, expr: Micheline): + self.expr = expr + + def __bytes__(self) -> bytes: + raw = b'' + raw += forge_int_fixed(Watermark.MICHELINE_EXPRESSION, 1) + raw += forge_micheline(self.expr) + return raw + + +class OperationBuilder(ContentMixin): + """Class representing to extends and fix pytezos.ContentMixin.""" + + def delegation(self, delegate, *args, **kwargs): + delegation = super().delegation(delegate, *args, **kwargs) + + if delegate is None: + delegation.pop('delegate') + + return delegation + + def set_deposit_limit( + self, + limit: Optional[int] = None, + source: str = '', + counter: int = 0, + fee: int = 0, + gas_limit: int = 0, + storage_limit: int = 0): + """Build a Tezos set-deposit-limit.""" + content = { + 'kind': 'set_deposit_limit', + 'source': source, + 'fee': format_mutez(fee), + 'counter': str(counter), + 'gas_limit': str(gas_limit), + 'storage_limit': str(storage_limit), + } + + if limit is not None: + content['limit'] = format_mutez(limit) + + return self.operation(content) + + def increase_paid_storage( + self, + amount: int = 0, + destination: str = '', + source: str = '', + counter: int = 0, + fee: int = 0, + gas_limit: int = 0, + storage_limit: int = 0): + """Build a Tezos increase-paid-storage.""" + return self.operation( + { + 'kind': 'increase_paid_storage', + 'source': source, + 'fee': format_mutez(fee), + 'counter': str(counter), + 'gas_limit': str(gas_limit), + 'storage_limit': str(storage_limit), + 'amount': str(amount), + 'destination': destination, + } + ) + + def update_consensus_key( + self, + pk: str = '', + source: str = '', + counter: int = 0, + fee: int = 0, + gas_limit: int = 0, + storage_limit: int = 0): + """Build a Tezos update-consensus-key.""" + return self.operation( + { + 'kind': 'update_consensus_key', + 'source': source, + 'fee': format_mutez(fee), + 'counter': str(counter), + 'gas_limit': str(gas_limit), + 'storage_limit': str(storage_limit), + 'pk': pk, + } + ) + + def smart_rollup_originate( + self, + pvm_kind: str = '', + kernel: str = '', + parameters_ty: Micheline = Default.DefaultMicheline.TYPE, + whitelist: Optional[List[str]] = None, + source: str = '', + counter: int = 0, + fee: int = 0, + gas_limit: int = 0, + storage_limit: int = 0): + """Build a Tezos smart rollup originate.""" + content = { + 'kind': 'smart_rollup_originate', + 'source': source, + 'fee': format_mutez(fee), + 'counter': str(counter), + 'gas_limit': str(gas_limit), + 'storage_limit': str(storage_limit), + 'pvm_kind': pvm_kind, + 'kernel': kernel, + 'parameters_ty': parameters_ty, + } + + if whitelist is not None: + content['whitelist'] = whitelist + + return self.operation(content) + +class OperationForge: + """Class to helps forging Tezos operation.""" + + # Insert new reserved entrypoint + reserved_entrypoints['stake'] = b'\x06' + reserved_entrypoints['unstake'] = b'\x07' + reserved_entrypoints['finalize_unstake'] = b'\x08' + reserved_entrypoints['delegate_parameters'] = b'\x09' + + # Insert new operation tag + operation_tags['set_deposit_limit'] = 112 + operation_tags['increase_paid_storage'] = 113 + operation_tags['update_consensus_key'] = 114 + operation_tags['smart_rollup_originate'] = 200 + + failing_noop = forge_operation.forge_failing_noop + reveal = forge_operation.forge_reveal + transaction = forge_operation.forge_transaction + origination = forge_operation.forge_origination + delegation = forge_operation.forge_delegation + register_global_constant = forge_operation.forge_register_global_constant + transfer_ticket = forge_operation.forge_transfer_ticket + smart_rollup_add_messages = forge_operation.forge_smart_rollup_add_messages + smart_rollup_execute_outbox_message = forge_operation.forge_smart_rollup_execute_outbox_message + + @staticmethod + def proposals(content: Dict[str, Any]) -> bytes: + """Forge a Tezos proposals.""" + res = forge_tag(operation_tags[content['kind']]) + res += forge_address(content['source'], tz_only=True) + res += forge_int32(int(content['period'])) + res += forge_array(b''.join(map(forge_base58, content['proposals']))) + return res + + BALLOT_TAG = { 'yay': 0, 'nay': 1, 'pass': 2 } + + @staticmethod + def ballot(content: Dict[str, Any]) -> bytes: + """Forge a Tezos ballot.""" + res = forge_tag(operation_tags[content['kind']]) + res += forge_address(content['source'], tz_only=True) + res += forge_int32(int(content['period'])) + res += forge_base58(content['proposal']) + res += forge_int_fixed(OperationForge.BALLOT_TAG[content['ballot']], 1) + return res + + @staticmethod + def set_deposit_limit(content: Dict[str, Any]) -> bytes: + """Forge a Tezos set-deposit-limit.""" + res = forge_tag(operation_tags[content['kind']]) + res += forge_address(content['source'], tz_only=True) + res += forge_nat(int(content['fee'])) + res += forge_nat(int(content['counter'])) + res += forge_nat(int(content['gas_limit'])) + res += forge_nat(int(content['storage_limit'])) + + if content.get('limit'): + res += forge_bool(True) + res += forge_nat(int(content['limit'])) + else: + res += forge_bool(False) + + return res + + @staticmethod + def increase_paid_storage(content: Dict[str, Any]) -> bytes: + """Forge a Tezos increase-paid-storage.""" + res = forge_tag(operation_tags[content['kind']]) + res += forge_address(content['source'], tz_only=True) + res += forge_nat(int(content['fee'])) + res += forge_nat(int(content['counter'])) + res += forge_nat(int(content['gas_limit'])) + res += forge_nat(int(content['storage_limit'])) + res += forge_nat(int(content['amount'])) + res += forge_address(content['destination']) + return res + + @staticmethod + def update_consensus_key(content: Dict[str, Any]) -> bytes: + """Forge a Tezos update-consensus-key.""" + res = forge_tag(operation_tags[content['kind']]) + res += forge_address(content['source'], tz_only=True) + res += forge_nat(int(content['fee'])) + res += forge_nat(int(content['counter'])) + res += forge_nat(int(content['gas_limit'])) + res += forge_nat(int(content['storage_limit'])) + res += forge_public_key(content['pk']) + return res + + PVM_KIND_TAG = { 'arith': 0, 'wasm_2_0_0': 1, 'riscv': 2 } + + @staticmethod + def smart_rollup_originate(content: Dict[str, Any]) -> bytes: + """Forge a Tezos smart rollup originate.""" + res = forge_tag(operation_tags[content['kind']]) + res += forge_address(content['source'], tz_only=True) + res += forge_nat(int(content['fee'])) + res += forge_nat(int(content['counter'])) + res += forge_nat(int(content['gas_limit'])) + res += forge_nat(int(content['storage_limit'])) + res += forge_int_fixed( + OperationForge.PVM_KIND_TAG[content['pvm_kind']], 1 + ) + res += forge_array(bytes.fromhex(content['kernel'])) + res += forge_array(forge_micheline(content['parameters_ty'])) + + if content.get('whitelist') is not None: + res += forge_bool(True) + res += forge_array(b''.join( + forge_address(pkh, tz_only=True) + for pkh in content['whitelist'] + )) + else: + res += forge_bool(False) + + return res + +class Operation(Message, OperationBuilder): + """Class representing a tezos operation.""" + + branch: str + + def __init__(self, branch: str = Default.BLOCK_HASH): + self.branch = branch + + @abstractmethod + def forge(self) -> bytes: + """Forge the operation.""" + raise NotImplementedError + + def __bytes__(self) -> bytes: + raw = b'' + raw += forge_int_fixed(Watermark.MANAGER_OPERATION, 1) + raw += forge_base58(self.branch) + raw += self.forge() + return raw + +class Proposals(Operation): + """Class representing a tezos proposals.""" + + proposals_: List[str] + source: str + period: int + + def __init__(self, + proposals: List[str] = [Default.PROTOCOL_HASH], + source: str = Default.ED25519_PUBLIC_KEY_HASH, + period: int = 0, + **kwargs): + self.proposals_ = proposals + self.source = source + self.period = period + Operation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.proposals( + self.proposals( + self.proposals_, + self.source, + self.period + ) + ) + +class Ballot(Operation): + """Class representing a tezos ballot.""" + + proposal : str + ballot_ : str + source : str + period: int + + def __init__(self, + proposal: str = Default.PROTOCOL_HASH, + ballot: str = Default.BALLOT, + source: str = Default.ED25519_PUBLIC_KEY_HASH, + period: int = 0, + **kwargs): + self.proposal = proposal + self.ballot_ = ballot + self.source = source + self.period = period + Operation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.ballot( + self.ballot( + self.proposal, + self.ballot_, + self.source, + self.period + ) + ) + +class FailingNoop(Operation): + """Class representing a tezos failing-noop.""" + + message: str + + def __init__(self, message: str = "", **kwargs): + self.message = message + Operation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.failing_noop(self.failing_noop(self.message)) + +class ManagerOperation(Operation): + """Class representing a tezos manager operation.""" + + source: str + fee: int + counter: int + gas_limit: int + storage_limit: int + + def __init__(self, + source: str = Default.ED25519_PUBLIC_KEY_HASH, + fee: int = 0, + counter: int = 0, + gas_limit: int = 0, + storage_limit: int = 0, + **kwargs): + self.source = source + self.fee = fee + self.counter = counter + self.gas_limit = gas_limit + self.storage_limit = storage_limit + Operation.__init__(self, **kwargs) + +class OperationGroup(Operation): + """Class representing a group of tezos manager operation.""" + + operations: List[ManagerOperation] + + def __init__(self, + operations: List[ManagerOperation] = [], + **kwargs): + self.operations = operations + Operation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return b''.join(map(lambda op: op.forge(), self.operations)) + +class Reveal(ManagerOperation): + """Class representing a tezos reveal.""" + + public_key: str + + def __init__(self, + public_key: str = Default.ED25519_PUBLIC_KEY, + **kwargs): + self.public_key = public_key + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.reveal( + self.reveal( + self.public_key, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class Transaction(ManagerOperation): + """Class representing a tezos transaction.""" + + destination: str + amount: int + entrypoint: str + parameter: Micheline + + def __init__(self, + destination: str = Default.ED25519_PUBLIC_KEY_HASH, + amount: int = 0, + entrypoint: str = Default.ENTRYPOINT, + parameter: Micheline = Default.DefaultMicheline.VALUE, + **kwargs): + self.destination = destination + self.amount = amount + self.entrypoint = entrypoint + self.parameter = parameter + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + parameters = { "entrypoint": self.entrypoint, "value": self.parameter } + return OperationForge.transaction( + self.transaction( + self.destination, + self.amount, + parameters, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class Origination(ManagerOperation): + """Class representing a tezos origination.""" + + code: Micheline + storage: Micheline + balance: int + delegate: Optional[str] + + def __init__(self, + code: Micheline = Default.DefaultMicheline.CODE, + storage: Micheline = Default.DefaultMicheline.TYPE, + balance: int = 0, + delegate: Optional[str] = None, + **kwargs): + self.code = code + self.storage = storage + self.balance = balance + self.delegate = delegate + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + script = { "code": self.code, "storage": self.storage } + return OperationForge.origination( + self.origination( + script, + self.balance, + self.delegate, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class Delegation(ManagerOperation): + """Class representing a tezos delegation.""" + + delegate: Optional[str] + + def __init__(self, + delegate: Optional[str] = None, + **kwargs): + self.delegate = delegate + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.delegation( + self.delegation( + self.delegate, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class RegisterGlobalConstant(ManagerOperation): + """Class representing a tezos register global constant.""" + + value: Micheline + + def __init__(self, + value: Micheline = Default.DefaultMicheline.VALUE, + **kwargs): + self.value = value + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.register_global_constant( + self.register_global_constant( + self.value, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class SetDepositLimit(ManagerOperation): + """Class representing a tezos set deposit limit.""" + + limit: Optional[int] + + def __init__(self, + limit: Optional[int] = None, + **kwargs): + self.limit = limit + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.set_deposit_limit( + self.set_deposit_limit( + self.limit, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class IncreasePaidStorage(ManagerOperation): + """Class representing a tezos increase paid storage.""" + + amount: int + destination: str + + def __init__(self, + amount: int = 0, + destination: str = Default.ORIGINATED_ADDRESS, + **kwargs): + self.amount = amount + self.destination = destination + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.increase_paid_storage( + self.increase_paid_storage( + self.amount, + self.destination, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class UpdateConsensusKey(ManagerOperation): + """Class representing a tezos update consensus key.""" + + pk: str + + def __init__(self, + pk: str = Default.ED25519_PUBLIC_KEY, + **kwargs): + self.pk = pk + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.update_consensus_key( + self.update_consensus_key( + self.pk, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class TransferTicket(ManagerOperation): + """Class representing a tezos transfer ticket.""" + + ticket_contents: Micheline + ticket_ty: Micheline + ticket_ticketer: str + ticket_amount: int + destination: str + entrypoint: str + + def __init__(self, + ticket_contents: Micheline = Default.DefaultMicheline.VALUE, + ticket_ty: Micheline = Default.DefaultMicheline.TYPE, + ticket_ticketer: str = Default.ORIGINATED_ADDRESS, + ticket_amount: int = 0, + destination: str = Default.ORIGINATED_ADDRESS, + entrypoint: str = Default.ENTRYPOINT, + **kwargs): + self.ticket_contents = ticket_contents + self.ticket_ty = ticket_ty + self.ticket_ticketer = ticket_ticketer + self.ticket_amount = ticket_amount + self.destination = destination + self.entrypoint = entrypoint + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.transfer_ticket( + self.transfer_ticket( + self.ticket_contents, + self.ticket_ty, + self.ticket_ticketer, + self.ticket_amount, + self.destination, + self.entrypoint, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class ScRollupOriginate(ManagerOperation): + """Class representing a tezos smart rollup originate.""" + + pvm_kind: str + kernel: str + parameters_ty: Micheline + whitelist: Optional[List[str]] + + def __init__(self, + pvm_kind: str = Default.SMART_ROLLUP_KIND, + kernel: str = "", + parameters_ty: Micheline = Default.DefaultMicheline.TYPE, + whitelist: Optional[List[str]] = None, + **kwargs): + self.pvm_kind = pvm_kind + self.kernel = kernel + self.parameters_ty = parameters_ty + self.whitelist = whitelist + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.smart_rollup_originate( + self.smart_rollup_originate( + self.pvm_kind, + self.kernel, + self.parameters_ty, + self.whitelist, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class ScRollupAddMessage(ManagerOperation): + """Class representing a tezos smart rollup add message.""" + + message: List[bytes] + + def __init__(self, + message: List[bytes] = [b''], + **kwargs): + self.message = message + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.smart_rollup_add_messages( + self.smart_rollup_add_messages( + self.message, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) + +class ScRollupExecuteOutboxMessage(ManagerOperation): + """Class representing a tezos smart rollup execute outbox message.""" + + rollup: str + cemented_commitment: str + output_proof: bytes + + def __init__(self, + rollup: str = Default.ORIGINATED_SMART_ROLLUP_ADDRESS, + cemented_commitment: str = Default.SMART_ROLLUP_COMMITMENT_HASH, + output_proof: bytes = b'', + **kwargs): + self.rollup = rollup + self.cemented_commitment = cemented_commitment + self.output_proof = output_proof + ManagerOperation.__init__(self, **kwargs) + + def forge(self) -> bytes: + return OperationForge.smart_rollup_execute_outbox_message( + self.smart_rollup_execute_outbox_message( + self.rollup, + self.cemented_commitment, + self.output_proof, + self.source, + self.counter, + self.fee, + self.gas_limit, + self.storage_limit + ) + ) diff --git a/tests/integration/run_test_docker.sh b/tests/integration/run_test_docker.sh index cdb41f286..3a8ed0e52 100755 --- a/tests/integration/run_test_docker.sh +++ b/tests/integration/run_test_docker.sh @@ -33,5 +33,6 @@ docker run --rm -it -v "$(realpath .):/app" \ -c " apk add gmp-dev curl jq libsodium-dev git xxd procps; \ python3 -m venv tezos_test_env --system-site-package; \ source ./tezos_test_env/bin/activate; \ + python3 -m pip install --upgrade pip -q; \ python3 -m pip install -r tests/requirements.txt -q; \ ./tests/integration/run_test_local.sh -F -m $*" diff --git a/tests/integration/touch/snapshots/flex/info_flex_1.png b/tests/integration/touch/snapshots/flex/info_flex_1.png index 352fc1482..674bfb5e5 100644 Binary files a/tests/integration/touch/snapshots/flex/info_flex_1.png and b/tests/integration/touch/snapshots/flex/info_flex_1.png differ diff --git a/tests/integration/touch/snapshots/flex/test_sign_micheline/micheline_screen.png b/tests/integration/touch/snapshots/flex/test_sign_micheline/micheline_screen.png new file mode 100644 index 000000000..9426c7548 Binary files /dev/null and b/tests/integration/touch/snapshots/flex/test_sign_micheline/micheline_screen.png differ diff --git a/tests/integration/touch/snapshots/flex/test_sign_micheline/operation_sign.png b/tests/integration/touch/snapshots/flex/test_sign_micheline/operation_sign.png new file mode 100644 index 000000000..5e787d8ed Binary files /dev/null and b/tests/integration/touch/snapshots/flex/test_sign_micheline/operation_sign.png differ diff --git a/tests/integration/touch/snapshots/flex/test_sign_micheline/signing_successful.png b/tests/integration/touch/snapshots/flex/test_sign_micheline/signing_successful.png new file mode 100644 index 000000000..be51a9d55 Binary files /dev/null and b/tests/integration/touch/snapshots/flex/test_sign_micheline/signing_successful.png differ diff --git a/tests/integration/touch/snapshots/stax/info_stax.png b/tests/integration/touch/snapshots/stax/info_stax.png index aa09433a3..3f9faf25d 100644 Binary files a/tests/integration/touch/snapshots/stax/info_stax.png and b/tests/integration/touch/snapshots/stax/info_stax.png differ diff --git a/tests/integration/touch/snapshots/stax/test_sign_micheline/micheline_screen.png b/tests/integration/touch/snapshots/stax/test_sign_micheline/micheline_screen.png new file mode 100644 index 000000000..9ef4d5372 Binary files /dev/null and b/tests/integration/touch/snapshots/stax/test_sign_micheline/micheline_screen.png differ diff --git a/tests/integration/touch/snapshots/stax/test_sign_micheline/operation_sign.png b/tests/integration/touch/snapshots/stax/test_sign_micheline/operation_sign.png new file mode 100644 index 000000000..c1e5457de Binary files /dev/null and b/tests/integration/touch/snapshots/stax/test_sign_micheline/operation_sign.png differ diff --git a/tests/integration/touch/snapshots/stax/test_sign_micheline/signing_successful.png b/tests/integration/touch/snapshots/stax/test_sign_micheline/signing_successful.png new file mode 100644 index 000000000..392165d4f Binary files /dev/null and b/tests/integration/touch/snapshots/stax/test_sign_micheline/signing_successful.png differ diff --git a/tests/integration/touch/test_sign_micheline.py b/tests/integration/touch/test_sign_micheline.py new file mode 100644 index 000000000..1aa67c383 --- /dev/null +++ b/tests/integration/touch/test_sign_micheline.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# Copyright 2023 Trilitech + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from utils import ( + tezos_app, + send_payload +) + +# full input: 0502000000080100000001300000 +# full output: Expression {"0";0} + +if __name__ == "__main__": + app = tezos_app(__file__) + + app.assert_home() + + app.send_initialize_msg("8004000011048000002c800006c18000000080000000") + send_payload(app, "800481ff0e0502000000080100000001300000") + + app.review.next() + app.assert_screen("micheline_screen") + + expected_apdu = "c871155802f26f35ea07a15c8002d836a8779ac8cc2feb0141f08831dad0326a0905cd4fc8f00615b7b4a78e7eaf7fcc0b7959e01e3119b6261ebd71d76d580b9000" + app.review_confirm_signing(expected_apdu) + + app.assert_home() + app.quit() diff --git a/tests/requirements.txt b/tests/requirements.txt index 419eeb132..751725d2e 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -3,6 +3,7 @@ setuptools pkginfo cryptography base58 -pytezos +pytezos==3.13.4 GitPython pytest-xdist +ragger[tests,speculos,ledgerwallet]