-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d7a01a6
commit bbcf0d1
Showing
3 changed files
with
182 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
## |