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

Commit

Permalink
feat(api): Impl api_send_trytes()
Browse files Browse the repository at this point in the history
Implement api_send_trytes() which can send raw transaction trytes
array. Call it in the following JSON format
{"trytes":["TRYTES_2673_1","TRYTES_2673_2"]}
  • Loading branch information
howjmay committed May 18, 2019
1 parent ca2add6 commit 301befd
Show file tree
Hide file tree
Showing 11 changed files with 354 additions and 23 deletions.
3 changes: 3 additions & 0 deletions accelerator/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ cc_library(
"//request",
"//response",
"@com_github_uthash//:uthash",
"@entangled//cclient/api",
"@entangled//cclient/serialization:serializer",
"@entangled//cclient/serialization:serializer_json",
"@entangled//common/model:bundle",
"@entangled//utils:time",
],
Expand Down
28 changes: 28 additions & 0 deletions accelerator/apis.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,31 @@ status_t api_send_transfer(const iota_config_t* const tangle,
ta_get_transaction_object_res_free(&txn_obj_res);
return ret;
}

status_t api_send_trytes(const iota_config_t* const tangle,
const iota_client_service_t* const service,
const char* const obj, char** json_result) {
status_t ret = SC_OK;
hash8019_array_p trytes = hash8019_array_new();

if (!trytes) {
ret = SC_TA_OOM;
goto done;
}

ret = ta_send_trytes_req_deserialize(obj, trytes);
if (ret != SC_OK) {
goto done;
}

ret = ta_send_trytes(tangle, service, trytes);
if (ret != SC_OK) {
goto done;
}

ret = ta_send_trytes_res_serialize(trytes, json_result);

done:
hash_array_free(trytes);
return ret;
}
21 changes: 21 additions & 0 deletions accelerator/apis.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,27 @@ status_t api_find_transactions_obj_by_tag(
const iota_client_service_t* const service, const char* const obj,
char** json_result);

/**
* @brief Attach trytes to Tangle and return transaction hashes
*
* Persist trytes locally before sending to network.
* This allows for reattachments and prevents key reuse if trytes can't
* be recovered by querying the network after broadcasting.
*
* @param[in] tangle IOTA API parameter configurations
* @param[in] service IRI node end point service
* @param[in] obj trytes to attach, store and broadcast in json array
* @param[out] json_result Result containing list of attached transaction hashes
* in json format
*
* @return
* - SC_OK on success
* - non-zero on error
*/
status_t api_send_trytes(const iota_config_t* const tangle,
const iota_client_service_t* const service,
const char* const obj, char** json_result);

