Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
07344b1
Add hedera swap transaction logic tests
mnowakbd Apr 9, 2025
5ce16d0
Add hedera swap transaction screenshots
mnowakbd Apr 14, 2025
163f9ca
Add hedera swap transaction screenshots
mnowakbd Apr 18, 2025
65ade15
Fixed tests
AntekWozniakBlocky Apr 29, 2025
cc8d5c7
Merge remote-tracking branch 'upstream/develop' into add_swap_transac…
AntekWozniakBlocky Apr 29, 2025
de73a03
Changed index in tests
AntekWozniakBlocky May 21, 2025
98e1215
Added hedera to conftest
AntekWozniakBlocky May 21, 2025
703d806
Regenerated golden runs
AntekWozniakBlocky May 22, 2025
4039d78
Added sign verification
AntekWozniakBlocky May 23, 2025
08de8b1
Add hedera to test pipeline
AntekWozniakBlocky May 26, 2025
4479a76
Changed hedera test repo
AntekWozniakBlocky Jun 23, 2025
5afd935
feat: add Hedera swap transaction logic tests
mnowakbd Apr 9, 2025
4686b2f
feat: add hedera swap transaction screenshots
mnowakbd Apr 14, 2025
4496cb1
Fixed tests
AntekWozniakBlocky Apr 29, 2025
29396be
Changed index in tests
AntekWozniakBlocky May 21, 2025
0ec1066
feat: add hedera to test configuration
AntekWozniakBlocky May 21, 2025
d311a94
Added sign verification
AntekWozniakBlocky May 23, 2025
e5e0667
Regenerated golden runs
AntekWozniakBlocky May 22, 2025
e36ca7a
Add hedera to test pipeline
AntekWozniakBlocky May 26, 2025
5f29444
build: change hedera test repo
AntekWozniakBlocky Jun 23, 2025
4462267
Merge branch 'feature/swap' of github.com:blockydevs/app-exchange int…
AntekWozniakBlocky Jun 23, 2025
9a87e8a
git: merge develop to feature branch
AntekWozniakBlocky Jun 23, 2025
7b10abb
fix: add missing sol_conf snippet markers for documentation build
AntekWozniakBlocky Jun 23, 2025
2af98f4
fix: add missing native_test snippet markers in test_ton.py
AntekWozniakBlocky Jun 23, 2025
abe9e3c
refactor: prepare for review and PR
AntekWozniakBlocky Jun 24, 2025
0c2bbc3
chore: align cal.py with develop branch
AntekWozniakBlocky Jun 24, 2025
97fe534
chore: version bump
AntekWozniakBlocky Jun 30, 2025
2da3cde
git: pull merge artifacts from develop branch
AntekWozniakBlocky Jun 30, 2025
0e78372
chore: bump version on golden runs
AntekWozniakBlocky Jun 30, 2025
0da4e4f
ci: run CI on local fork
AntekWozniakBlocky Jul 1, 2025
ce34f36
fix: wrong exchange repo
AntekWozniakBlocky Jul 1, 2025
04838ad
fix: wrong exchange repo
AntekWozniakBlocky Jul 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 16 additions & 2 deletions .github/workflows/reusable_swap_functional_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ on:
inputs:
branch_for_exchange:
required: false
default: 'develop'
default: 'feature/swap-implementation'
type: string
repo_for_exchange:
required: false
default: 'LedgerHQ/app-exchange'
#Only for testing purposes, to test integration with our hedera implementation
default: 'blockydevs/app-exchange'
type: string

branch_for_stellar:
Expand Down Expand Up @@ -164,6 +165,16 @@ on:
default: 'LedgerHQ/app-boilerplate'
type: string

branch_for_hedera:
required: false
#Only for testing purposes, to test integration with our hedera implementation
default: 'feature/swap-implementation'
type: string
repo_for_hedera:
required: false
default: 'blockydevs/app-hedera'
type: string

test_filter:
required: false
default: '""'
Expand Down Expand Up @@ -238,6 +249,9 @@ jobs:
- name: APTOS
repo: ${{ inputs.repo_for_aptos }}
branch: ${{ inputs.branch_for_aptos }}
- name: hedera
repo: ${{ inputs.repo_for_hedera }}
branch: ${{ inputs.branch_for_hedera }}
- name: boilerplate
repo: ${{ inputs.repo_for_bol }}
branch: ${{ inputs.branch_for_bol }}
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ APPNAME = "Exchange"
# Application version
APPVERSION_M = 4
APPVERSION_N = 2
APPVERSION_P = 4
APPVERSION_P = 5
APPVERSION = "$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)"

# Application source files
Expand Down
4 changes: 4 additions & 0 deletions test/python/apps/cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
from .near import NEAR_PACKED_DERIVATION_PATH, NEAR_CONF
from .aptos import APTOS_PACKED_DERIVATION_PATH, APTOS_CONF
from .boilerplate import BOL_PACKED_DERIVATION_PATH, BOL_CONF
from .hedera import HEDERA_PACKED_DERIVATION_PATH, HEDERA_CONF



# Define a configuration for each currency used in our tests: native coins and tokens

Expand Down Expand Up @@ -65,3 +68,4 @@
SUI_USDC_CURRENCY_CONFIGURATION = CurrencyConfiguration(ticker="USDC", conf=SUI_USDC_CONF, packed_derivation_path=SUI_PACKED_DERIVATION_PATH)
APTOS_CURRENCY_CONFIGURATION = CurrencyConfiguration(ticker="APT", conf=APTOS_CONF, packed_derivation_path=APTOS_PACKED_DERIVATION_PATH)
BOL_CURRENCY_CONFIGURATION = CurrencyConfiguration(ticker="BOL", conf=BOL_CONF, packed_derivation_path=BOL_PACKED_DERIVATION_PATH)
HEDERA_CURRENCY_CONFIGURATION = CurrencyConfiguration(ticker="HBAR", conf=HEDERA_CONF, packed_derivation_path=HEDERA_PACKED_DERIVATION_PATH)
164 changes: 164 additions & 0 deletions test/python/apps/hedera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
from typing import List, Generator, Dict
from enum import IntEnum
from contextlib import contextmanager
from time import sleep
from nacl.signing import VerifyKey

from ragger.backend.interface import BackendInterface, RAPDU
from ragger.bip import pack_derivation_path
from ragger.utils import create_currency_config

from .hedera_builder import hedera_transaction


class INS(IntEnum):
INS_GET_APP_CONFIGURATION = 0x01
INS_GET_PUBLIC_KEY = 0x02
INS_SIGN_TRANSACTION = 0x04

CLA = 0xE0

P1_CONFIRM = 0x00
P1_NON_CONFIRM = 0x01

P2_EXTEND = 0x01
P2_MORE = 0x02


PUBLIC_KEY_LENGTH = 32

MAX_CHUNK_SIZE = 255


STATUS_OK = 0x9000

HEDERA_CONF = create_currency_config("HBAR", "Hedera")

HEDERA_PACKED_DERIVATION_PATH = pack_derivation_path("m/44'/3030'/12345'")
HEDERA_PACKED_DERIVATION_PATH_2 = pack_derivation_path("m/44'/3030'/0'/0'")

# Public key for verification
HEDERA_PUBLIC_KEY = "698f0bad5c0c043a5f09cdcbb4c48ddcf6fb2886fa006df26298003fd59dc7c9"

class ErrorType:
EXCEPTION_USER_REJECTED = 0x6985


def to_zigzag(n):
return n + n + (n < 0)


class HederaClient:
client: BackendInterface

def __init__(self, client: BackendInterface):
self._client = client

def get_public_key_non_confirm(self, index: int) -> RAPDU:
index_b = index.to_bytes(4, "little")
return self._client.exchange(CLA, INS.INS_GET_PUBLIC_KEY, P1_NON_CONFIRM, 0, index_b)

@contextmanager
def get_public_key_confirm(self, index: int) -> Generator[None, None, None]:
index_b = index.to_bytes(4, "little")
with self._client.exchange_async(CLA, INS.INS_GET_PUBLIC_KEY, P1_CONFIRM, 0, index_b):
sleep(0.5)
yield

def get_async_response(self) -> RAPDU:
return self._client.last_async_response

def get_public_key_raw(self, index: int) -> bytes:
"""
Get the raw Ed25519 public key for an index.

:param index: The BIP32 path index
:return: The public key
"""
response = self.get_public_key_non_confirm(index)
if len(response.data) >= PUBLIC_KEY_LENGTH:
return response.data[:PUBLIC_KEY_LENGTH]
raise ValueError(f"Invalid public key response length")

def verify_signature(self, public_key: bytes, transaction: bytes, signature: bytes) -> bool:
"""
Verify the signature of a transaction.

:param public_key: The public key bytes to verify against
:param transaction: The transaction that was signed (including 4-byte index prefix)
:param signature: The signature to verify
:return: True if the signature is valid, False otherwise
"""
try:
verify_key = VerifyKey(public_key)
# The device receives index+transaction but only signs the transaction part
transaction_without_index = transaction[4:] if len(transaction) > 4 else transaction
verify_key.verify(transaction_without_index, signature)
print("Signature verification successful!")
return True
except Exception:
return False

def sign_transaction(self,
index: int,
operator_shard_num: int,
operator_realm_num: int,
operator_account_num: int,
transaction_fee: int,
memo: str,
conf: Dict) -> bytes:
"""
Sign a transaction with the key at the specified index.

:param index: The index to use when signing the transaction.
:param operator_shard_num: The shard number of the operator.
:param operator_realm_num: The realm number of the operator.
:param operator_account_num: The account number of the operator.
:param transaction_fee: The transaction fee.
:param memo: The transaction memo.
:param conf: The transaction configuration.
:return: The signature.
"""
# Build the transaction
transaction = hedera_transaction(
operator_shard_num=operator_shard_num,
operator_realm_num=operator_realm_num,
operator_account_num=operator_account_num,
transaction_fee=transaction_fee,
memo=memo,
conf=conf
)

# Prepare the payload with index
payload = index.to_bytes(4, "little") + transaction

# Send to device for signing
response = self._client.exchange(CLA, INS.INS_SIGN_TRANSACTION, P1_NON_CONFIRM, 0, payload)

if not response.data or len(response.data) == 0:
return b''

return response.data

@contextmanager
def send_sign_transaction(self,
index: int,
operator_shard_num: int,
operator_realm_num: int,
operator_account_num: int,
transaction_fee: int,
memo: str,
conf: Dict) -> Generator[None, None, None]:

transaction = hedera_transaction(operator_shard_num,
operator_realm_num,
operator_account_num,
transaction_fee,
memo,
conf)

payload = index.to_bytes(4, "little") + transaction

with self._client.exchange_async(CLA, INS.INS_SIGN_TRANSACTION, P1_CONFIRM, 0, payload):
sleep(0.5)
yield
Loading
Loading