Skip to content

Commit 0f6b830

Browse files
WIP
1 parent 342236f commit 0f6b830

37 files changed

Lines changed: 289 additions & 79 deletions

.vscode/settings.json

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
11
{
22
"files.associations": {
3-
"*.h": "c"
3+
"*.h": "c",
4+
"optional": "c",
5+
"istream": "c",
6+
"ostream": "c",
7+
"ratio": "c",
8+
"array": "c",
9+
"functional": "c",
10+
"tuple": "c",
11+
"type_traits": "c",
12+
"utility": "c",
13+
"system_error": "c"
414
},
515
"ledgerDevTools.appSettings": {
6-
"selectedUseCase": "release",
7-
"selectedDevice": "Nano X",
16+
"selectedUseCase": "debug",
17+
"selectedDevice": "Stax",
818
"selectedVariant": "BOL",
919
"testsList": [
1020
"test_app_mainmenu",
1121
"test_app_name",
12-
"test_bad_cla",
13-
"test_bad_ins",
14-
"test_wrong_p1p2",
15-
"test_wrong_data_length",
16-
"test_invalid_state",
1722
"test_get_app_and_version",
1823
"test_get_public_key_no_confirm",
1924
"test_get_public_key_confirm_accepted",
2025
"test_get_public_key_confirm_refused",
21-
"test_sign_tx_short_tx",
22-
"test_sign_tx_short_tx_blind_sign",
23-
"test_sign_tx_long_tx",
24-
"test_sign_tx_refused",
26+
"test_sign_tx_hash_32",
27+
"test_sign_tx_hash_34",
28+
"test_sign_tx_ping",
29+
"test_sign_topology_tx",
2530
"test_version"
31+
],
32+
"selectedTests": [
33+
"test_sign_tx_token_transfer"
2634
]
2735
}
2836
}