#ifdef __cplusplus
}
#endif
Expand Down
50 changes: 27 additions & 23 deletions accelerator/common_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,47 +160,51 @@ status_t ta_send_trytes(const iota_config_t* const tangle,
const iota_client_service_t* const service,
hash8019_array_p trytes) {
status_t ret = SC_OK;
ta_get_tips_res_t* get_txn_res = ta_get_tips_res_new();
get_transactions_to_approve_req_t* tx_approve_req =
get_transactions_to_approve_req_new();
get_transactions_to_approve_res_t* tx_approve_res =
get_transactions_to_approve_res_new();
attach_to_tangle_req_t* attach_req = attach_to_tangle_req_new();
attach_to_tangle_res_t* attach_res = attach_to_tangle_res_new();
if (!get_txn_res || !attach_req || !attach_res) {
if (!tx_approve_req || !tx_approve_res || !attach_req || !attach_res) {
ret = SC_CCLIENT_OOM;
goto done;
}

// get transaction to approve
ret =
cclient_get_txn_to_approve(service, tangle->milestone_depth, get_txn_res);
if (ret) {
get_transactions_to_approve_req_set_depth(tx_approve_req,
tangle->milestone_depth);
if (iota_client_get_transactions_to_approve(service, tx_approve_req,
tx_approve_res)) {
ret = SC_CCLIENT_FAILED_RESPONSE;
goto done;
}

// attach to tangle
memcpy(attach_req->trunk, hash243_stack_peek(get_txn_res->tips),
FLEX_TRIT_SIZE_243);
hash243_stack_pop(&get_txn_res->tips);
memcpy(attach_req->branch, hash243_stack_peek(get_txn_res->tips),
FLEX_TRIT_SIZE_243);
hash243_stack_pop(&get_txn_res->tips);
attach_req->mwm = tangle->mwm;

// copy trytes to attach_req->trytes
flex_trit_t* elt = NULL;
HASH_ARRAY_FOREACH(trytes, elt) { hash_array_push(attach_req->trytes, elt); }

ret = ta_attach_to_tangle(attach_req, attach_res);
if (ret) {
HASH_ARRAY_FOREACH(trytes, elt) {
attach_to_tangle_req_trytes_add(attach_req, elt);
}
attach_to_tangle_req_init(
attach_req, get_transactions_to_approve_res_trunk(tx_approve_res),
get_transactions_to_approve_res_branch(tx_approve_res), tangle->mwm);
if (ta_attach_to_tangle(attach_req, attach_res) != SC_OK) {
goto done;
}

// store and broadcast
ret = iota_client_store_and_broadcast(service,
(store_transactions_req_t*)attach_res);
if (ret) {
if (iota_client_store_and_broadcast(
service, (store_transactions_req_t*)attach_res) != RC_OK) {
ret = SC_CCLIENT_FAILED_RESPONSE;
goto done;
}

// set the value of attach_res->trytes as output trytes result
memcpy(trytes, attach_res->trytes,
hash_array_len(attach_res->trytes) * sizeof(hash8019_array_p));

done:
ta_get_tips_res_free(&get_txn_res);
get_transactions_to_approve_req_free(&tx_approve_req);
get_transactions_to_approve_res_free(&tx_approve_res);
attach_to_tangle_req_free(&attach_req);
attach_to_tangle_res_free(&attach_res);
return ret;
Expand Down
4 changes: 4 additions & 0 deletions accelerator/errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ typedef enum {
/**< JSON key not found */
SC_CCLIENT_JSON_PARSE = 0x07 | SC_MODULE_CCLIENT | SC_SEVERITY_MAJOR,
/**< json parsing error, might the wrong format */
SC_CCLIENT_FLEX_TRITS = 0x09 | SC_MODULE_CCLIENT | SC_SEVERITY_MAJOR,
/**< Flex trits converting error */
SC_CCLIENT_JSON_CREATE = 0x0A | SC_MODULE_CCLIENT | SC_SEVERITY_MAJOR,
/**< json create object error, might OOM. */

// Serializer module
SC_SERIALIZER_JSON_CREATE = 0x01 | SC_MODULE_SERIALIZER | SC_SEVERITY_FATAL,
Expand Down
102 changes: 102 additions & 0 deletions serializer/serializer.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,61 @@ static status_t ta_hash243_queue_to_json_array(hash243_queue_t queue,
return SC_OK;
}

static status_t ta_json_array_to_hash8019_array(cJSON const* const obj,
char const* const obj_name,
hash8019_array_p array) {
status_t ret = SC_OK;
flex_trit_t hash[FLEX_TRIT_SIZE_8019] = {};
cJSON* json_item = cJSON_GetObjectItemCaseSensitive(obj, obj_name);
if (!cJSON_IsArray(json_item)) {
return SC_CCLIENT_JSON_PARSE;
}

cJSON* current_obj = NULL;
cJSON_ArrayForEach(current_obj, json_item) {
if (current_obj->valuestring != NULL) {
flex_trits_from_trytes(hash, NUM_TRITS_SERIALIZED_TRANSACTION,
(tryte_t const*)current_obj->valuestring,
NUM_TRYTES_SERIALIZED_TRANSACTION,
NUM_TRYTES_SERIALIZED_TRANSACTION);
hash_array_push(array, hash);
}
}
return ret;
}

status_t ta_hash8019_array_to_json_array(hash8019_array_p array,
cJSON* const json_root,
char const* const obj_name) {
size_t array_count = 0;
cJSON* array_obj = NULL;
tryte_t trytes_out[NUM_TRYTES_SERIALIZED_TRANSACTION + 1] = {};
size_t trits_count = 0;
flex_trit_t* elt = NULL;

array_count = hash_array_len(array);
if (array_count > 0) {
array_obj = cJSON_CreateArray();
if (array_obj == NULL) {
return SC_SERIALIZER_JSON_CREATE;
}
cJSON_AddItemToObject(json_root, obj_name, array_obj);

HASH_ARRAY_FOREACH(array, elt) {
trits_count = flex_trits_to_trytes(
trytes_out, NUM_TRYTES_SERIALIZED_TRANSACTION, elt,
NUM_TRITS_SERIALIZED_TRANSACTION, NUM_TRITS_SERIALIZED_TRANSACTION);
trytes_out[NUM_TRYTES_SERIALIZED_TRANSACTION] = '\0';
if (trits_count == 0) {
return SC_CCLIENT_FLEX_TRITS;
}
cJSON_AddItemToArray(array_obj,
cJSON_CreateString((char const*)trytes_out));
}
}
return SC_OK;
}

static status_t ta_json_get_string(cJSON const* const json_obj,
char const* const obj_name,
char* const text) {
Expand Down Expand Up @@ -344,6 +399,53 @@ status_t ta_send_transfer_req_deserialize(const char* const obj,
return ret;
}

status_t ta_send_trytes_req_deserialize(const char* const obj,
hash8019_array_p out_trytes) {
if (obj == NULL || out_trytes == NULL) {
return SC_SERIALIZER_NULL;
}
status_t ret = SC_OK;
cJSON* json_obj = cJSON_Parse(obj);

if (json_obj == NULL) {
ret = SC_SERIALIZER_JSON_PARSE;
goto done;
}

ret = ta_json_array_to_hash8019_array(json_obj, "trytes", out_trytes);
if (ret != SC_OK) {
goto done;
}

done:
cJSON_Delete(json_obj);
return ret;
}

status_t ta_send_trytes_res_serialize(const hash8019_array_p trytes,
char** obj) {
if (trytes == NULL) {
return SC_SERIALIZER_NULL;
}

status_t ret = SC_OK;
cJSON* json_root = cJSON_CreateObject();

ret = ta_hash8019_array_to_json_array(trytes, json_root, "trytes");
if (ret != SC_OK) {
goto done;
}

*obj = cJSON_PrintUnformatted(json_root);
if (*obj == NULL) {
ret = SC_SERIALIZER_JSON_PARSE;
}

done:
cJSON_Delete(json_root);
return ret;
}

status_t ta_get_transaction_object_res_serialize(
char** obj, const ta_get_transaction_object_res_t* const res) {
status_t ret = SC_OK;
Expand Down
27 changes: 27 additions & 0 deletions serializer/serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,33 @@ status_t ta_get_tips_res_serialize(char** obj,
status_t ta_send_transfer_req_deserialize(const char* const obj,
ta_send_transfer_req_t* req);

/**
* @brief Deserialze JSON string to hash8019_array_p
*
* @param[in] obj Input values in JSON
* @param[out] out_trytes trytes arrary in the request data in type of
* hash8019_array_p
*
* @return
* - SC_OK on success
* - non-zero on error
*/
status_t ta_send_trytes_req_deserialize(const char* const obj,
hash8019_array_p out_trytes);

/**
* @brief Serialze hash8019_array_p to JSON string
*
* @param[in] trytes trytes array returned in type of hash8019_array_p
* @param[out] obj output serialized JSON values
*
* @return
* - SC_OK on success
* - non-zero on error
*/
status_t ta_send_trytes_res_serialize(const hash8019_array_p trytes,
char** obj);

/**
* @brief Serialze type of ta_get_transaction_object_res_t to JSON string
*
Expand Down
18 changes: 18 additions & 0 deletions tests/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,23 @@ void test_send_transfer(void) {
printf("Average time of send_transfer: %lf\n", sum / TEST_COUNT);
}

void test_send_trytes(void) {
const char* json =
"{\"trytes\":[\"" TRYTES_2673_1 "\",\"" TRYTES_2673_2 "\"]}";
char* json_result;
double sum = 0;

for (size_t count = 0; count < TEST_COUNT; count++) {
test_time_start(&start_time);
TEST_ASSERT_EQUAL_INT32(
SC_OK,
api_send_trytes(&ta_core.tangle, &ta_core.service, json, &json_result));
test_time_end(&start_time, &end_time, &sum);
free(json_result);
}
printf("Average time of send_trytes: %lf\n", sum / TEST_COUNT);
}

void test_get_transaction_object(void) {
char* json_result;
double sum = 0;
Expand Down Expand Up @@ -212,6 +229,7 @@ int main(void) {
RUN_TEST(test_get_tips_pair);
RUN_TEST(test_get_tips);
RUN_TEST(test_send_transfer);
RUN_TEST(test_send_trytes);
RUN_TEST(test_get_transaction_object);
RUN_TEST(test_find_transactions_by_tag);
RUN_TEST(test_find_transactions_obj_by_tag);
Expand Down
35 changes: 35 additions & 0 deletions tests/test_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,41 @@ TEST(GetBundleTest, RetreiveBundleTest) {
bundle_transactions_free(&bundle);
}

TEST(SendTrytesTest, SendTrytesTest) {
size_t trits_count;
hash8019_array_p trytes = hash8019_array_new();
flex_trit_t tx_trits[FLEX_TRIT_SIZE_8019];
tryte_t trytes_out[NUM_TRYTES_SERIALIZED_TRANSACTION + 1] = {};

flex_trits_from_trytes(
tx_trits, NUM_TRITS_SERIALIZED_TRANSACTION, (const tryte_t*)TRYTES_2673_1,
NUM_TRYTES_SERIALIZED_TRANSACTION, NUM_TRYTES_SERIALIZED_TRANSACTION);
hash_array_push(trytes, tx_trits);

flex_trits_from_trytes(
tx_trits, NUM_TRITS_SERIALIZED_TRANSACTION, (const tryte_t*)TRYTES_2673_2,
NUM_TRYTES_SERIALIZED_TRANSACTION, NUM_TRYTES_SERIALIZED_TRANSACTION);
hash_array_push(trytes, tx_trits);

EXPECT_EQ(ta_send_trytes(&tangle, &service, trytes), SC_OK);

trits_count = flex_trits_to_trytes(
trytes_out, NUM_TRYTES_SERIALIZED_TRANSACTION, hash_array_at(trytes, 0),
NUM_TRITS_SERIALIZED_TRANSACTION, NUM_TRITS_SERIALIZED_TRANSACTION);
trytes_out[NUM_TRYTES_SERIALIZED_TRANSACTION] = '\0';
EXPECT_EQ(NUM_TRITS_SERIALIZED_TRANSACTION, trits_count);
EXPECT_STREQ(TRYTES_2673_1, (char*)trytes_out);

trits_count = flex_trits_to_trytes(
trytes_out, NUM_TRYTES_SERIALIZED_TRANSACTION, hash_array_at(trytes, 1),
NUM_TRITS_SERIALIZED_TRANSACTION, NUM_TRITS_SERIALIZED_TRANSACTION);
trytes_out[NUM_TRYTES_SERIALIZED_TRANSACTION] = '\0';
EXPECT_EQ(NUM_TRITS_SERIALIZED_TRANSACTION, trits_count);
EXPECT_STREQ(TRYTES_2673_2, (char*)trytes_out);

hash_array_free(trytes);
}

int main(int argc, char** argv) {
// GTest manage to cleanup after testing, so only need to initialize here
cache_init(REDIS_HOST, REDIS_PORT);
Expand Down
Loading

0 comments on commit 301befd

Please sign in to comment.