Skip to content

Commit 1d8d0ee

Browse files
committed
feat: Add hedera-base support
1 parent aa77d2a commit 1d8d0ee

File tree

11 files changed

+759
-0
lines changed

11 files changed

+759
-0
lines changed

apps/hedera_app/hedera_api.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* @file hedera_api.c
3+
* @author Cypherock X1 Team
4+
* @brief Defines helper APIs for the Hedera app.
5+
* @copyright Copyright (c) 2025 HODL TECH PTE LTD
6+
*/
7+
#include "hedera_api.h"
8+
#include <pb_decode.h>
9+
#include <pb_encode.h>
10+
#include "common_error.h"
11+
#include "core_api.h"
12+
#include "events.h"
13+
14+
bool decode_hedera_query(const uint8_t *data, uint16_t data_size, hedera_query_t *query_out) {
15+
if (NULL == data || NULL == query_out || 0 == data_size) {
16+
hedera_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, ERROR_DATA_FLOW_DECODING_FAILED);
17+
return false;
18+
}
19+
memzero(query_out, sizeof(hedera_query_t));
20+
pb_istream_t stream = pb_istream_from_buffer(data, data_size);
21+
bool status = pb_decode(&stream, HEDERA_QUERY_FIELDS, query_out);
22+
if (!status) {
23+
hedera_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, ERROR_DATA_FLOW_DECODING_FAILED);
24+
}
25+
return status;
26+
}
27+
28+
bool encode_hedera_result(const hedera_result_t *result, uint8_t *buffer, uint16_t max_buffer_len, size_t *bytes_written_out) {
29+
if (NULL == result || NULL == buffer || NULL == bytes_written_out) return false;
30+
pb_ostream_t stream = pb_ostream_from_buffer(buffer, max_buffer_len);
31+
bool status = pb_encode(&stream, HEDERA_RESULT_FIELDS, result);
32+
if (status) {
33+
*bytes_written_out = stream.bytes_written;
34+
}
35+
return status;
36+
}
37+
38+
bool check_hedera_query(const hedera_query_t *query, pb_size_t exp_query_tag) {
39+
if ((NULL == query) || (exp_query_tag != query->which_request)) {
40+
hedera_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, ERROR_DATA_FLOW_INVALID_QUERY);
41+
return false;
42+
}
43+
return true;
44+
}
45+
46+
hedera_result_t init_hedera_result(pb_size_t result_tag) {
47+
hedera_result_t result = HEDERA_RESULT_INIT_ZERO;
48+
result.which_response = result_tag;
49+
return result;
50+
}
51+
52+
void hedera_send_error(pb_size_t which_error, uint32_t error_code) {
53+
hedera_result_t result = init_hedera_result(HEDERA_RESULT_COMMON_ERROR_TAG);
54+
result.common_error = init_common_error(which_error, error_code);
55+
hedera_send_result(&result);
56+
}
57+
58+
void hedera_send_result(const hedera_result_t *result) {
59+
uint8_t buffer[1700] = {0};
60+
size_t bytes_encoded = 0;
61+
ASSERT(encode_hedera_result(result, buffer, sizeof(buffer), &bytes_encoded));
62+
send_response_to_host(buffer, bytes_encoded);
63+
}
64+
65+
bool hedera_get_query(hedera_query_t *query, pb_size_t exp_query_tag) {
66+
evt_status_t event = get_events(EVENT_CONFIG_USB, MAX_INACTIVITY_TIMEOUT);
67+
if (event.p0_event.flag) {
68+
return false;
69+
}
70+
if (!decode_hedera_query(event.usb_event.p_msg, event.usb_event.msg_size, query)) {
71+
return false;
72+
}
73+
if (!check_hedera_query(query, exp_query_tag)) {
74+
return false;
75+
}
76+
return true;
77+
}

apps/hedera_app/hedera_api.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @file hedera_api.h
3+
* @author Cypherock X1 Team
4+
* @brief Header file for Hedera app helper functions.
5+
* @copyright Copyright (c) 2025 HODL TECH PTE LTD
6+
*/
7+
#ifndef HEDERA_API_H
8+
#define HEDERA_API_H
9+
10+
#include <hedera/core.pb.h>
11+
#include <stdint.h>
12+
13+
// API to decode query from host with HEDERA_QUERY_FIELDS
14+
bool decode_hedera_query(const uint8_t *data, uint16_t data_size, hedera_query_t *query_out);
15+
16+
// Encodes the Hedera result with HEDERA_RESULT_FIELDS to byte-stream
17+
bool encode_hedera_result(const hedera_result_t *result, uint8_t *buffer, uint16_t max_buffer_len, size_t *bytes_written_out);
18+
19+
// Checks if the `which_request` field of the query matches the expected tag.
20+
bool check_hedera_query(const hedera_query_t *query, pb_size_t exp_query_tag);
21+
22+
// Returns a zero-initialized hedera_result_t with the specified result tag.
23+
hedera_result_t init_hedera_result(pb_size_t result_tag);
24+
25+
// Sends an error response to the host.
26+
void hedera_send_error(pb_size_t which_error, uint32_t error_code);
27+
28+
// Encodes and sends a result to the host.
29+
void hedera_send_result(const hedera_result_t *result);
30+
31+
// Waits for and decodes a query from the host, ensuring it matches the expected tag.
32+
bool hedera_get_query(hedera_query_t *query, pb_size_t exp_query_tag);
33+
34+
#endif

