forked from LedgerHQ/app-everscale
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'test/LDG-662--nano-app-migrate-tests-from-rust-to-pytho…
…n-rag' into feat/LDG-679--nano-app-prepare-the-codebase-for-nbgl-devices
- Loading branch information
Showing
66 changed files
with
1,194 additions
and
3 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,17 +1,34 @@ | ||
# Build and compilation directories (Répertoires de compilation) | ||
bin | ||
debug | ||
dep | ||
obj | ||
dev-env | ||
|
||
# Generated source files | ||
src/glyphs.c | ||
src/glyphs.h | ||
|
||
# IDE/editor specific files | ||
.idea | ||
.vscode | ||
gitignore | ||
|
||
# Dependency directories | ||
node_modules | ||
|
||
# Log files and test specifications | ||
*.log* | ||
*.spec | ||
|
||
# Build output and distribution directories | ||
build | ||
dist | ||
*.spec | ||
*.DS_Store | ||
client/target | ||
|
||
# Operating system files | ||
*.DS_Store | ||
|
||
# Python cache and temporary files | ||
__pycache__ | ||
snapshots-tmp |
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,4 +1,7 @@ | ||
[app] | ||
build_directory = "./" | ||
sdk = "C" | ||
devices = ["nanos", "nanos+", "nanox", "flex", "stax"] | ||
devices = ["nanos+", "nanox"] | ||
|
||
[tests] | ||
pytest_directory = "./tests/" |
Empty file.
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 |
---|---|---|
@@ -0,0 +1,166 @@ | ||
from enum import IntEnum | ||
from typing import Generator, List, Optional | ||
from contextlib import contextmanager | ||
|
||
from ragger.backend.interface import BackendInterface, RAPDU | ||
from ragger.bip import pack_derivation_path | ||
|
||
|
||
MAX_APDU_LEN: int = 255 | ||
|
||
CLA: int = 0xE0 | ||
|
||
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_APP_CONFIGURATION = 0x01 | ||
GET_PUBLIC_KEY = 0x02 | ||
SIGN_MESSAGE = 0x03 | ||
GET_ADDRESS = 0X04 | ||
SIGN_TRANSACTION = 0x05 | ||
|
||
class WalletType(IntEnum): | ||
WALLET_V3 = 0 | ||
EVER_WALLET = 1 | ||
SAFE_MULTISIG_WALLET = 2 | ||
SAFE_MULTISIG_WALLET_24H = 3 | ||
SETCODE_MULTISIG_WALLET = 4 | ||
BRIDGE_MULTISIG_WALLET = 5 | ||
SURF_WALLET = 6 | ||
MULTISIG_2 = 7 | ||
MULTISIG_2_1 = 8 | ||
|
||
class Errors(IntEnum): | ||
SW_INVALID_DATA = 0x6B00, | ||
SW_CELL_UNDERFLOW = 0x6B01, | ||
SW_RANGE_CHECK_FAIL = 0x6B02, | ||
SW_WRONG_LABEL = 0x6B03, | ||
SW_INVALID_FLAG = 0x6B04, | ||
SW_END_OF_STREAM = 0x6B05, | ||
SW_SLICE_IS_EMPTY = 0x6B06, | ||
SW_INVALID_KEY = 0x6B07, | ||
SW_CELL_IS_EMPTY = 0x6B08, | ||
SW_INVALID_HASH = 0x6B09, | ||
SW_INVALID_CELL_INDEX = 0x6B10, | ||
SW_INVALID_REQUEST = 0x6B11, | ||
SW_INVALID_FUNCTION_ID = 0x6B12, | ||
SW_INVALID_SRC_ADDRESS = 0x6B13, | ||
SW_INVALID_WALLET_ID = 0x6B14, | ||
SW_INVALID_WALLET_TYPE = 0x6B15, | ||
SW_INVALID_TICKER_LENGTH = 0x6B16, | ||
SW_DENY = 0x6985, | ||
# Status Word from everscale app | ||
# 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_TX_HASH_FAIL = 0xB006 | ||
# SW_BAD_STATE = 0xB007 | ||
# SW_SIGNATURE_FAIL = 0xB008 | ||
|
||
|
||
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)] | ||
|
||
|
||
class EverscaleCommandSender: | ||
def __init__(self, backend: BackendInterface) -> None: | ||
self.backend = backend | ||
|
||
|
||
def get_app_and_version(self) -> RAPDU: | ||
return self.backend.exchange(cla=0xB0, # specific CLA for BOLOS | ||
ins=0x01, # specific INS for get_app_and_version | ||
p1=P1.P1_START, | ||
p2=P2.P2_LAST, | ||
data=b"") | ||
|
||
|
||
def get_app_config(self) -> RAPDU: | ||
return self.backend.exchange(cla=CLA, | ||
ins=InsType.GET_APP_CONFIGURATION, | ||
p1=P1.P1_START, | ||
p2=P2.P2_LAST, | ||
data=b"") | ||
|
||
|
||
def get_app_name(self) -> RAPDU: | ||
return self.backend.exchange(cla=CLA, | ||
ins=InsType.GET_APP_NAME, | ||
p1=P1.P1_START, | ||
p2=P2.P2_LAST, | ||
data=b"") | ||
|
||
|
||
def get_public_key(self, account_number: int) -> RAPDU: | ||
return self.backend.exchange(cla=CLA, | ||
ins=InsType.GET_PUBLIC_KEY, | ||
p1=P1.P1_START, | ||
p2=P2.P2_LAST, | ||
data=account_number.to_bytes(4, "big")) | ||
|
||
|
||
@contextmanager | ||
def get_public_key_with_confirmation(self, account_number: int) -> Generator[None, None, None]: | ||
with self.backend.exchange_async(cla=CLA, | ||
ins=InsType.GET_PUBLIC_KEY, | ||
p1=P1.P1_CONFIRM, | ||
p2=P2.P2_LAST, | ||
data=account_number.to_bytes(4, "big")) as response: | ||
yield response | ||
|
||
def get_address(self, account_number: int, wallet_type: WalletType) -> RAPDU: | ||
return self.backend.exchange(cla=CLA, | ||
ins=InsType.GET_ADDRESS, | ||
p1=P1.P1_START, | ||
p2=P2.P2_LAST, | ||
data=account_number.to_bytes(4, "big") + wallet_type.to_bytes(1, "big")) | ||
|
||
@contextmanager | ||
def get_address_with_confirmation(self, account_number: int, wallet_type: WalletType) -> Generator[None, None, None]: | ||
with self.backend.exchange_async(cla=CLA, | ||
ins=InsType.GET_ADDRESS, | ||
p1=P1.P1_CONFIRM, | ||
p2=P2.P2_LAST, | ||
data=account_number.to_bytes(4, "big") + wallet_type.to_bytes(1, "big")) as response: | ||
yield response | ||
|
||
|
||
@contextmanager | ||
def sign_message(self, account_number: int, wallet_type: WalletType, message: bytes) -> Generator[None, None, None]: | ||
with self.backend.exchange_async(cla=CLA, | ||
ins=InsType.SIGN_MESSAGE, | ||
p1=P1.P1_CONFIRM, | ||
p2=P2.P2_LAST, | ||
data=account_number.to_bytes(4, "big") + wallet_type.to_bytes(1, "big") + message) as response: | ||
yield response | ||
|
||
@contextmanager | ||
def sign_tx(self, account_number: int, wallet_type: WalletType, transaction: bytes) -> Generator[None, None, None]: | ||
with self.backend.exchange_async(cla=CLA, | ||
ins=InsType.SIGN_TX, | ||
p1=P1.P1_START, | ||
p2=P2.P2_LAST, | ||
data=account_number.to_bytes(4, "big") + wallet_type.to_bytes(1, "big") + transaction) as response: | ||
yield 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
from typing import Tuple | ||
from struct import unpack | ||
|
||
# remainder, data_len, data | ||
def pop_sized_buf_from_buffer(buffer:bytes, size:int) -> Tuple[bytes, bytes]: | ||
return buffer[size:], buffer[0:size] | ||
|
||
# remainder, data_len, data | ||
def pop_size_prefixed_buf_from_buf(buffer:bytes) -> Tuple[bytes, int, bytes]: | ||
data_len = buffer[0] | ||
return buffer[1+data_len:], data_len, buffer[1:data_len+1] | ||
|
||
# Unpack from response: | ||
# response = app_name (var) | ||
def unpack_get_app_name_response(response: bytes) -> str: | ||
return response.decode("ascii") | ||
|
||
# Unpack from response: | ||
# response = MAJOR (1) | ||
# MINOR (1) | ||
# PATCH (1) | ||
def unpack_get_version_response(response: bytes) -> Tuple[int, int, int]: | ||
assert len(response) == 3 | ||
major, minor, patch = unpack("BBB", response) | ||
return (major, minor, patch) | ||
|
||
# Unpack from response: | ||
# response = format_id (1) | ||
# app_name_raw_len (1) | ||
# app_name_raw (var) | ||
# version_raw_len (1) | ||
# version_raw (var) | ||
# unused_len (1) | ||
# unused (var) | ||
def unpack_get_app_and_version_response(response: bytes) -> Tuple[str, str]: | ||
response, _ = pop_sized_buf_from_buffer(response, 1) | ||
response, _, app_name_raw = pop_size_prefixed_buf_from_buf(response) | ||
response, _, version_raw = pop_size_prefixed_buf_from_buf(response) | ||
response, _, _ = pop_size_prefixed_buf_from_buf(response) | ||
|
||
assert len(response) == 0 | ||
|
||
return app_name_raw.decode("ascii"), version_raw.decode("ascii") | ||
|
||
# Unpack from response: | ||
# response = pub_key_len (1) | ||
# pub_key (var) | ||
def unpack_get_public_key_response(response: bytes) -> Tuple[int, bytes]: | ||
response, pub_key_len, pub_key = pop_size_prefixed_buf_from_buf(response) | ||
|
||
assert pub_key_len == 32 | ||
assert len(response) == 0 | ||
|
||
return pub_key_len, pub_key | ||
|
||
# Unpack from response: | ||
# response = address_len (1) | ||
# address (var) | ||
def unpack_get_address_response(response: bytes) -> Tuple[int, bytes]: | ||
response, address_len, address = pop_size_prefixed_buf_from_buf(response) | ||
|
||
assert address_len == 32 | ||
assert len(response) == 0 | ||
|
||
return address_len, address | ||
|
||
# Unpack from response: | ||
# response = der_sig_len (1) | ||
# der_sig (var) | ||
# v (1) | ||
# TODO: check if this needs to be edited | ||
def unpack_sign_tx_response(response: bytes) -> Tuple[int, bytes, int]: | ||
response, der_sig_len, der_sig = pop_size_prefixed_buf_from_buf(response) | ||
response, v = pop_sized_buf_from_buffer(response, 1) | ||
|
||
assert len(response) == 0 | ||
|
||
return der_sig_len, der_sig, int.from_bytes(v, byteorder='big') |
Oops, something went wrong.