Skip to content
This repository was archived by the owner on Dec 26, 2022. It is now read-only.

Commit 395c1b1

Browse files
committed
feat(mam): Send mam message from given chid
The current implemetaion of MAM service is a data structure which is similar to linked-list. This structure causes searching time increase linearly, so if the user is searching some information with a large N, then then elapsing time would be really long, too. Therefore, in this PR, tangle-accelerator starts to support searching MAM message from a given channel ID which can dramatically reduce searching time with a proper given channel ID. Close #610
1 parent efa87d3 commit 395c1b1

File tree

9 files changed

+166
-28
lines changed

9 files changed

+166
-28
lines changed

accelerator/core/mam_core.c

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
#define MAM_LOGGER "mam_core"
1414

15+
typedef struct channel_info_s {
16+
int32_t ch_mss_depth;
17+
tryte_t *chid;
18+
} channel_info_t;
19+
1520
static logger_id_t logger_id;
1621

1722
void ta_mam_logger_init() { logger_id = logger_helper_enable(MAM_LOGGER, LOGGER_DEBUG, true); }
@@ -230,7 +235,7 @@ static status_t ta_mam_init(mam_api_t *const api, const iota_config_t *const ico
230235
}
231236

232237
static status_t create_channel_fetch_all_transactions(const iota_client_service_t *const service, mam_api_t *const api,
233-
const size_t channel_depth, tryte_t *const chid,
238+
const size_t channel_depth, bool first_iter, tryte_t *const chid,
234239
hash81_array_p tag_array) {
235240
status_t ret = SC_OK;
236241
find_transactions_req_t *txn_req = find_transactions_req_new();
@@ -241,10 +246,14 @@ static status_t create_channel_fetch_all_transactions(const iota_client_service_
241246
goto done;
242247
}
243248

244-
if (mam_api_channel_create(api, channel_depth, chid) != RC_OK) {
245-
ret = SC_MAM_FAILED_CREATE_OR_GET_ID;
246-
ta_log_error("%s\n", ta_error_to_string(ret));
247-
goto done;
249+
// We have created a channel when we want to get to the given channel at the first loop in
250+
// `ta_mam_written_msg_to_bundle()`, so we don't need to create a channel once again.
251+
if (!first_iter) {
252+
if (mam_api_channel_create(api, channel_depth, chid) != RC_OK) {
253+
ret = SC_MAM_FAILED_CREATE_OR_GET_ID;
254+
ta_log_error("%s\n", "SC_MAM_FAILED_CREATE_OR_GET_ID");
255+
goto done;
256+
}
248257
}
249258

250259
flex_trit_t chid_flex_trit[NUM_TRITS_ADDRESS];
@@ -271,6 +280,35 @@ static status_t create_channel_fetch_all_transactions(const iota_client_service_
271280
return ret;
272281
}
273282

283+
static bool is_setting_changed(const iota_client_service_t *const service, mam_api_t *const api,
284+
const size_t channel_depth, tryte_t *const chid) {
285+
bool changed = true;
286+
find_transactions_req_t *txn_req = find_transactions_req_new();
287+
transaction_array_t *txn_res = transaction_array_new();
288+
289+
if (mam_api_channel_create(api, channel_depth, chid) != RC_OK) {
290+
ta_log_error("%s\n", ta_error_to_string(SC_MAM_FAILED_CREATE_OR_GET_ID));
291+
goto done;
292+
}
293+
294+
flex_trit_t chid_flex_trit[NUM_TRITS_ADDRESS];
295+
flex_trits_from_trytes(chid_flex_trit, NUM_TRITS_ADDRESS, chid, NUM_TRYTES_ADDRESS, NUM_TRYTES_ADDRESS);
296+
hash243_queue_push(&txn_req->addresses, chid_flex_trit);
297+
// TODO use `ta_find_transaction_objects(service, txn_req, txn_res)` instead of the original entangled function
298+
retcode_t ret_rc = iota_client_find_transaction_objects(service, txn_req, txn_res);
299+
if (ret_rc && ret_rc != RC_NULL_PARAM) {
300+
ta_log_error("%s\n", SC_MAM_FAILED_DESTROYED);
301+
goto done;
302+
}
303+
304+
changed = (transaction_array_len(txn_res) == 0);
305+
306+
done:
307+
find_transactions_req_free(&txn_req);
308+
transaction_array_free(txn_res);
309+
return changed;
310+
}
311+
274312
/**
275313
* @brief Write payload to bundle on the smallest secret key.
276314
*
@@ -290,27 +328,62 @@ static status_t create_channel_fetch_all_transactions(const iota_client_service_
290328
* @return return code
291329
*/
292330
static status_t ta_mam_written_msg_to_bundle(const iota_client_service_t *const service, mam_api_t *const api,
293-
const size_t channel_depth, mam_encrypt_key_t mam_key,
331+
const channel_info_t *channel_info, mam_encrypt_key_t mam_key,
294332
char const *const payload, bundle_transactions_t **bundle,
295333
tryte_t *const chid, tryte_t *const msg_id,
296334
mam_send_operation_t *mam_operation) {
297335
status_t ret = SC_OK;
298-
if (!service || !api || !chid || !msg_id || channel_depth < 1) {
336+
if (!service || !api || !chid || !msg_id || !channel_info || channel_info->ch_mss_depth < 1) {
299337
ret = SC_MAM_NULL;
300338
ta_log_error("%s\n", ta_error_to_string(ret));
301339
return ret;
302340
}
303341
trit_t msg_id_trits[MAM_MSG_ID_SIZE];
304342
hash81_array_p tag_array = hash81_array_new();
343+
344+
// Get to the assigned beginning channel ID. If the initial setting is different, then tangle-accelerator won't be
345+
// able to generate same chid.
346+
if (channel_info->chid) {
347+
// The current setting hasn't been used on Tangle, so we should take the normal procedure.
348+
if (is_setting_changed(service, api, channel_info->ch_mss_depth, chid)) {
349+
goto end_find_starting_chid;
350+
}
351+
352+
int cnt = 0;
353+
while (memcmp(channel_info->chid, chid, NUM_TRYTES_ADDRESS)) {
354+
if (mam_api_channel_create(api, channel_info->ch_mss_depth, chid) != RC_OK) {
355+
ret = SC_MAM_FAILED_CREATE_OR_GET_ID;
356+
ta_log_error("%s\n", ta_error_to_string(ret));
357+
goto done;
358+
}
359+
360+
if (cnt++ > 100) {
361+
ret = SC_MAM_EXCEEDED_CHID_ITER;
362+
ta_log_error("%s\n", ta_error_to_string(ret));
363+
goto done;
364+
}
365+
}
366+
367+
end_find_starting_chid:
368+
ta_log_debug("%s\n", "Finish finding starting chid");
369+
}
370+
371+
// If a starting chid is provided, then we don't need to create a new chid in the first iteration of the following
372+
// loop.
373+
// FIXME: We should figure out a way to avoid passing 'first_iter_with_given_chid' to
374+
// 'create_channel_fetch_all_transactions()'
375+
bool first_iter_with_given_chid = (bool)channel_info->chid;
305376
for (;;) {
306377
hash_array_free(tag_array);
307378
tag_array = hash81_array_new();
308379

309-
ret = create_channel_fetch_all_transactions(service, api, channel_depth, chid, tag_array);
380+
ret = create_channel_fetch_all_transactions(service, api, channel_info->ch_mss_depth, first_iter_with_given_chid,
381+
chid, tag_array);
310382
if (ret) {
311383
ta_log_error("%s\n", ta_error_to_string(ret));
312384
goto done;
313385
}
386+
first_iter_with_given_chid = false;
314387

315388
/*
316389
* Three cases should be considered:
@@ -320,7 +393,7 @@ static status_t ta_mam_written_msg_to_bundle(const iota_client_service_t *const
320393
*/
321394

322395
// Calculate the smallest available msg_ord
323-
const int ch_leaf_num = 1 << channel_depth;
396+
const int ch_leaf_num = 1 << channel_info->ch_mss_depth;
324397
int ch_remain_key_num = ch_leaf_num;
325398
int used_key_num = hash_array_len(tag_array);
326399
do {
@@ -447,11 +520,6 @@ static status_t ta_mam_api_bundle_read(mam_api_t *const api, bundle_transactions
447520
* External functions
448521
***********************************************************************************************************/
449522

450-
void bundle_transactions_renew(bundle_transactions_t **bundle) {
451-
bundle_transactions_free(bundle);
452-
bundle_transactions_new(bundle);
453-
}
454-
455523
status_t ta_send_mam_message(const ta_config_t *const info, const iota_config_t *const iconf,
456524
const iota_client_service_t *const service, ta_send_mam_req_t const *const req,
457525
ta_send_mam_res_t *const res) {
@@ -473,10 +541,10 @@ status_t ta_send_mam_message(const ta_config_t *const info, const iota_config_t
473541
mam_send_operation_t mam_operation;
474542
while (!msg_sent) {
475543
bundle_transactions_renew(&bundle);
544+
struct channel_info_s channel_info = {.ch_mss_depth = data->ch_mss_depth, .chid = data->chid};
476545

477-
// Create channel merkle tree and find the smallest unused secret key.
478546
// Write both Header and Packet into one single bundle.
479-
ret = ta_mam_written_msg_to_bundle(service, &mam, data->ch_mss_depth, mam_key, data->message, &bundle, chid, msg_id,
547+
ret = ta_mam_written_msg_to_bundle(service, &mam, &channel_info, mam_key, data->message, &bundle, chid, msg_id,
480548
&mam_operation);
481549
if (ret == SC_OK) {
482550
msg_sent = true;

accelerator/core/mam_core.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,6 @@ typedef struct mam_encrypt_key_s {
4646
mam_ntru_pk_t_set_t ntru_pks;
4747
} mam_encrypt_key_t;
4848

49-
/**
50-
* @brief Renew the given bundle
51-
*
52-
* @param bundle[in,out] The bundle that will be renewed
53-
*
54-
*/
55-
void bundle_transactions_renew(bundle_transactions_t** bundle);
56-
5749
/**
5850
* @brief Send a MAM message.
5951
*

accelerator/core/request/ta_send_mam.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ status_t send_mam_req_v1_init(ta_send_mam_req_t* req) {
3636

3737
send_mam_data_mam_v1_t* data = req->data;
3838
data->seed = NULL;
39+
data->chid = NULL;
3940
data->message = NULL;
4041
data->ch_mss_depth = 6;
4142

@@ -50,6 +51,7 @@ static void send_mam_req_v1_free(ta_send_mam_req_t** req) {
5051
if ((*req)->data) {
5152
send_mam_data_mam_v1_t* data = (*req)->data;
5253
free(data->seed);
54+
free(data->chid);
5355
free(data->message);
5456
free((*req)->data);
5557
(*req)->data = NULL;

accelerator/core/request/ta_send_mam.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ typedef struct send_mam_data_mam_v1_s {
4545
tryte_t* seed;
4646
/** Optional. The depth of channel merkle tree. */
4747
int32_t ch_mss_depth;
48+
/** Optional. The Channel ID which tangle-accelerator starts to search available message slot. */
49+
tryte_t* chid;
4850
/** Required. The message will be append to the channel. */
4951
char* message;
5052
} send_mam_data_mam_v1_t;

accelerator/core/serializer/serializer.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ static status_t send_mam_message_mam_v1_req_deserialize(cJSON const* const json_
808808
if (json_value != NULL) {
809809
size_t seed_size = strnlen(json_value->valuestring, NUM_TRYTES_ADDRESS);
810810

811-
if (seed_size != NUM_TRYTES_HASH) {
811+
if (seed_size != NUM_TRYTES_ADDRESS) {
812812
ret = SC_SERIALIZER_INVALID_REQ;
813813
ta_log_error("%s\n", ta_error_to_string(ret));
814814
goto done;
@@ -817,6 +817,21 @@ static status_t send_mam_message_mam_v1_req_deserialize(cJSON const* const json_
817817
snprintf((char*)data->seed, seed_size + 1, "%s", json_value->valuestring);
818818
}
819819

820+
if (cJSON_HasObjectItem(json_key, "chid")) {
821+
json_value = cJSON_GetObjectItemCaseSensitive(json_key, "chid");
822+
if (json_value != NULL) {
823+
size_t chid_size = strnlen(json_value->valuestring, NUM_TRYTES_ADDRESS);
824+
825+
if (chid_size != NUM_TRYTES_ADDRESS) {
826+
ret = SC_SERIALIZER_INVALID_REQ;
827+
ta_log_error("%s\n", ta_error_to_string(ret));
828+
goto done;
829+
}
830+
data->chid = (tryte_t*)malloc(sizeof(tryte_t) * (NUM_TRYTES_ADDRESS + 1));
831+
snprintf((char*)data->chid, chid_size + 1, "%s", json_value->valuestring);
832+
}
833+
}
834+
820835
json_value = cJSON_GetObjectItemCaseSensitive(json_key, "message");
821836
if (json_value != NULL) {
822837
size_t payload_size = strlen(json_value->valuestring);

common/ta_errors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ typedef enum {
156156
/**< Can't find message in the assign bundle */
157157
SC_MAM_INVAID_CHID_OR_EPID = 0x0E | SC_MODULE_MAM | SC_SEVERITY_FATAL,
158158
/**< Failed to add trusted channel ID or endpoint ID */
159+
SC_MAM_EXCEEDED_CHID_ITER = 0x0F | SC_MODULE_MAM | SC_SEVERITY_FATAL,
160+
/**< Too much iteration for finding a starting chid */
159161

160162
// response module
161163
SC_RES_NULL = 0x01 | SC_MODULE_RES | SC_SEVERITY_FATAL,

tests/api/mam_test.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,50 @@ void test_write_until_next_channel(void) {
136136
}
137137
}
138138

139+
void test_write_with_chid(void) {
140+
const int beginning_msg_num = 1;
141+
char seed[NUM_TRYTES_ADDRESS + 1] = {};
142+
const char* json_template_send =
143+
"{\"x-api-key\":\"" TEST_TOKEN
144+
"\",\"data\":{\"seed\":\"%s\",\"ch_mss_depth\":1,\"message\":\"%s:%d\"}, \"protocol\":\"MAM_V1\"}";
145+
const char payload[] = "This is test payload number";
146+
const int len = strlen(json_template_send) + NUM_TRYTES_ADDRESS + strlen(payload) + 2;
147+
gen_rand_trytes(NUM_TRYTES_ADDRESS, (tryte_t*)seed);
148+
double sum = 0;
149+
ta_send_mam_res_t* res;
150+
test_time_start(&start_time);
151+
char* json_result = NULL;
152+
for (int i = 0; i < beginning_msg_num; ++i) {
153+
res = send_mam_res_new();
154+
char* json = (char*)malloc(sizeof(char) * len);
155+
snprintf(json, len, json_template_send, seed, payload, i);
156+
TEST_ASSERT_EQUAL_INT32(
157+
SC_OK, api_send_mam_message(&ta_core.ta_conf, &ta_core.iota_conf, &ta_core.iota_service, json, &json_result));
158+
free(json);
159+
if (i != beginning_msg_num - 1) {
160+
free(json_result);
161+
send_mam_res_free(&res);
162+
}
163+
}
164+
send_mam_res_deserialize(json_result, res);
165+
free(json_result);
166+
167+
// Send message from next channel ID
168+
const char* json_template_send_chid =
169+
"{\"x-api-key\":\"" TEST_TOKEN
170+
"\",\"data\":{\"seed\":\"%s\",\"chid\":\"%s\",\"ch_mss_depth\":2,\"message\":\"%s\"}, \"protocol\":\"MAM_V1\"}";
171+
const int len_send_chid = strlen(json_template_send_chid) + NUM_TRYTES_ADDRESS * 2 + strlen(payload);
172+
char* json_send_chid = (char*)malloc(sizeof(char) * (len_send_chid + 1));
173+
snprintf(json_send_chid, len_send_chid, json_template_send_chid, seed, res->chid1, payload);
174+
TEST_ASSERT_EQUAL_INT32(SC_OK, api_send_mam_message(&ta_core.ta_conf, &ta_core.iota_conf, &ta_core.iota_service,
175+
json_send_chid, &json_result));
176+
test_time_end(&start_time, &end_time, &sum);
177+
send_mam_res_free(&res);
178+
free(json_send_chid);
179+
free(json_result);
180+
printf("Elapsed time of write_with_chid: %lf\n", sum);
181+
}
182+
139183
int main(int argc, char* argv[]) {
140184
UNITY_BEGIN();
141185
rand_trytes_init();
@@ -158,6 +202,7 @@ int main(int argc, char* argv[]) {
158202
RUN_TEST(test_send_mam_message);
159203
RUN_TEST(test_receive_mam_message);
160204
RUN_TEST(test_write_until_next_channel);
205+
RUN_TEST(test_write_with_chid);
161206
ta_core_destroy(&ta_core);
162207
return UNITY_END();
163208
}

tests/unit-test/test_serializer.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,8 @@ void test_recv_mam_message_response_deserialize(void) {
361361

362362
void test_send_mam_message_request_deserialize(void) {
363363
const char* json =
364-
"{\"x-api-key\":\"" TEST_TOKEN "\",\"data\":{\"seed\":\"" TRYTES_81_1 "\",\"message\":\"" TEST_PAYLOAD
365-
"\",\"ch_mss_depth\":" STR(TEST_CH_DEPTH) ",\"ep_mss_depth\":" STR(
364+
"{\"x-api-key\":\"" TEST_TOKEN "\",\"data\":{\"seed\":\"" TRYTES_81_1 "\",\"chid\":\"" TEST_ADDRESS
365+
"\",\"message\":\"" TEST_PAYLOAD "\",\"ch_mss_depth\":" STR(TEST_CH_DEPTH) ",\"ep_mss_depth\":" STR(
366366
TEST_EP_DEPTH) "},\"key\":{\"ntru\":[\"" TEST_NTRU_PK "\"],\"psk\":[\"" TRYTES_81_2 "\",\"" TRYTES_81_3
367367
"\"]}, \"protocol\":\"MAM_V1\"}";
368368

@@ -371,7 +371,8 @@ void test_send_mam_message_request_deserialize(void) {
371371
send_mam_data_mam_v1_t* data = (send_mam_data_mam_v1_t*)req->data;
372372

373373
TEST_ASSERT_EQUAL_STRING(TEST_TOKEN, req->service_token);
374-
TEST_ASSERT_EQUAL_MEMORY(TRYTES_81_1, data->seed, NUM_TRYTES_HASH);
374+
TEST_ASSERT_EQUAL_STRING(TRYTES_81_1, data->seed);
375+
TEST_ASSERT_EQUAL_STRING(TEST_ADDRESS, data->chid);
375376
TEST_ASSERT_EQUAL_INT(TEST_CH_DEPTH, data->ch_mss_depth);
376377
TEST_ASSERT_EQUAL_STRING(TEST_PAYLOAD, data->message);
377378
TEST_ASSERT_EQUAL_STRING(TEST_NTRU_PK, mamv1_ntru_key_at(req, 0));

utils/bundle_array.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ extern "C" {
2323
* `bundle_transactions_t` objects. It provides an easier way to save and traverse all the bundles.
2424
*/
2525

26+
/**
27+
* @brief Renew the given bundle
28+
*
29+
* @param bundle[in,out] The bundle that will be renewed
30+
*
31+
*/
32+
static inline void bundle_transactions_renew(bundle_transactions_t **bundle) {
33+
bundle_transactions_free(bundle);
34+
bundle_transactions_new(bundle);
35+
}
36+
2637
typedef UT_array bundle_array_t;
2738
// We should synchronize this implementation as to the implementation in the IOTA library
2839
static UT_icd bundle_transactions_icd = {sizeof(iota_transaction_t), 0, 0, 0};

0 commit comments

Comments
 (0)