diff --git a/test/python/test_instructions.py b/test/python/test_instructions.py index 519ac781..be657d0e 100644 --- a/test/python/test_instructions.py +++ b/test/python/test_instructions.py @@ -1,7 +1,7 @@ """Module gathering the baking app instruction tests.""" from pathlib import Path -from typing import Callable, Tuple +from typing import Callable, Optional, Tuple import pytest from pytezos import pytezos from ragger.firmware import Firmware @@ -22,7 +22,7 @@ Block, DEFAULT_CHAIN_ID ) -from utils.navigator import TezosNavigator +from utils.navigator import TezosNavigator, send_and_navigate from common import ( DEFAULT_ACCOUNT, DEFAULT_ACCOUNT_2, @@ -838,6 +838,138 @@ def test_sign_transaction( client.sign_message(account_1, transaction) +def build_reveal(account: Account) -> Reveal: + """Build a reveal.""" + return Reveal( + public_key=account.public_key, + source=account.public_key_hash, + ) +def build_delegation(account: Account) -> Delegation: + """Build a delegation.""" + return Delegation( + delegate=account.public_key_hash, + source=account.public_key_hash, + ) +def build_transaction(account: Account) -> UnsafeOp: + """Build a transaction.""" + ctxt = pytezos.using() + return UnsafeOp( + ctxt.transaction( + source=account.public_key_hash, + destination=DEFAULT_ACCOUNT_2.public_key_hash, + amount=10_000, + ) + ) +def build_bad_reveal_1(account: Account) -> Reveal: + """Build a bad reveal.""" + return Reveal( + public_key=account.public_key, + source=DEFAULT_ACCOUNT_2.public_key_hash, + ) +def build_bad_reveal_2(account: Account) -> Reveal: + """Build an other bad reveal.""" + return Reveal( + public_key=DEFAULT_ACCOUNT_2.public_key, + source=account.public_key_hash, + ) +def build_bad_delegation_1(account: Account) -> Delegation: + """Build a bad delegation.""" + return Delegation( + delegate=account.public_key_hash, + source=DEFAULT_ACCOUNT_2.public_key_hash, + ) +def build_bad_delegation_2(account: Account) -> Delegation: + """Build a other bad delegation.""" + return Delegation( + delegate=DEFAULT_ACCOUNT_2.public_key_hash, + source=account.public_key_hash, + ) + + +# Warning: operation PARSE_ERROR are not available on DEBUG-mode +PARAMETERS_SIGN_MULTIPLE_OPERATIONS = [ + (build_reveal, build_reveal, None, False, StatusCode.OK ), + (build_reveal, build_delegation, None, True, StatusCode.OK ), + (build_delegation, build_reveal, None, True, StatusCode.OK ), + (build_reveal, build_delegation, build_reveal, True, StatusCode.OK ), +] + [ + (build_bad_reveal_1, build_reveal, None, False, StatusCode.OK ), + (build_bad_reveal_1, build_delegation, None, True, StatusCode.OK ), + (build_bad_reveal_2, build_reveal, None, False, StatusCode.PARSE_ERROR), + (build_bad_reveal_2, build_delegation, None, True, StatusCode.PARSE_ERROR), + (build_reveal, build_bad_reveal_1, None, False, StatusCode.SECURITY ), + (build_delegation, build_bad_reveal_1, None, True, StatusCode.SECURITY ), + (build_reveal, build_bad_reveal_2, None, False, StatusCode.PARSE_ERROR), + (build_delegation, build_bad_reveal_2, None, True, StatusCode.PARSE_ERROR), + (build_reveal, build_bad_delegation_1, None, True, StatusCode.PARSE_ERROR), + (build_reveal, build_bad_delegation_2, None, True, StatusCode.SECURITY ), + (build_bad_delegation_1, build_reveal, None, True, StatusCode.PARSE_ERROR), + (build_bad_delegation_2, build_reveal, None, True, StatusCode.SECURITY ), + (build_reveal, build_transaction, None, False, StatusCode.PARSE_ERROR), + (build_transaction, build_reveal, None, False, StatusCode.PARSE_ERROR), + (build_delegation, build_delegation, None, True, StatusCode.PARSE_ERROR), + (build_delegation, build_delegation, None, True, StatusCode.PARSE_ERROR), + (build_reveal, build_delegation, build_delegation, True, StatusCode.PARSE_ERROR), + (build_delegation, build_delegation, build_reveal, True, StatusCode.PARSE_ERROR), + (build_reveal, build_delegation, build_transaction, True, StatusCode.PARSE_ERROR), + (build_transaction, build_delegation, build_reveal, True, StatusCode.PARSE_ERROR), +] + +@pytest.mark.parametrize( + "operation_builder_1," \ + "operation_builder_2," \ + "operation_builder_3," \ + "operation_display," \ + "status_code", + PARAMETERS_SIGN_MULTIPLE_OPERATIONS +) +def test_sign_multiple_operation( + operation_builder_1: Callable[[Account], UnsafeOp], + operation_builder_2: Callable[[Account], UnsafeOp], + operation_builder_3: Optional[Callable[[Account], UnsafeOp]], + status_code: StatusCode, + operation_display: bool, + client: TezosClient, + tezos_navigator: TezosNavigator) -> None: + """Test multiple operations signing constraints.""" + + account = DEFAULT_ACCOUNT + + tezos_navigator.setup_app_context( + account, + DEFAULT_CHAIN_ID, + main_hwm=Hwm(0), + test_hwm=Hwm(0) + ) + + operation = operation_builder_1(account) + operation = operation.merge( + operation_builder_2(account) + ) + if operation_builder_3 is not None: + operation = operation.merge( + operation_builder_3(account) + ) + + message = operation.forge() + + with status_code.expected(): + if operation_display: + signature = send_and_navigate( + send=lambda: client.sign_message( + account, + message + ), + navigate=tezos_navigator.accept_sign_navigate + ) + else: + signature = client.sign_message( + account, + message + ) + account.check_signature(signature, bytes(message)) + + def test_sign_when_no_chain_setup( client: TezosClient, tezos_navigator: TezosNavigator) -> None: diff --git a/test/python/utils/client.py b/test/python/utils/client.py index 45212edf..1d684fb0 100644 --- a/test/python/utils/client.py +++ b/test/python/utils/client.py @@ -162,7 +162,8 @@ def expected(self) -> Generator[None, None, None]: """Fail if the right RAPDU code exception is not raise.""" try: yield - assert False, f"Expect fail with { self.name } but succeed" + assert self == StatusCode.OK, \ + f"Expect fail with { self.name } but succeed" except ExceptionRAPDU as e: try: name = f"{StatusCode(e.status).name}" diff --git a/test/python/utils/message.py b/test/python/utils/message.py index 4dcdbf38..b88fcd92 100644 --- a/test/python/utils/message.py +++ b/test/python/utils/message.py @@ -118,6 +118,12 @@ def forge(self, branch: str = DEFAULT_BLOCK_HASH) -> Message: raw = watermark + bytes.fromhex(self.operation.forge()) return RawMessage(raw) + def merge(self, unsafe_op: 'UnsafeOp') -> 'UnsafeOp': + res = self.operation + for content in unsafe_op.operation.contents: + res = res.operation(content) + return UnsafeOp(res) + class Delegation(UnsafeOp): """Class representing a delegation.""" diff --git a/test/python/utils/navigator.py b/test/python/utils/navigator.py index 86bc3e6d..21a2096e 100644 --- a/test/python/utils/navigator.py +++ b/test/python/utils/navigator.py @@ -370,7 +370,7 @@ def sign_delegation_with_hash(self, account: Account, delegation: Delegation, branch: str = DEFAULT_BLOCK_HASH, - navigate: Optional[Callable] = None, + navigate: Optional[Callable] = None, **kwargs) -> Tuple[bytes, Signature]: """Send a sign and get hash request on delegation and navigate until accept""" if navigate is None: