Skip to content

Commit

Permalink
Updated test to work with signing.
Browse files Browse the repository at this point in the history
  • Loading branch information
joaoccmartins committed Jan 31, 2025
1 parent d7a01a6 commit bbcf0d1
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 98 deletions.
87 changes: 85 additions & 2 deletions test/python/apps/aptos.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,91 @@
from ragger.utils import create_currency_config
from enum import IntEnum
from ragger.utils import create_currency_config, RAPDU
from ragger.bip import pack_derivation_path
from typing import List, Optional

APTOS_CONF = create_currency_config("APT", "Aptos")
APTOS_DERIVATION_PATH = "m/44'/637'/0'"
APTOS_PACKED_DERIVATION_PATH = pack_derivation_path(APTOS_DERIVATION_PATH)

MAX_APDU_LEN: int = 255
MAX_APDU_LEN: int = 255

CLA: int = 0x5B

class P1(IntEnum):
# Parameter 1 for first APDU number.
P1_START = 0x00
# Parameter 1 for maximum APDU number.
P1_MAX = 0x03
# Parameter 1 for screen confirmation for GET_PUBLIC_KEY.
P1_CONFIRM = 0x01

class P2(IntEnum):
# Parameter 2 for last APDU to receive.
P2_LAST = 0x00
# Parameter 2 for more APDU to receive.
P2_MORE = 0x80

class InsType(IntEnum):
GET_VERSION = 0x03
GET_APP_NAME = 0x04
GET_PUBLIC_KEY = 0x05
SIGN_TX = 0x06

class Errors(IntEnum):
SW_DENY = 0x6985
SW_WRONG_P1P2 = 0x6A86
SW_WRONG_DATA_LENGTH = 0x6A87
SW_INS_NOT_SUPPORTED = 0x6D00
SW_CLA_NOT_SUPPORTED = 0x6E00
SW_WRONG_RESPONSE_LENGTH = 0xB000
SW_DISPLAY_BIP32_PATH_FAIL = 0xB001
SW_DISPLAY_ADDRESS_FAIL = 0xB002
SW_DISPLAY_AMOUNT_FAIL = 0xB003
SW_WRONG_TX_LENGTH = 0xB004
SW_TX_PARSING_FAIL = 0xB005
SW_GET_PUB_KEY_FAIL = 0xB006
SW_BAD_STATE = 0xB007
SW_SIGNATURE_FAIL = 0xB008
SW_DISPLAY_GAS_FEE_FAIL = 0xB009


def split_message(message: bytes, max_size: int) -> List[bytes]:
return [message[x:x + max_size] for x in range(0, len(message), max_size)]


# As copied from the Nano App tester
class AptosCommandSender:
def __init__(self, backend) -> None:
self.backend = backend

def sign_tx(self, path: str, transaction: bytes) -> List[RAPDU]:
apdus = []
# TODO: for some reason, this is stalling the function
packed_path = pack_derivation_path(path)
apdus.append(self.backend.exchange(cla=CLA,
ins=InsType.SIGN_TX,
p1=P1.P1_START,
p2=P2.P2_MORE,
data=packed_path))
messages = split_message(transaction, MAX_APDU_LEN)
idx: int = P1.P1_START + 1

for msg in messages[:-1]:
apdus.append(self.backend.exchange(cla=CLA,
ins=InsType.SIGN_TX,
p1=idx,
p2=P2.P2_MORE,
data=msg))
idx += 1

with self.backend.exchange_async(cla=CLA,
ins=InsType.SIGN_TX,
p1=idx,
p2=P2.P2_LAST,
data=messages[-1]) as response:
apdus.append(response)
return response

def get_async_response(self) -> Optional[RAPDU]:
return self.backend.last_async_response

12 changes: 6 additions & 6 deletions test/python/test_aptos.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from .apps.exchange_test_runner import ExchangeTestRunner, ALL_TESTS_EXCEPT_MEMO_THORSWAP_AND_FEES
from .apps import cal as cal
from .apps.aptos import AptosCommandSender

# ExchangeTestRunner implementation for Ton
class AptosTests(ExchangeTestRunner):
Expand All @@ -22,16 +23,15 @@ class AptosTests(ExchangeTestRunner):
fake_payout = "abcdabcd"
fake_payout_memo = "1"
def perform_final_tx(self, destination, send_amount, fees, memo):
assert False

print("----------------------perform_final_tx----------------------")
if destination != self.valid_destination_1 or destination == self.valid_destination_2:
assert False
transaction = bytes.fromhex("b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b193094c6fc0d3b382a599c37e1aaa7618eff2c96a3586876082c4594c50c50d7dde1b000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e736665720002203835075df1bf469c336eabed8ac87052ee4485f3ec93380a5382fbf76b7a33070840420f000000000006000000000000006400000000000000c39aa4640000000002")
AptosCommandSender(self.backend).sign_tx(path="m/44'/637'/0''",transaction=transaction)

# Use a class to reuse the same Speculos instance
class TestsAptos:

@pytest.mark.parametrize('test_to_run', ALL_TESTS_EXCEPT_MEMO_THORSWAP_AND_FEES)
def test_aptos(self, backend, exchange_navigation_helper, test_to_run):
AptosTests(backend, exchange_navigation_helper).run_test(test_to_run)


def test_eval():
assert False
181 changes: 91 additions & 90 deletions test/python/test_ton.py
Original file line number Diff line number Diff line change
@@ -1,90 +1,91 @@
import pytest
import os

from .apps.ton_application_client.ton_transaction import Transaction, SendMode, CommentPayload, Payload, JettonTransferPayload, NFTTransferPayload, CustomUnsafePayload, JettonBurnPayload, AddWhitelistPayload, SingleNominatorWithdrawPayload, ChangeValidatorPayload, TonstakersDepositPayload, JettonDAOVotePayload, ChangeDNSWalletPayload, ChangeDNSPayload, TokenBridgePaySwapPayload
from .apps.ton_application_client.ton_command_sender import BoilerplateCommandSender, Errors
from .apps.ton_application_client.ton_response_unpacker import unpack_sign_tx_response
from .apps.ton_utils import check_signature_validity

from tonsdk.utils import Address

from .apps.exchange_test_runner import ExchangeTestRunner, ALL_TESTS_EXCEPT_MEMO_THORSWAP_AND_FEES
from .apps.ton import DEVICE_PUBLIC_KEY, Bounceability, WorkchainID, craft_address, SW_SWAP_FAILURE, TON_DERIVATION_PATH
from .apps import cal as cal

# ExchangeTestRunner implementation for Ton
class TonTests(ExchangeTestRunner):
currency_configuration = cal.TON_CURRENCY_CONFIGURATION
valid_destination_1 = "EQD0sKn8DbS12U015TWOSpYmyJYYDC_7sxg1upaMxnBvTiX8"
valid_destination_2 = "EQANxfGN1EgFPawYB1fhPqebKe1Nb6FIsaiekEecJ6R-3kYF"
# valid_refund = craft_address(Bounceability.NON_BOUNCEABLE, WorkchainID.BASE_CHAIN, DEVICE_PUBLIC_KEY).decode('utf-8')
valid_refund = "UQDWey_FGPhd3phmerdVXi-zUIujfyO4-29y_VT1yD0meY1n"
valid_send_amount_1 = 12345670000
valid_send_amount_2 = 446739662
valid_fees_1 = 100000000
valid_fees_2 = 10000123
fake_refund = "EQD0sKn8DbS12U015TWOSpYmyJYYDC_7sxg1upaMxnBvTiX8"
fake_payout = "EQD0sKn8DbS12U015TWOSpYmyJYYDC_7sxg1upaMxnBvTiX8"
wrong_method_error_code = SW_SWAP_FAILURE
wrong_destination_error_code = SW_SWAP_FAILURE
wrong_amount_error_code = SW_SWAP_FAILURE

def perform_final_tx(self, destination, send_amount, fees, memo):
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(self.backend)

# First we need to get the public key of the device in order to build the transaction
pubkey = client.get_public_key(path=TON_DERIVATION_PATH).data

# Create the transaction that will be sent to the device for signing
tx = Transaction(Address(destination), SendMode.PAY_GAS_SEPARATLY, 0, 1686176000, True, send_amount)
tx_bytes = tx.to_request_bytes()

# Send the sign device instruction.
# As it requires on-screen validation, the function is asynchronous.
# It will yield the result when the navigation is done
with client.sign_tx(path=TON_DERIVATION_PATH, transaction=tx_bytes):
pass

# The device as yielded the result, parse it and ensure that the signature is correct
response = client.get_async_response().data
sig, hash_b = unpack_sign_tx_response(response)
assert hash_b == tx.transfer_cell().bytes_hash()
assert check_signature_validity(pubkey, sig, hash_b)

class TonUSDTTests(TonTests):
currency_configuration = cal.TON_CURRENCY_CONFIGURATION
def perform_final_tx(self, destination, send_amount, fees, memo):
# Use the app interface instead of raw interface
client = BoilerplateCommandSender(self.backend)

# First we need to get the public key of the device in order to build the transaction
pubkey = client.get_public_key(path=TON_DERIVATION_PATH).data

payload = JettonTransferPayload(100, Address("0:" + "0" * 64), forward_amount=1)

tx = Transaction(Address("0:" + "0" * 64), SendMode.PAY_GAS_SEPARATLY, 0, 1686176000, True, 100000000, payload=payload)
tx_bytes = tx.to_request_bytes()

with client.sign_tx(path=TON_DERIVATION_PATH, transaction=tx_bytes):
pass

# The device as yielded the result, parse it and ensure that the signature is correct
response = client.get_async_response().data
sig, hash_b = unpack_sign_tx_response(response)
assert hash_b == tx.transfer_cell().bytes_hash()
assert check_signature_validity(pubkey, sig, hash_b)


# Use a class to reuse the same Speculos instance
class TestsTon:

@pytest.mark.parametrize('test_to_run', ALL_TESTS_EXCEPT_MEMO_THORSWAP_AND_FEES)
def test_ton(self, backend, exchange_navigation_helper, test_to_run):
if backend.firmware.device == "nanos":
pytest.skip("Polkadot swap is not supported on NanoS device")
TonTests(backend, exchange_navigation_helper).run_test(test_to_run)

# @pytest.mark.parametrize('test_to_run', ALL_TESTS_EXCEPT_MEMO_THORSWAP_AND_FEES)
# def test_ton_usdt(self, backend, exchange_navigation_helper, test_to_run):
# TonUSDTTests(backend, exchange_navigation_helper).run_test(test_to_run)
##import pytest
##import os
##
##from .apps.ton_application_client.ton_transaction import Transaction, SendMode, CommentPayload, Payload, JettonTransferPayload, NFTTransferPayload, CustomUnsafePayload, JettonBurnPayload, AddWhitelistPayload, SingleNominatorWithdrawPayload, ChangeValidatorPayload, TonstakersDepositPayload, JettonDAOVotePayload, ChangeDNSWalletPayload, ChangeDNSPayload, TokenBridgePaySwapPayload
##from .apps.ton_application_client.ton_command_sender import BoilerplateCommandSender, Errors
##from .apps.ton_application_client.ton_response_unpacker import unpack_sign_tx_response
##from .apps.ton_utils import check_signature_validity
##
##from tonsdk.utils import Address
##
##from .apps.exchange_test_runner import ExchangeTestRunner, ALL_TESTS_EXCEPT_MEMO_THORSWAP_AND_FEES
##from .apps.ton import DEVICE_PUBLIC_KEY, Bounceability, WorkchainID, craft_address, SW_SWAP_FAILURE, TON_DERIVATION_PATH
##from .apps import cal as cal
##
### ExchangeTestRunner implementation for Ton
##class TonTests(ExchangeTestRunner):
## currency_configuration = cal.TON_CURRENCY_CONFIGURATION
## valid_destination_1 = "EQD0sKn8DbS12U015TWOSpYmyJYYDC_7sxg1upaMxnBvTiX8"
## valid_destination_2 = "EQANxfGN1EgFPawYB1fhPqebKe1Nb6FIsaiekEecJ6R-3kYF"
## # valid_refund = craft_address(Bounceability.NON_BOUNCEABLE, WorkchainID.BASE_CHAIN, DEVICE_PUBLIC_KEY).decode('utf-8')
## valid_refund = "UQDWey_FGPhd3phmerdVXi-zUIujfyO4-29y_VT1yD0meY1n"
## valid_send_amount_1 = 12345670000
## valid_send_amount_2 = 446739662
## valid_fees_1 = 100000000
## valid_fees_2 = 10000123
## fake_refund = "EQD0sKn8DbS12U015TWOSpYmyJYYDC_7sxg1upaMxnBvTiX8"
## fake_payout = "EQD0sKn8DbS12U015TWOSpYmyJYYDC_7sxg1upaMxnBvTiX8"
## wrong_method_error_code = SW_SWAP_FAILURE
## wrong_destination_error_code = SW_SWAP_FAILURE
## wrong_amount_error_code = SW_SWAP_FAILURE
##
## def perform_final_tx(self, destination, send_amount, fees, memo):
## # Use the app interface instead of raw interface
## client = BoilerplateCommandSender(self.backend)
##
## # First we need to get the public key of the device in order to build the transaction
## pubkey = client.get_public_key(path=TON_DERIVATION_PATH).data
##
## # Create the transaction that will be sent to the device for signing
## tx = Transaction(Address(destination), SendMode.PAY_GAS_SEPARATLY, 0, 1686176000, True, send_amount)
## tx_bytes = tx.to_request_bytes()
##
## # Send the sign device instruction.
## # As it requires on-screen validation, the function is asynchronous.
## # It will yield the result when the navigation is done
## with client.sign_tx(path=TON_DERIVATION_PATH, transaction=tx_bytes):
## pass
##
## # The device as yielded the result, parse it and ensure that the signature is correct
## response = client.get_async_response().data
## sig, hash_b = unpack_sign_tx_response(response)
## assert hash_b == tx.transfer_cell().bytes_hash()
## assert check_signature_validity(pubkey, sig, hash_b)
##
##class TonUSDTTests(TonTests):
## currency_configuration = cal.TON_CURRENCY_CONFIGURATION
## def perform_final_tx(self, destination, send_amount, fees, memo):
## # Use the app interface instead of raw interface
## client = BoilerplateCommandSender(self.backend)
##
## # First we need to get the public key of the device in order to build the transaction
## pubkey = client.get_public_key(path=TON_DERIVATION_PATH).data
##
## payload = JettonTransferPayload(100, Address("0:" + "0" * 64), forward_amount=1)
##
## tx = Transaction(Address("0:" + "0" * 64), SendMode.PAY_GAS_SEPARATLY, 0, 1686176000, True, 100000000, payload=payload)
## tx_bytes = tx.to_request_bytes()
##
## with client.sign_tx(path=TON_DERIVATION_PATH, transaction=tx_bytes):
## pass
##
## # The device as yielded the result, parse it and ensure that the signature is correct
## response = client.get_async_response().data
## sig, hash_b = unpack_sign_tx_response(response)
## assert hash_b == tx.transfer_cell().bytes_hash()
## assert check_signature_validity(pubkey, sig, hash_b)
##
##
### Use a class to reuse the same Speculos instance
##class TestsTon:
##
## @pytest.mark.parametrize('test_to_run', ALL_TESTS_EXCEPT_MEMO_THORSWAP_AND_FEES)
## def test_ton(self, backend, exchange_navigation_helper, test_to_run):
## if backend.firmware.device == "nanos":
## pytest.skip("Polkadot swap is not supported on NanoS device")
## TonTests(backend, exchange_navigation_helper).run_test(test_to_run)
##
## # @pytest.mark.parametrize('test_to_run', ALL_TESTS_EXCEPT_MEMO_THORSWAP_AND_FEES)
## # def test_ton_usdt(self, backend, exchange_navigation_helper, test_to_run):
## # TonUSDTTests(backend, exchange_navigation_helper).run_test(test_to_run)
##

0 comments on commit bbcf0d1

Please sign in to comment.