Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI improvements #41

Merged
merged 5 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ file(GLOB_RECURSE LIB_SRC
${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl.c
${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_impl_common.c
${CMAKE_CURRENT_SOURCE_DIR}/app/src/crypto_helper.c
${CMAKE_CURRENT_SOURCE_DIR}/app/src/parser_print_common.c

# ###
${CMAKE_CURRENT_SOURCE_DIR}/deps/blake2/ref/blake2b-ref.c
Expand Down
2 changes: 1 addition & 1 deletion app/Makefile.version
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This is the major version
APPVERSION_M=1
# This is the minor version
APPVERSION_N=0
APPVERSION_N=1
# This is the patch version
APPVERSION_P=0
7 changes: 7 additions & 0 deletions app/src/apdu_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ __Z_INLINE void handleSign(volatile uint32_t *flags, volatile uint32_t *tx, uint
THROW(APDU_CODE_OK);
}

// Get address for the received path
zxerr_t zxerr = crypto_get_change_address();
if (zxerr != zxerr_ok) {
*tx = 0;
THROW(APDU_CODE_DATA_INVALID);
}

const char *error_msg = tx_parse();
CHECK_APP_CANARY()
if (error_msg != NULL) {
Expand Down
10 changes: 10 additions & 0 deletions app/src/common/actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,13 @@ __Z_INLINE void app_reply_error() {
set_code(G_io_apdu_buffer, 0, APDU_CODE_DATA_INVALID);
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2);
}

__Z_INLINE zxerr_t app_get_address() {
zxerr_t err = crypto_get_change_address();

if (err != zxerr_ok) {
THROW(APDU_CODE_EXECUTION_ERROR);
}

return zxerr_ok;
}
3 changes: 3 additions & 0 deletions app/src/common/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ parser_error_t parser_getNumItems(const parser_context_t *ctx, uint8_t *num_item
parser_error_t parser_getItem(const parser_context_t *ctx, uint8_t displayIdx, char *outKey, uint16_t outKeyLen,
char *outVal, uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount);

// checks outputs asset_id
parser_error_t parser_check_outputs(parser_tx_t *tx_obj);

#ifdef __cplusplus
}
#endif
1 change: 1 addition & 0 deletions app/src/common/parser_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ typedef enum {
parser_unexpected_chain,
parser_missing_field,
paser_unknown_transaction,
parser_require_expert_mode,
} parser_error_t;

typedef struct {
Expand Down
5 changes: 5 additions & 0 deletions app/src/common/tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ const char *tx_parse() {
return parser_getErrorDescription(err);
}

err = parser_check_outputs(&tx_obj);
if (err != parser_ok) {
return parser_getErrorDescription(err);
}

err = parser_validate(&ctx_parsed_tx);
CHECK_APP_CANARY()

Expand Down
7 changes: 7 additions & 0 deletions app/src/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "zxmacros.h"

uint32_t hdPath[HDPATH_LEN_DEFAULT];
uint8_t change_address[32];

static zxerr_t computeKeys(keys_t *saplingKeys) {
if (saplingKeys == NULL) {
Expand Down Expand Up @@ -179,3 +180,9 @@ zxerr_t crypto_fillKeys(uint8_t *buffer, uint16_t bufferLen, key_kind_e requeste

return zxerr_ok;
}

zxerr_t crypto_get_change_address(void) {
MEMZERO(change_address, sizeof(change_address));
CHECK_ZXERR(crypto_generateSaplingKeys(change_address, sizeof(change_address), PublicAddress));
return zxerr_ok;
}
3 changes: 2 additions & 1 deletion app/src/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ extern "C" {
#include "zxerror.h"

extern uint32_t hdPath[HDPATH_LEN_DEFAULT];
extern uint8_t change_address[32];

zxerr_t crypto_fillKeys(uint8_t *buffer, uint16_t bufferLen, key_kind_e requestedKey, uint16_t *cmdResponseLen);
zxerr_t crypto_sign(const uint8_t publickeyRandomness[32], const uint8_t txnHash[32], uint8_t *output, uint16_t outputLen);

zxerr_t crypto_get_change_address(void);
#if defined(LEDGER_SPECIFIC)
zxerr_t crypto_generateSaplingKeys(uint8_t *output, uint16_t outputLen, key_kind_e requestedKey);
#endif
Expand Down
207 changes: 164 additions & 43 deletions app/src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,85 @@
#include <zxmacros.h>
#include <zxtypes.h>

#include "app_mode.h"
#include "coin.h"
#include "crypto.h"
#include "crypto_helper.h"
#include "parser_common.h"
#include "parser_impl.h"
#include "parser_print_common.h"
#include "rslib.h"

// lookup table for future use
static const asset_id_lookpup_t asset_id_lookups[] = {
{{0x51, 0xf3, 0x3a, 0x2f, 0x14, 0xf9, 0x27, 0x35, 0xe5, 0x62, 0xdc, 0x65, 0x8a, 0x56, 0x39, 0x27,
0x9d, 0xdc, 0xa3, 0xd5, 0x07, 0x9a, 0x6d, 0x12, 0x42, 0xb2, 0xa5, 0x88, 0xa9, 0xcb, 0xf4, 0x4c},
8,
" IRON"},
};

bool parser_verify_asset_id(uint8_t *asset_id, uint8_t *index) {
for (size_t i = 0; i < sizeof(asset_id_lookups) / sizeof(asset_id_lookups[0]); i++) {
if (MEMCMP(asset_id, PIC(asset_id_lookups[i].identifier), 32) == 0) {
*index = i;
return true;
}
}
return false;
}

parser_error_t parser_check_outputs(parser_tx_t *tx_obj) {
for (size_t i = 0; i < tx_obj->outputs.elements; i++) {
// Decrypt the output
const uint8_t *output = tx_obj->outputs.data.ptr + (i * (192 + 328));
CHECK_ERROR(crypto_decrypt_merkle_note(tx_obj, output + 192, tx_obj->ovk));

bool is_renderable = true;
// If in expert mode show every output
if (!app_mode_expert()) {
// Verify the output owner
#if defined(LEDGER_SPECIFIC)
is_renderable = MEMCMP(tx_obj->outputs.decrypted_note.owner, change_address, KEY_LENGTH) != 0;

#else
is_renderable = true;
uint8_t test_change_address[32] = {0x67, 0x3a, 0x8b, 0xfd, 0x38, 0xf9, 0x77, 0xea, 0x1e, 0x51, 0x1a,
0x40, 0x65, 0x6d, 0x2a, 0x7a, 0x83, 0x22, 0x52, 0xbc, 0x40, 0xc1,
0x4c, 0x27, 0x60, 0xad, 0x90, 0x64, 0x7d, 0x55, 0xb2, 0xef};
is_renderable = MEMCMP(tx_obj->outputs.decrypted_note.owner, test_change_address, KEY_LENGTH) != 0;
#endif
}

if (!is_renderable) {
tx_obj->output_render_mask &= ~(1ULL << i); // Set the bit to 0 if equal
} else {
tx_obj->output_render_mask |= (1ULL << i); // Set the bit to 1 if not equal
tx_obj->n_rendered_outputs++;

// If its renderable then we need to check if the asset ID is known
bool asset_found = false; // Track if asset ID is found
for (size_t j = 0; j < sizeof(asset_id_lookups) / sizeof(asset_id_lookups[0]); j++) {
if (MEMCMP(tx_obj->outputs.decrypted_note.asset_id, PIC(asset_id_lookups[j].identifier), 32) == 0) {
asset_found = true; // Asset ID found
break;
}
}

// Handle case when asset ID is not found
if (!asset_found) {
tx_obj->n_raw_asset_id++; // Increment if asset ID is not found
#if defined(LEDGER_SPECIFIC)
// Check for expert mode if asset ID is not found
if (!app_mode_expert()) {
return parser_require_expert_mode;
}
#endif
}
}
}
return parser_ok; // Return parser_ok after processing all outputs
}

parser_error_t parser_init_context(parser_context_t *ctx, const uint8_t *buffer, uint16_t bufferSize) {
ctx->offset = 0;
ctx->buffer = NULL;
Expand Down Expand Up @@ -67,8 +139,10 @@ parser_error_t parser_validate(parser_context_t *ctx) {
parser_error_t parser_getNumItems(const parser_context_t *ctx, uint8_t *num_items) {
UNUSED(ctx);

// Txversion + (ownner + amount + asset id) * n_output + fee + expiration
*num_items = 1 + ctx->tx_obj->outputs.elements * 3 + 2;
// Txversion + From + (owner + amount ) * output_with_valid_asset_id + (owner + amount + asset id) *
// output_with_raw_asset_id + fee + expiration
*num_items =
2 + ((ctx->tx_obj->n_rendered_outputs - ctx->tx_obj->n_raw_asset_id) * 2) + (ctx->tx_obj->n_raw_asset_id * 3) + 2;

if (*num_items == 0) {
return parser_unexpected_number_items;
Expand All @@ -90,8 +164,10 @@ static parser_error_t checkSanity(uint8_t numItems, uint8_t displayIdx) {
return parser_ok;
}

uint8_t out_idx = 0;
uint8_t prev_decrypted_out_idx = 0;
uint8_t out_idx = 0; // Current output index
uint8_t prev_decrypted_out_idx = 0; // Previous decrypted output index
uint8_t cumulative_display_count = 0; // Track cumulative display items processed

parser_error_t parser_getItem(const parser_context_t *ctx, uint8_t displayIdx, char *outKey, uint16_t outKeyLen,
char *outVal, uint16_t outValLen, uint8_t pageIdx, uint8_t *pageCount) {
UNUSED(pageIdx);
Expand All @@ -103,64 +179,107 @@ parser_error_t parser_getItem(const parser_context_t *ctx, uint8_t displayIdx, c
CHECK_ERROR(checkSanity(numItems, displayIdx));
cleanOutput(outKey, outKeyLen, outVal, outValLen);

uint64_t total_out_elements = ctx->tx_obj->outputs.elements * ELEMENTS_PER_OUTPUT;
uint8_t tmp_idx = displayIdx;
uint8_t asset_id_idx = 0;
bool known_asset_id = false;
char buf[70] = {0};

if (displayIdx == 0) {
snprintf(outKey, outKeyLen, "Tx Version");
snprintf(outVal, outValLen, "V%d", (uint8_t)ctx->tx_obj->transactionVersion);
return parser_ok;
}

if (tmp_idx > 0 && tmp_idx <= total_out_elements) {
tmp_idx = (displayIdx % ELEMENTS_PER_OUTPUT);
out_idx = (displayIdx / ELEMENTS_PER_OUTPUT);
if (displayIdx == 1) {
snprintf(outKey, outKeyLen, "From");
#if defined(LEDGER_SPECIFIC)
array_to_hexstr(buf, sizeof(buf), change_address, 32);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
#else
uint8_t test_change_address[32] = {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa};
array_to_hexstr(buf, sizeof(buf), test_change_address, 32);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
#endif
return parser_ok;
}

tmp_idx -= 2; // Adjust for the transaction version
cumulative_display_count = 0; // Reset cumulative display count for fresh calculation

if (tmp_idx == 1 || tmp_idx == 2) {
out_idx++;
for (out_idx = 1; out_idx <= ctx->tx_obj->outputs.elements; out_idx++) {
// Check if the output index is renderable, if not
if (!(ctx->tx_obj->output_render_mask & (1ULL << (out_idx - 1)))) {
continue;
}

// Decrypt output if needed
if (prev_decrypted_out_idx != out_idx) {
const uint8_t *output = ctx->tx_obj->outputs.data.ptr + ((out_idx - 1) * (192 + 328));
CHECK_ERROR(crypto_decrypt_merkle_note(ctx->tx_obj, output + 192, ctx->tx_obj->ovk));
prev_decrypted_out_idx = out_idx;
prev_decrypted_out_idx = out_idx; // Update previous decrypted index
}

// Verify the asset ID
known_asset_id = parser_verify_asset_id(ctx->tx_obj->outputs.decrypted_note.asset_id, &asset_id_idx);
uint8_t elements_in_output = known_asset_id ? 2 : 3;

// Check if displayIdx falls within this output's range
if (tmp_idx < cumulative_display_count + elements_in_output) {
uint8_t local_idx = tmp_idx - cumulative_display_count;

// Generate output based on the local index
switch (local_idx) {
case 0:
snprintf(outKey, outKeyLen, "To");
array_to_hexstr(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.owner, 32);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
return parser_ok;

case 1:
if (known_asset_id) {
snprintf(outKey, outKeyLen, "Amount");
CHECK_ERROR(
printAmount64(ctx->tx_obj->outputs.decrypted_note.value, asset_id_lookups[asset_id_idx].decimals,
PIC(asset_id_lookups[asset_id_idx].name), outVal, outValLen, pageIdx, pageCount));
return parser_ok;
} else {
snprintf(outKey, outKeyLen, "Raw amount");
uint64_to_str(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.value);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
return parser_ok;
}

case 2:
snprintf(outKey, outKeyLen, "Raw Asset ID");
array_to_hexstr(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.asset_id, 32);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
return parser_ok;
}
}
} else if (tmp_idx > total_out_elements) {
tmp_idx -= total_out_elements - ELEMENTS_PER_OUTPUT + 1;

cumulative_display_count += elements_in_output; // Increment for next output
}

char buf[70] = {0};
switch (tmp_idx) {
case 0:
snprintf(outKey, outKeyLen, "AssetID %d", out_idx);
array_to_hexstr(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.asset_id, 32);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
return parser_ok;
case 1:
snprintf(outKey, outKeyLen, "Owner %d", out_idx);
array_to_hexstr(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.owner, 32);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
return parser_ok;
case 2:
snprintf(outKey, outKeyLen, "Amount %d", out_idx);
uint64_to_str(buf, sizeof(buf), ctx->tx_obj->outputs.decrypted_note.value);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
return parser_ok;
case 3:
snprintf(outKey, outKeyLen, "Fee");
uint64_to_str(buf, sizeof(buf), ctx->tx_obj->fee);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
return parser_ok;
case 4:
snprintf(outKey, outKeyLen, "Expiration");
uint32_to_str(buf, sizeof(buf), ctx->tx_obj->expiration);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
return parser_ok;
default:
break;
// Handle "Fee" and "Expiration" after all outputs
uint8_t additional_elements_start = cumulative_display_count;

if (tmp_idx == additional_elements_start) {
snprintf(outKey, outKeyLen, "Fee");
CHECK_ERROR(printAmount64(ctx->tx_obj->fee, asset_id_lookups[0].decimals, PIC(asset_id_lookups[0].name), outVal,
outValLen, pageIdx, pageCount));
return parser_ok;
}

if (tmp_idx == additional_elements_start + 1) {
snprintf(outKey, outKeyLen, "Expiration");
uint32_to_str(buf, sizeof(buf), ctx->tx_obj->expiration);
pageString(outVal, outValLen, buf, pageIdx, pageCount);
return parser_ok;
}

return parser_display_idx_out_of_range;
return parser_display_idx_out_of_range; // If nothing matched
}

const char *parser_getErrorDescription(parser_error_t err) {
Expand Down Expand Up @@ -192,6 +311,8 @@ const char *parser_getErrorDescription(parser_error_t err) {
return "display index out of range";
case parser_display_page_out_of_range:
return "display page out of range";
case parser_require_expert_mode:
return "Expert mode required";

default:
return "Unrecognized error code";
Expand Down
5 changes: 5 additions & 0 deletions app/src/parser_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ parser_error_t _read(parser_context_t *ctx, parser_tx_t *v) {
CHECK_ERROR(readTransactionVersion(ctx, &v->transactionVersion));
CHECK_ERROR(readUint64(ctx, &v->spends.elements));
CHECK_ERROR(readUint64(ctx, &v->outputs.elements));
// Check number of outputs is <= than the number of supported outputs in the render mask
if (v->outputs.elements > 64) {
return parser_unexpected_number_items;
}

CHECK_ERROR(readUint64(ctx, &v->mints.elements));
CHECK_ERROR(readUint64(ctx, &v->burns.elements));
CHECK_ERROR(readInt64(ctx, &v->fee));
Expand Down
2 changes: 1 addition & 1 deletion app/src/parser_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
extern "C" {
#endif

#define ELEMENTS_PER_OUTPUT 3
#define ELEMENTS_PER_OUTPUT 2
#define OUTPUT_ELEMENT_OFFSET 1
/**
* @brief Checks that there are at least SIZE bytes available in the buffer.
Expand Down
Loading
Loading