apps/hedera_app/hedera_context.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* @file hedera_context.h
3+
* @author Cypherock X1 Team
4+
* @brief Header file defining typedefs and MACROS for the Hedera app.
5+
* @copyright Copyright (c) 2025 HODL TECH PTE LTD
6+
*/
7+
#ifndef HEDERA_CONTEXT_H
8+
#define HEDERA_CONTEXT_H
9+
10+
#include <stdbool.h>
11+
#include <stdint.h>
12+
13+
#define HEDERA_NAME "Hedera"
14+
#define HEDERA_LUNIT "HBAR"
15+
16+
#define HEDERA_COIN_DEPTH 5
17+
18+
// Derivation path: m/44'/3030'/0'/0'/i'
19+
#define HEDERA_PURPOSE_INDEX (0x80000000 | 44)
20+
#define HEDERA_COIN_INDEX (0x80000000 | 3030)
21+
#define HEDERA_ACCOUNT_INDEX (0x80000000 | 0)
22+
#define HEDERA_CHANGE_INDEX (0x00000000 | 0)
23+
24+
#define HEDERA_PUB_KEY_SIZE 32 // Raw Ed25519 public key
25+
#define HEDERA_ADDRESS_STRING_SIZE (HEDERA_PUB_KEY_SIZE * 2 + 1) // Hex string + null
26+
#define HEDERA_SIGNATURE_SIZE 64 // Raw Ed25519 signature
27+
28+
#define MAX_TXN_SIZE 512 // Maximum size of a serialized transaction body
29+
30+
#endif /* HEDERA_CONTEXT_H */

apps/hedera_app/hedera_helpers.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* @file hedera_helpers.c
3+
* @author Cypherock X1 Team
4+
* @brief Utilities specific to the Hedera app.
5+
* @copyright Copyright (c) 2025 HODL TECH PTE LTD
6+
*/
7+
#include "hedera_helpers.h"
8+
#include "coin_utils.h"
9+
#include "hedera_context.h"
10+
#include <stdio.h>
11+
#include <string.h>
12+
13+
bool hedera_derivation_path_guard(const uint32_t *path, uint8_t levels) {
14+
if (levels != HEDERA_COIN_DEPTH) {
15+
return false;
16+
}
17+
18+
// Path must be m/44'/3030'/0'/0'/i'
19+
return (path[0] == HEDERA_PURPOSE_INDEX &&
20+
path[1] == HEDERA_COIN_INDEX &&
21+
path[2] == HEDERA_ACCOUNT_INDEX &&
22+
path[3] == HEDERA_CHANGE_INDEX &&
23+
is_non_hardened(path[4]));
24+
}
25+
26+
void hedera_format_pubkey(const uint8_t *pubkey, char *out_str) {
27+
byte_array_to_hex_string(pubkey, HEDERA_PUB_KEY_SIZE, out_str, HEDERA_ADDRESS_STRING_SIZE);
28+
}
29+
30+
void hedera_format_account_id(const Hedera_AccountID *account_id, char *out_str) {
31+
snprintf(out_str, 40, "%lld.%lld.%lld",
32+
(long long)account_id->shardNum,
33+
(long long)account_id->realmNum,
34+
(long long)account_id->account.accountNum);
35+
}
36+
37+
void hedera_format_tinybars_to_hbar_string(int64_t tinybars, char *out_str) {
38+
char temp_str[30];
39+
const int64_t hbar_div = 100000000;
40+
41+
int sign = (tinybars < 0) ? -1 : 1;
42+
if (tinybars < 0) tinybars = -tinybars;
43+
44+
int64_t whole_part = tinybars / hbar_div;
45+
int64_t frac_part = tinybars % hbar_div;
46+
47+
// Format fractional part with leading zeros
48+
snprintf(temp_str, sizeof(temp_str), "%lld.%08lld", (long long)whole_part, (long long)frac_part);
49+
50+
// Trim trailing zeros
51+
char *end = temp_str + strlen(temp_str) - 1;
52+
while (end > temp_str && *end == '0') {
53+
*end-- = '\0';
54+
}
55+
if (end > temp_str && *end == '.') {
56+
*end = '\0';
57+
}
58+
59+
snprintf(out_str, 40, "%s%s %s", (sign == -1) ? "-" : "", temp_str, HEDERA_LUNIT);
60+
}

