Skip to content

Commit

Permalink
Merge pull request #44 from LedgerHQ/wip/fbeutin/swap_rebase_post_ref…
Browse files Browse the repository at this point in the history
…acto

Add Swap feature to Solana application
  • Loading branch information
fbeutin-ledger authored Apr 18, 2023
2 parents bc5e535 + e891ce5 commit f7e9932
Show file tree
Hide file tree
Showing 24 changed files with 506 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
path: scan-build

python_tests_nano:
name: NanoS Ragger tests
name: Ragger tests
needs: build_application
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ jobs:
# Use Config file when the github action supports it
builtin: clear,rare
check_filenames: true
skip: ./libsol,./tests
skip: ./libsol/printer_test.c,./tests/Cargo.lock
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ endif

include $(BOLOS_SDK)/Makefile.defines

APP_LOAD_PARAMS = --curve ed25519
ifeq ($(TARGET_NAME), TARGET_NANOX)
APP_LOAD_PARAMS += --appFlags 0x200 # APPLICATION_FLAG_BOLOS_SETTINGS
APP_LOAD_PARAMS = --curve ed25519
ifeq ($(TARGET_NAME), TARGET_NANOS)
APP_LOAD_PARAMS += --appFlags 0x800 # APPLICATION_FLAG_LIBRARY
else
APP_LOAD_PARAMS += --appFlags 0x000
APP_LOAD_PARAMS += --appFlags 0xa00 # APPLICATION_FLAG_LIBRARY + APPLICATION_FLAG_BOLOS_SETTINGS
endif
APP_LOAD_PARAMS += --path "44'/501'"
APP_LOAD_PARAMS += $(COMMON_LOAD_PARAMS)
Expand Down
1 change: 1 addition & 0 deletions doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
## 1.3.0

- Add SIGN SOLANA OFF-CHAIN MESSAGE
- Add compatibility with the Exchange Application to SWAP, FUND, or SELL SOL tokens

## About

Expand Down
2 changes: 1 addition & 1 deletion libsol/message_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <assert.h>
#include <stdio.h>

// Disable clang format for this file to keep clear buffer formating
// Disable clang format for this file to keep clear buffer formatting
/* clang-format off */