src/handler/sign_tx.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,19 @@ int handler_sign_tx(buffer_t *cdata, signing_type_e type, bool first, bool more,
7979
if (result != 0) {
8080
return io_send_sw(result); // Send the error code via io_send_sw
8181
}
82-
return (G_context.state == STATE_PARSED) ? ui_display_blind_signed_transaction()
83-
: io_send_sw(SW_OK);
82+
83+
if (G_context.state == STATE_PARSED) {
84+
if (G_context.tx_info.clear_signing_available == true) {
85+
return ui_display_transaction();
86+
} else {
87+
return ui_display_blind_signed_transaction();
88+
}
89+
} else {
90+
return io_send_sw(SW_OK);
91+
}
92+
93+
// return (G_context.state == STATE_PARSED) ? ui_display_blind_signed_transaction()
94+
// : io_send_sw(SW_OK);
8495
} else {
8596
// Invalid state
8697
PRINTF("Invalid state after processing chunk: %d\n", G_context.state);
@@ -98,6 +109,7 @@ static int process_tx_chunk(buffer_t *cdata,
98109
PRINTF("Processing first chunk of transaction\n");
99110
G_context.req_type = CONFIRM_TRANSACTION;
100111
G_context.signing_type = type;
112+
G_context.tx_info.clear_signing_available = false;
101113
G_context.state = STATE_EXPECTING_MORE;
102114

103115
if (type == SIGN_PREPARED_TRANSACTION) {

src/transaction/canonical_hash.c

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -24,50 +24,6 @@ static const uint8_t PREPARED_TRANSACTION_HASH_PURPOSE[4] = {0x00, 0x00, 0x00, 0
2424
#define HASHING_SCHEME_VERSION ((uint8_t) 2) /* 0x02 */
2525
#define NODE_ENCODING_VERSION ((uint8_t) 1) /* 0x01 */
2626

27-
/* -------------------------------------------------------------------------- */
28-
/* Adaption layer for nanopb oneof names */
29-
/* -------------------------------------------------------------------------- */
30-
#define VALUE_ONEOF_FIELD which_sum
31-
#define VALUE_UNIT_TAG com_daml_ledger_api_v2_Value_unit_tag
32-
#define VALUE_BOOL_TAG com_daml_ledger_api_v2_Value_bool__tag
33-
#define VALUE_INT64_TAG com_daml_ledger_api_v2_Value_int64_tag
34-
#define VALUE_NUMERIC_TAG com_daml_ledger_api_v2_Value_numeric_tag
35-
#define VALUE_TIMESTAMP_TAG com_daml_ledger_api_v2_Value_timestamp_tag
36-
#define VALUE_DATE_TAG com_daml_ledger_api_v2_Value_date_tag
37-
#define VALUE_PARTY_TAG com_daml_ledger_api_v2_Value_party_tag
38-
#define VALUE_TEXT_TAG com_daml_ledger_api_v2_Value_text_tag
39-
#define VALUE_CONTRACT_ID_TAG com_daml_ledger_api_v2_Value_contract_id_tag
40-
#define VALUE_OPTIONAL_TAG com_daml_ledger_api_v2_Value_optional_tag
41-
#define VALUE_LIST_TAG com_daml_ledger_api_v2_Value_list_tag
42-
#define VALUE_TEXT_MAP_TAG com_daml_ledger_api_v2_Value_text_map_tag
43-
#define VALUE_RECORD_TAG com_daml_ledger_api_v2_Value_record_tag
44-
#define VALUE_VARIANT_TAG com_daml_ledger_api_v2_Value_variant_tag
45-
#define VALUE_ENUM_TAG com_daml_ledger_api_v2_Value_enum__tag
46-
#define VALUE_GEN_MAP_TAG com_daml_ledger_api_v2_Value_gen_map_tag
47-
48-
// Node version and kind oneofs
49-
#define NODE_VERSION_ONEOF_FIELD which_versioned_node
50-
#define NODE_V1_TAG com_daml_ledger_api_v2_interactive_DeviceDamlTransaction_Node_v1_tag
51-
#define NODE_V1_KIND_ONEOF_FIELD which_node_type
52-
#define NODE_V1_CREATE_TAG com_daml_ledger_api_v2_interactive_transaction_v1_Node_create_tag
53-
#define NODE_V1_EXERCISE_TAG com_daml_ledger_api_v2_interactive_transaction_v1_Node_exercise_tag
54-
#define NODE_V1_FETCH_TAG com_daml_ledger_api_v2_interactive_transaction_v1_Node_fetch_tag
55-
#define NODE_V1_ROLLBACK_TAG com_daml_ledger_api_v2_interactive_transaction_v1_Node_rollback_tag
56-
57-
typedef com_daml_ledger_api_v2_interactive_transaction_v1_Node Node_V1;
58-
typedef com_daml_ledger_api_v2_interactive_DeviceDamlTransaction_NodeSeed NodeSeed;
59-
60-
typedef com_daml_ledger_api_v2_interactive_transaction_v1_Create Node_Create;
61-
typedef com_daml_ledger_api_v2_interactive_transaction_v1_Exercise Node_Exercise;
62-
typedef com_daml_ledger_api_v2_interactive_transaction_v1_Fetch Node_Fetch;
63-
typedef com_daml_ledger_api_v2_interactive_transaction_v1_Rollback Node_Rollback;
64-
65-
typedef com_daml_ledger_api_v2_Value Value;
66-
typedef com_daml_ledger_api_v2_RecordField RecordField;
67-
typedef com_daml_ledger_api_v2_GenMap_Entry GenMapEntry;
68-
typedef com_daml_ledger_api_v2_TextMap_Entry TextMapEntry;
69-
typedef com_daml_ledger_api_v2_Identifier Identifier;
70-
7127
/* -------------------------------------------------------------------------- */
7228
/* Error handling */
7329
/* -------------------------------------------------------------------------- */

src/transaction/canonical_hash.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#pragma once
22

33
#include "cx.h"
4-
4+
#include "com/daml/ledger/api/v2/interactive/interactive_submission_service.pb.h"
5+
#include "bytewriter.h"
56
#include "tx_types.h"
67

78
typedef struct {

src/transaction/prepared_transaction.c

Lines changed: 178 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,58 @@
66
#include "os.h"
77
#include "cx.h"
88
#include "buffer.h"
9+
#include "mem.h" // for app_mem_alloc
910

1011
#include "sign_tx.h"
1112
#include "sw.h"
1213
#include "globals.h"
1314
#include "display.h"
1415
#include "tx_types.h"
1516
#include "proto_deserialize.h"
17+
#include "prepared_transaction.h"
1618
#include "validate.h"
1719
#include "canonical_hash.h"
1820

21+
#define TRANSFER_CMD_DISPLAY_FIELDS 4
22+
23+
// Configuration structures
24+
typedef struct {
25+
const char *package_id;
26+
const char *module_name;
27+
const char *entity_name;
28+
} identifier_config_t;
29+
30+
// Configuration constants
31+
static const identifier_config_t EXTERNAL_PARTY_AMULET_RULES_TEMPLATE = {
32+
.package_id = "95a88ff9ffd509e097802ecf3bbd58c83a5dff408e439cca4e2105ebd2cd0760",
33+
.module_name = "Splice.ExternalPartyAmuletRules",
34+
.entity_name = "ExternalPartyAmuletRules"};
35+
36+
static const char *TRANSFER_FACTORY_CHOICE_ID = "TransferFactory_Transfer";
37+
38+
static const identifier_config_t TRANSFER_RECORD = {
39+
.package_id = "55ba4deb0ad4662c4168b39859738a0e91388d252286480c7331b3f71a517281",
40+
.module_name = "Splice.Api.Token.TransferInstructionV1",
41+
.entity_name = "TransferFactory_Transfer"};
42+
43+
typedef struct {
44+
const char *item_name;
45+
int field_index;
46+
} field_display_t;
47+
48+
// const char sender_label[] = "Sender";
49+
// const char receiver_label[] = "Receiver";
50+
// const char amount_label[] = "Amount";
51+
// const char instrument_id_label[] = "Instrument ID";
52+
53+
// Field display configuration
54+
const field_display_t SENDER_FIELD = {"Sender", 0};
55+
const field_display_t RECEIVER_FIELD = {"Receiver", 1};
56+
const field_display_t AMOUNT_FIELD = {"Amount", 2};
57+
const field_display_t INSTRUMENT_ID_FIELD = {"Instrument ID", 3};
58+
59+
#define TRANSFER_CMD_DISPLAY_FIELDS 4
60+
1961
typedef enum {
2062
RECEIVING_DAML_TX_PART, /// Receiving part of DAML transaction
2163
RECEIVING_DAML_NODES, /// Receiving DAML nodes
@@ -24,8 +66,8 @@ typedef enum {
2466
} prepared_tx_receiving_state_e;
2567

2668
static prepared_tx_receiving_state_e tx_state = RECEIVING_DAML_TX_PART;
27-
2869
static int process_prepared_tx_finalize();
70+
static void parse_node_for_display(const Node *node, transaction_ctx_t *tx_info);
2971

3072
void process_prepared_tx_init() {
3173
tx_state = RECEIVING_DAML_TX_PART;
@@ -66,6 +108,9 @@ int process_prepared_tx_part(buffer_t *buf) {
66108
&G_context.tx_info.tx_parts_ctx.daml_transaction,
67109
&G_context.tx_info.tx_parts_ctx.node);
68110

111+
// Extract displayable fields from the node
112+
parse_node_for_display(&G_context.tx_info.tx_parts_ctx.node, &G_context.tx_info);
113+
69114
release_node(&G_context.tx_info);
70115

71116
if (res != 0) {
@@ -176,3 +221,135 @@ static int process_prepared_tx_finalize() {
176221

177222
return 0;
178223
}
224+
225+
// Helper function to match identifiers
226+
static bool match_identifier(const Identifier *id, const identifier_config_t *config) {
227+
return strcmp(id->package_id, (char *) PIC(config->package_id)) == 0 &&
228+
strcmp(id->module_name, (char *) PIC(config->module_name)) == 0 &&
229+
strcmp(id->entity_name, (char *) PIC(config->entity_name)) == 0;
230+
}
231+
232+
// Helper function to initialize transaction pairs
233+
static bool init_transaction_pairs(transaction_ctx_t *tx_info, size_t count) {
234+
// Free existing pairs if any
235+
if (tx_info->pairs != NULL) {
236+
app_mem_free(tx_info->pairs);
237+
}
238+
239+
// Free existing allocated strings if any
240+
if (tx_info->allocated_strings != NULL) {
241+
// Free individual strings first
242+
for (size_t i = 0; i < tx_info->pairs_count; i++) {
243+
if (tx_info->allocated_strings[i] != NULL) {
244+
app_mem_free(tx_info->allocated_strings[i]);
245+
}
246+
}
247+
app_mem_free(tx_info->allocated_strings);
248+
}
249+
250+
// Allocate new arrays
251+
tx_info->pairs_count = count;
252+
tx_info->pairs =
253+
(nbgl_contentTagValue_t *) app_mem_alloc(count * sizeof(nbgl_contentTagValue_t));
254+
tx_info->allocated_strings = (char **) app_mem_alloc(count * sizeof(char *));
255+
256+
if (tx_info->pairs == NULL || tx_info->allocated_strings == NULL) {
257+
tx_info->pairs_count = 0;
258+
return false;
259+
}
260+
261+
memset(tx_info->pairs, 0, count * sizeof(nbgl_contentTagValue_t));
262+
memset(tx_info->allocated_strings, 0, count * sizeof(char *));
263+
264+
return true;
265+
}
266+
267+
// Helper function to set field value safely with context-managed memory
268+
static void set_field_value(transaction_ctx_t *tx_info,
269+
const field_display_t *field_config,
270+
const char *value) {
271+
if (field_config->field_index < TRANSFER_CMD_DISPLAY_FIELDS && value != NULL) {
272+
size_t value_len = strlen(value) + 1;
273+
274+
// Allocate memory in the context's allocated_strings array
275+
tx_info->allocated_strings[field_config->field_index] = (char *) app_mem_alloc(value_len);
276+
if (tx_info->allocated_strings[field_config->field_index] != NULL) {
277+
memcpy(tx_info->allocated_strings[field_config->field_index], value, value_len);
278+
tx_info->pairs[field_config->field_index].item = (char *) PIC(field_config->item_name);
279+
tx_info->pairs[field_config->field_index].value =
280+
tx_info->allocated_strings[field_config->field_index];
281+
}
282+
}
283+
}
284+
285+
// Process transfer fields - maintains single-pass efficiency
286+
static void process_transfer_fields(const com_daml_ledger_api_v2_Record *rec,
287+
transaction_ctx_t *tx_info) {
288+
// Single pass through top-level fields
289+
for (size_t i = 0; i < rec->fields_count; i++) {
290+
const RecordField *field = &rec->fields[i];
291+
292+
if (strcmp(field->label, "transfer") == 0) {
293+
const com_daml_ledger_api_v2_Record *transfer_rec = &field->value->record;
294+
for (size_t j = 0; j < transfer_rec->fields_count; j++) {
295+
const RecordField *transfer_field = &transfer_rec->fields[j];
296+
297+
if (strcmp(transfer_field->label, "sender") == 0) {
298+
set_field_value(tx_info, &SENDER_FIELD, transfer_field->value->party);
299+
} else if (strcmp(transfer_field->label, "receiver") == 0) {
300+
set_field_value(tx_info, &RECEIVER_FIELD, transfer_field->value->party);
301+
} else if (strcmp(transfer_field->label, "amount") == 0) {
302+
set_field_value(tx_info, &AMOUNT_FIELD, transfer_field->value->numeric);
303+
} else if (strcmp(transfer_field->label, "instrumentId") == 0) {
304+
const com_daml_ledger_api_v2_Record *inst_id_rec =
305+
&transfer_field->value->record;
306+
// Single pass through instrument ID fields
307+
for (size_t k = 0; k < inst_id_rec->fields_count; k++) {
308+
const RecordField *inst_id_field = &inst_id_rec->fields[k];
309+
if (strcmp(inst_id_field->label, "id") == 0) {
310+
set_field_value(tx_info,
311+
&INSTRUMENT_ID_FIELD,
312+
inst_id_field->value->text);
313+
}
314+
}
315+
}
316+
}
317+
}
318+
}
319+
}
320+
321+
// Helper function to process transfer command
322+
static void process_transfer_command(const com_daml_ledger_api_v2_Record *rec,
323+
transaction_ctx_t *tx_info) {
324+
if (!init_transaction_pairs(tx_info, TRANSFER_CMD_DISPLAY_FIELDS)) {
325+
return;
326+
}
327+
process_transfer_fields(rec, tx_info);
328+
tx_info->clear_signing_available = true;
329+
}
330+
331+
// Helper function to process exercise node
332+
static void process_exercise_node(const Node_Exercise *exercise, transaction_ctx_t *tx_info) {
333+
if (!exercise->has_template_id) {
334+
return;
335+
}
336+
337+
// Check if this is a transfer command
338+
if (match_identifier(&exercise->template_id, &EXTERNAL_PARTY_AMULET_RULES_TEMPLATE) &&
339+
strcmp(exercise->choice_id, TRANSFER_FACTORY_CHOICE_ID) == 0 &&
340+
match_identifier(&exercise->chosen_value.record.record_id, &TRANSFER_RECORD)) {
341+
process_transfer_command(&exercise->chosen_value.record, tx_info);
342+
}
343+
}
344+
345+
// Main parsing function
346+
static void parse_node_for_display(const Node *node, transaction_ctx_t *tx_info) {
347+
if (node->NODE_VERSION_ONEOF_FIELD != NODE_V1_TAG) {
348+
return;
349+
}
350+
351+
const Node_V1 *v = &node->v1;
352+
if (v->NODE_V1_KIND_ONEOF_FIELD == NODE_V1_EXERCISE_TAG) {
353+
process_exercise_node(&v->exercise, tx_info);
354+
}
355+
}

0 commit comments

Comments
 (0)