apps/hedera_app/hedera_helpers.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @file hedera_helpers.h
3+
* @author Cypherock X1 Team
4+
* @brief Utilities API definitions for the Hedera app.
5+
* @copyright Copyright (c) 2025 HODL TECH PTE LTD
6+
*/
7+
#ifndef HEDERA_HELPERS_H
8+
#define HEDERA_HELPERS_H
9+
10+
#include <stdbool.h>
11+
#include <stddef.h>
12+
#include <stdint.h>
13+
#include "proto/basic_types.pb.h"
14+
15+
// Verifies the derivation path for Hedera (m/44'/3030'/0'/0'/i').
16+
bool hedera_derivation_path_guard(const uint32_t *path, uint8_t levels);
17+
18+
// Formats a raw public key into a hex string for display.
19+
void hedera_format_pubkey(const uint8_t *pubkey, char *out_str);
20+
21+
// Formats an AccountID protobuf struct into a human-readable string "shard.realm.num".
22+
void hedera_format_account_id(const Hedera_AccountID *account_id, char *out_str);
23+
24+
// Formats a tinybar amount into an HBAR string with 8 decimal places.
25+
void hedera_format_tinybars_to_hbar_string(int64_t tinybars, char *out_str);
26+
27+
#endif // HEDERA_HELPERS_H

apps/hedera_app/hedera_main.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* @file hedera_main.c
3+
* @author Cypherock X1 Team
4+
* @brief A common entry point to various Hedera coin actions.
5+
* @copyright Copyright (c) 2025 HODL TECH PTE LTD
6+
*/
7+
#include "hedera_main.h"
8+
#include "hedera_api.h"
9+
#include "hedera_priv.h"
10+
#include "status_api.h"
11+
12+
void hedera_main(usb_event_t usb_evt, const void *hedera_app_config);
13+
14+
static const cy_app_desc_t hedera_app_desc = {
15+
.id = 24, // IMPORTANT: Use an unused app ID here.
16+
.version = {.major = 1, .minor = 0, .patch = 0},
17+
.app = hedera_main,
18+
.app_config = NULL
19+
};
20+
21+
void hedera_main(usb_event_t usb_evt, const void *hedera_app_config) {
22+
hedera_query_t query = HEDERA_QUERY_INIT_DEFAULT;
23+
24+
if (!decode_hedera_query(usb_evt.p_msg, usb_evt.msg_size, &query)) {
25+
return;
26+
}
27+
28+
core_status_set_idle_state(CORE_DEVICE_IDLE_STATE_USB);
29+
30+
switch ((uint8_t)query.which_request) {
31+
case HEDERA_QUERY_GET_PUBLIC_KEYS_TAG:
32+
case HEDERA_QUERY_GET_USER_VERIFIED_PUBLIC_KEY_TAG: {
33+
hedera_get_pub_keys(&query);
34+
break;
35+
}
36+
case HEDERA_QUERY_SIGN_TXN_TAG: {
37+
hedera_sign_transaction(&query);
38+
break;
39+
}
40+
default: {
41+
hedera_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, ERROR_DATA_FLOW_INVALID_QUERY);
42+
break;
43+
}
44+
}
45+
}
46+
47+
const cy_app_desc_t *get_hedera_app_desc() {
48+
return &hedera_app_desc;
49+
}

apps/hedera_app/hedera_main.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @file hedera_main.h
3+
* @author Cypherock X1 Team
4+
* @brief Header for the main entry point of the Hedera app.
5+
* @copyright Copyright (c) 2025 HODL TECH PTE LTD
6+
*/
7+
#ifndef HEDERA_MAIN_H
8+
#define HEDERA_MAIN_H
9+
10+
#include "app_registry.h"
11+
#include "events.h"
12+
13+
// Returns the config for the Hedera app descriptor.
14+
const cy_app_desc_t *get_hedera_app_desc();
15+
16+
#endif /* HEDERA_MAIN_H */

apps/hedera_app/hedera_priv.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* @file hedera_priv.h
3+
* @author Cypherock X1 Team
4+
* @brief Support for Hedera app internal operations.
5+
* @copyright Copyright (c) 2025 HODL TECH PTE LTD
6+
*/
7+
#ifndef HEDERA_PRIV_H
8+
#define HEDERA_PRIV_H
9+
10+
#include "hedera/core.pb.h"
11+
#include "hedera/sign_txn.pb.h"
12+
#include "proto/transaction_body.pb.h" // Nanopb generated header
13+
#include "hedera_context.h"
14+
15+
// Context for the transaction signing flow
16+
typedef struct {
17+
// The structure holds the wallet information of the transaction.
18+
hedera_sign_txn_initiate_request_t init_info;
19+
20+
// Decoded protobuf transaction body for UI display
21+
Hedera_TransactionBody txn;
22+
23+
// Raw serialized transaction bytes received from host. This is what we sign.
24+
uint8_t raw_txn_bytes[MAX_TXN_SIZE];
25+
size_t raw_txn_len;
26+
27+
} hedera_txn_context_t;
28+
29+
/* --- FUNCTION PROTOTYPES --- */
30+
31+
// Handler for public key derivation flows
32+
void hedera_get_pub_keys(hedera_query_t *query);
33+
34+
// Handler for transaction signing flows
35+
void hedera_sign_transaction(hedera_query_t *query);
36+
37+
#endif /* HEDERA_PRIV_H */

0 commit comments

Comments
 (0)