void test_process_message_body_ok() {
Expand Down
2 changes: 1 addition & 1 deletion src/apdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,4 @@ int apdu_handle_message(const uint8_t* apdu_message,
apdu_command->state = ApduStatePayloadComplete;

return 0;
}
}
4 changes: 2 additions & 2 deletions src/getPubkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ UX_STEP_NOCB(ux_display_public_flow_5_step,
});
UX_STEP_CB(ux_display_public_flow_6_step,
pb,
sendResponse(set_result_get_pubkey(), true),
sendResponse(set_result_get_pubkey(), true, true),
{
&C_icon_validate_14,
"Approve",
});
UX_STEP_CB(ux_display_public_flow_7_step,
pb,
sendResponse(0, false),
sendResponse(0, false, true),
{
&C_icon_crossmark,
"Reject",
Expand Down
5 changes: 4 additions & 1 deletion src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ typedef enum InstructionCode {
InsSignOffchainMessage = 0x07
} InstructionCode;

extern volatile bool G_called_from_swap;
extern volatile bool G_swap_response_ready;

// display stepped screens
extern unsigned int ux_step;
extern unsigned int ux_step_count;
Expand Down Expand Up @@ -84,4 +87,4 @@ typedef struct internalStorage_t {

extern const internalStorage_t N_storage_real;
#define N_storage (*(volatile internalStorage_t*) PIC(&N_storage_real))
#endif
#endif
109 changes: 101 additions & 8 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@
#include "apdu.h"
#include "menu.h"

// Swap feature
#include "swap_lib_calls.h"
#include "handle_swap_sign_transaction.h"
#include "handle_get_printable_amount.h"
#include "handle_check_address.h"

ApduCommand G_command;
unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B];
volatile bool G_called_from_swap;
volatile bool G_swap_response_ready;

static void reset_main_globals(void) {
MEMCLEAR(G_command);
Expand All @@ -41,8 +49,10 @@ void handleApdu(volatile unsigned int *flags, volatile unsigned int *tx, int rx)

const int ret = apdu_handle_message(G_io_apdu_buffer, rx, &G_command);
if (ret != 0) {
MEMCLEAR(G_command);
THROW(ret);
}

if (G_command.state == ApduStatePayloadInProgress) {
THROW(ApduReplySuccess);
}
Expand Down Expand Up @@ -96,7 +106,6 @@ void app_main(void) {
// APDU injection faults.
for (;;) {
volatile unsigned short sw = 0;

BEGIN_TRY {
TRY {
rx = tx;
Expand All @@ -105,6 +114,12 @@ void app_main(void) {
rx = io_exchange(CHANNEL_APDU | flags, rx);
flags = 0;

if (G_called_from_swap && G_swap_response_ready) {
PRINTF("Quitting app started in swap mode\n");
// Quit app, we are in limited mode and our work is done
os_sched_exit(0);
}

// no apdu received, well, reset the session, and reset the
// bootloader configuration
if (rx == 0) {
Expand Down Expand Up @@ -260,13 +275,8 @@ void nv_app_state_init() {
}
}

__attribute__((section(".boot"))) int main(void) {
// exit critical section
__asm volatile("cpsie i");

// ensure exception will work as planned
os_boot();

void coin_main(void) {
G_called_from_swap = false;
for (;;) {
UX_INIT();

Expand Down Expand Up @@ -307,5 +317,88 @@ __attribute__((section(".boot"))) int main(void) {
END_TRY;
}
app_exit();
}

static void start_app_from_lib(void) {
G_called_from_swap = true;
G_swap_response_ready = false;
UX_INIT();
io_seproxyhal_init();
nv_app_state_init();
USB_power(0);
USB_power(1);
#ifdef HAVE_BLE
// Erase globals that may inherit values from exchange
MEMCLEAR(G_io_asynch_ux_callback);
// grab the current plane mode setting
G_io_app.plane_mode = os_setting_get(OS_SETTING_PLANEMODE, NULL, 0);
BLE_power(0, NULL);
BLE_power(1, "Nano X");
#endif // HAVE_BLE
app_main();
}

static void library_main_helper(libargs_t *args) {
check_api_level(CX_COMPAT_APILEVEL);
switch (args->command) {
case CHECK_ADDRESS:
// ensure result is zero if an exception is thrown
args->check_address->result = 0;
args->check_address->result = handle_check_address(args->check_address);
break;
case SIGN_TRANSACTION:
if (copy_transaction_parameters(args->create_transaction)) {
// never returns
start_app_from_lib();
}
break;
case GET_PRINTABLE_AMOUNT:
handle_get_printable_amount(args->get_printable_amount);
break;
default:
break;
}
}

static void library_main(libargs_t *args) {
volatile bool end = false;
/* This loop ensures that library_main_helper and os_lib_end are called
* within a try context, even if an exception is thrown */
while (1) {
BEGIN_TRY {
TRY {
if (!end) {
library_main_helper(args);
}
os_lib_end();
}
FINALLY {
end = true;
}
}
END_TRY;
}
}

__attribute__((section(".boot"))) int main(int arg0) {
// exit critical section
__asm volatile("cpsie i");

// ensure exception will work as planned
os_boot();

if (arg0 == 0) {
// called from dashboard as standalone app
coin_main();
} else {
// Called as library from another app
libargs_t *args = (libargs_t *) arg0;
if (args->id == 0x100) {
library_main(args);
} else {
app_exit();
}
}

return 0;
}
77 changes: 63 additions & 14 deletions src/signMessage.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "globals.h"
#include "apdu.h"

#include "handle_swap_sign_transaction.h"

static uint8_t set_result_sign_message() {
uint8_t signature[SIGNATURE_LENGTH];
cx_ecfp_private_key_t privateKey;
Expand Down Expand Up @@ -44,22 +46,18 @@ static uint8_t set_result_sign_message() {
return SIGNATURE_LENGTH;
}

static void send_result_sign_message(void) {
sendResponse(set_result_sign_message(), true);
}

//////////////////////////////////////////////////////////////////////

UX_STEP_CB(ux_approve_step,
pb,
send_result_sign_message(),
sendResponse(set_result_sign_message(), true, true),
{
&C_icon_validate_14,
"Approve",
});
UX_STEP_CB(ux_reject_step,
pb,
sendResponse(0, false),
sendResponse(0, false, true),
{
&C_icon_crossmark,
"Reject",
Expand Down Expand Up @@ -168,22 +166,73 @@ void handle_sign_message_parse_message(volatile unsigned int *tx) {
}
}

static bool check_swap_validity(const SummaryItemKind_t kinds[MAX_TRANSACTION_SUMMARY_ITEMS],
size_t num_summary_steps) {
bool amount_ok = false;
bool recipient_ok = false;
if (num_summary_steps != 2) {
PRINTF("2 steps expected for transaction in swap context, not %u\n", num_summary_steps);
return false;
}
for (size_t i = 0; i < num_summary_steps; ++i) {
transaction_summary_display_item(i, DisplayFlagNone | DisplayFlagLongPubkeys);
switch (kinds[i]) {
case SummaryItemAmount:
amount_ok =
check_swap_amount(G_transaction_summary_title, G_transaction_summary_text);
break;
case SummaryItemPubkey:
recipient_ok =
check_swap_recipient(G_transaction_summary_title, G_transaction_summary_text);
break;
default:
PRINTF("Refused kind '%u'\n", kinds[i]);
return false;
}
}
return amount_ok && recipient_ok;
}

void handle_sign_message_ui(volatile unsigned int *flags) {
// Display the transaction summary
SummaryItemKind_t summary_step_kinds[MAX_TRANSACTION_SUMMARY_ITEMS];
size_t num_summary_steps = 0;
if (transaction_summary_finalize(summary_step_kinds, &num_summary_steps) == 0) {
size_t num_flow_steps = 0;
// If we are in swap context, do not redisplay the message data
// Instead, ensure they are identitical with what was previously displayed
if (G_called_from_swap) {
if (G_swap_response_ready) {
// Safety against trying to make the app sign multiple TX
// This code should never be triggered as the app is supposed to exit after
// sending the signed transaction
PRINTF("Safety against double signing triggered\n");
os_sched_exit(-1);
} else {
// We will quit the app after this transaction, whether it succeeds or fails
PRINTF("Swap response is ready, the app will quit after the next send\n");
G_swap_response_ready = true;
}
if (check_swap_validity(summary_step_kinds, num_summary_steps)) {
PRINTF("Valid swap transaction signed\n");
sendResponse(set_result_sign_message(), true, false);
} else {
PRINTF("Refused signing incorrect Swap transaction\n");
THROW(ApduReplySolanaSummaryFinalizeFailed);
}
} else {
MEMCLEAR(flow_steps);
size_t num_flow_steps = 0;

for (size_t i = 0; i < num_summary_steps; i++) {
flow_steps[num_flow_steps++] = &ux_summary_step;
}
for (size_t i = 0; i < num_summary_steps; i++) {
flow_steps[num_flow_steps++] = &ux_summary_step;
}

flow_steps[num_flow_steps++] = &ux_approve_step;
flow_steps[num_flow_steps++] = &ux_reject_step;
flow_steps[num_flow_steps++] = FLOW_END_STEP;
flow_steps[num_flow_steps++] = &ux_approve_step;
flow_steps[num_flow_steps++] = &ux_reject_step;
flow_steps[num_flow_steps++] = FLOW_END_STEP;

ux_flow_init(0, flow_steps, NULL);
ux_flow_init(0, flow_steps, NULL);
}
} else {
THROW(ApduReplySolanaSummaryFinalizeFailed);
}
Expand Down
4 changes: 2 additions & 2 deletions src/signOffchainMessage.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,14 @@ UX_STEP_NOCB(ux_sign_msg_text_step,
});
UX_STEP_CB(ux_sign_msg_approve_step,
pb,
sendResponse(set_result_sign_message(), true),
sendResponse(set_result_sign_message(), true, true),
{
&C_icon_validate_14,
"Approve",
});
UX_STEP_CB(ux_sign_msg_reject_step,
pb,
sendResponse(0, false),
sendResponse(0, false, true),
{
&C_icon_crossmark,
"Reject",
Expand Down
Loading

0 comments on commit f7e9932

Please sign in to comment.