diff --git a/c/include/in3/client.h b/c/include/in3/client.h index 8958315c4..f1f59c21c 100644 --- a/c/include/in3/client.h +++ b/c/include/in3/client.h @@ -716,6 +716,15 @@ char* in3_configure( const char* config /**< JSON-string with the configuration to set. */ ); +/** + * gets the current config as json. + * + * For details about the structure of ther config see https://in3.readthedocs.io/en/develop/api-ts.html#type-in3config + */ +char* in3_get_config( + in3_t* c /**< the incubed client */ +); + /** * defines a default transport which is used when creating a new client. */ diff --git a/c/src/api/eth1/rpc_api.c b/c/src/api/eth1/rpc_api.c index 0a8b61699..54d865ff3 100644 --- a/c/src/api/eth1/rpc_api.c +++ b/c/src/api/eth1/rpc_api.c @@ -181,6 +181,14 @@ static in3_ret_t in3_config(in3_ctx_t* ctx, d_token_t* params, in3_response_t** RESPONSE_END(); return IN3_OK; } +static in3_ret_t in3_getConfig(in3_ctx_t* ctx, in3_response_t** response) { + char* ret = in3_get_config(ctx->client); + RESPONSE_START(); + sb_add_chars(&response[0]->result, ret); + RESPONSE_END(); + _free(ret); + return IN3_OK; +} static in3_ret_t in3_pk2address(in3_ctx_t* ctx, d_token_t* params, in3_response_t** response) { bytes_t* pk = d_get_bytes_at(params, 0); @@ -342,6 +350,7 @@ static in3_ret_t eth_handle_intern(in3_ctx_t* ctx, in3_response_t** response) { if (strcmp(method, "in3_ens") == 0) return in3_ens(ctx, params, response); if (strcmp(method, "web3_sha3") == 0) return in3_sha3(ctx, params, response); if (strcmp(method, "in3_config") == 0) return in3_config(ctx, params, response); + if (strcmp(method, "in3_getConfig") == 0) return in3_getConfig(ctx, response); if (strcmp(method, "in3_pk2address") == 0) return in3_pk2address(ctx, params, response); if (strcmp(method, "in3_pk2public") == 0) return in3_pk2address(ctx, params, response); if (strcmp(method, "in3_ecrecover") == 0) return in3_ecrecover(ctx, params, response); @@ -362,6 +371,7 @@ static int verify(in3_vctx_t* v) { strcmp(method, "web3_sha3") == 0 || strcmp(method, "in3_ens") == 0 || strcmp(method, "in3_config") == 0 || + strcmp(method, "in3_getConfig") == 0 || strcmp(method, "in3_pk2address") == 0 || strcmp(method, "in3_ecrecover") == 0 || strcmp(method, "in3_signData") == 0 || diff --git a/c/src/core/client/client.h b/c/src/core/client/client.h index a108ff4fc..441f1f564 100644 --- a/c/src/core/client/client.h +++ b/c/src/core/client/client.h @@ -716,6 +716,15 @@ char* in3_configure( const char* config /**< JSON-string with the configuration to set. */ ); +/** + * gets the current config as json. + * + * For details about the structure of ther config see https://in3.readthedocs.io/en/develop/api-ts.html#type-in3config + */ +char* in3_get_config( + in3_t* c /**< the incubed client */ +); + /** * defines a default transport which is used when creating a new client. */ diff --git a/c/src/core/client/client_init.c b/c/src/core/client/client_init.c index b686496e5..044e7cdc3 100644 --- a/c/src/core/client/client_init.c +++ b/c/src/core/client/client_init.c @@ -532,6 +532,101 @@ static inline bool is_hex_str(const char* str) { return str[strspn(str, "0123456789abcdefABCDEF")] == 0; } +static void add_prop(sb_t* sb, char prefix, const char* property) { + sb_add_char(sb, prefix); + sb_add_char(sb, '"'); + sb_add_chars(sb, property); + sb_add_chars(sb, "\":"); +} +static void add_bool(sb_t* sb, char prefix, const char* property, bool value) { + add_prop(sb, prefix, property); + sb_add_chars(sb, value ? "true" : "false"); +} +static void add_string(sb_t* sb, char prefix, const char* property, const char* value) { + add_prop(sb, prefix, property); + sb_add_char(sb, '"'); + sb_add_chars(sb, value); + sb_add_char(sb, '"'); +} + +static void add_uint(sb_t* sb, char prefix, const char* property, uint64_t value) { + add_prop(sb, prefix, property); + char tmp[16]; + sprintf(tmp, "%u", (uint32_t) value); + sb_add_chars(sb, tmp); +} + +static void add_hex(sb_t* sb, char prefix, const char* property, bytes_t value) { + add_prop(sb, prefix, property); + sb_add_bytes(sb, NULL, &value, 1, false); +} + +char* in3_get_config(in3_t* c) { + sb_t* sb = sb_new(""); + in3_chain_t* chain = in3_find_chain(c, c->chain_id); + add_bool(sb, '{', "autoUpdateList", c->flags & FLAGS_AUTO_UPDATE_LIST); + add_uint(sb, ',', "chainId", c->chain_id); + add_uint(sb, ',', "signatureCount", c->signature_count); + add_uint(sb, ',', "finality", c->finality); + add_bool(sb, ',', "includeCode", c->flags & FLAGS_INCLUDE_CODE); + add_uint(sb, ',', "maxAttempts", c->max_attempts); + add_bool(sb, ',', "keepIn3", c->flags & FLAGS_KEEP_IN3); + add_bool(sb, ',', "stats", c->flags & FLAGS_STATS); + add_bool(sb, ',', "useBinary", c->flags & FLAGS_BINARY); + add_bool(sb, ',', "useHttp", c->flags & FLAGS_HTTP); + add_uint(sb, ',', "maxBlockCache", c->max_block_cache); + add_uint(sb, ',', "maxCodeCache", c->max_code_cache); + add_uint(sb, ',', "maxVerifiedHashes", c->max_verified_hashes); + add_uint(sb, ',', "timeout", c->timeout); + add_uint(sb, ',', "minDeposit", c->min_deposit); + add_uint(sb, ',', "nodeProps", c->node_props); + add_uint(sb, ',', "nodeLimit", c->node_limit); + add_string(sb, ',', "proof", (c->proof == PROOF_NONE) ? "none" : (c->proof == PROOF_STANDARD ? "standard" : "full")); + if (c->key) + add_hex(sb, ',', "key", bytes(c->key, 32)); + if (c->replace_latest_block) + add_uint(sb, ',', "replaceLatestBlock", c->replace_latest_block); + add_uint(sb, ',', "requestCount", c->request_count); + if (c->chain_id == ETH_CHAIN_ID_LOCAL) + add_string(sb, ',', "rpc", chain->nodelist->url); + + sb_add_chars(sb, ",\"nodes\":{"); + for (int i = 0; i < c->chains_length; i++) { + chain = c->chains + i; + if (i) sb_add_char(sb, ','); + sb_add_char(sb, '"'); + sb_add_hexuint(sb, chain->chain_id); + sb_add_chars(sb, "\":"); + add_hex(sb, '{', "contract", *chain->contract); + if (chain->whitelist) + add_hex(sb, ',', "whiteListContract", bytes(chain->whitelist->contract, 20)); + add_hex(sb, ',', "registryId", bytes(chain->registry_id, 32)); + add_bool(sb, ',', "needsUpdate", chain->nodelist_upd8_params != NULL); + add_uint(sb, ',', "avgBlockTime", chain->avg_block_time); + sb_add_chars(sb, ",\"nodeList\":["); + for (int j = 0; j < chain->nodelist_length; j++) { + if ((chain->nodelist[j].attrs & ATTR_BOOT_NODE) == 0) continue; + if (sb->data[sb->len - 1] != '[') sb_add_char(sb, ','); + add_string(sb, '{', "url", chain->nodelist[j].url); + add_uint(sb, ',', "props", chain->nodelist[j].props); + add_hex(sb, ',', "address", *(chain->nodelist[j].address)); + sb_add_char(sb, '}'); + } + if (sb->data[sb->len - 1] == '[') { + sb->len -= 13; + sb_add_char(sb, '}'); + } else + sb_add_chars(sb, "]}"); + } + sb_add_chars(sb, "}}"); + + // TODO pay + + char* r = sb->data; + _free(sb); + return r; +} + char* in3_configure(in3_t* c, const char* config) { d_track_keynames(1); d_clear_keynames(); diff --git a/c/src/core/client/execute.c b/c/src/core/client/execute.c index 42e09150d..739c4f730 100644 --- a/c/src/core/client/execute.c +++ b/c/src/core/client/execute.c @@ -465,7 +465,7 @@ static in3_ret_t find_valid_result(in3_ctx_t* ctx, int nodes_count, in3_response } // check auto update opts only if this node wasn't blacklisted (due to wrong result/proof) - if (!is_blacklisted(node) && d_get(ctx->responses[0], K_IN3)) + if (!is_blacklisted(node) && ctx->responses && d_get(ctx->responses[0], K_IN3)) check_autoupdate(ctx, chain, d_get(ctx->responses[0], K_IN3), node); // !node_weight is valid, because it means this is a internaly handled response diff --git a/c/src/core/util/data.c b/c/src/core/util/data.c index 718ba2762..f3c7574d1 100644 --- a/c/src/core/util/data.c +++ b/c/src/core/util/data.c @@ -307,7 +307,8 @@ bool d_eq(const d_token_t* a, const d_token_t* b) { } return true; } - return (a->data && b->data) + if (d_type(a) == T_STRING) return strcmp((char*) a->data, (char*) b->data) == 0; + return (a->data && b->data && d_type(a) == T_BYTES) ? b_cmp(d_bytes(a), d_bytes(b)) : a->data == NULL && b->data == NULL; } diff --git a/c/test/unit_tests/test_config.c b/c/test/unit_tests/test_config.c new file mode 100644 index 000000000..4e8c77a9c --- /dev/null +++ b/c/test/unit_tests/test_config.c @@ -0,0 +1,76 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/slockit/in3-c + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ + +#ifndef TEST +#define TEST +#endif +#ifndef TEST +#define DEBUG +#endif + +#include "../../src/api/eth1/eth_api.h" +#include "../../src/core/client/cache.h" +#include "../../src/core/client/context.h" +#include "../../src/core/client/nodelist.h" +#include "../../src/core/util/data.h" +#include "../../src/core/util/debug.h" +#include "../../src/core/util/utils.h" +#include "../../src/verifier/eth1/nano/eth_nano.h" +#include "../test_utils.h" +#include +#include + +void test_get_config() { + in3_register_eth_nano(); + in3_register_eth_api(); + in3_t* c = in3_for_chain(ETH_CHAIN_ID_KOVAN); + char * result = NULL, *error = NULL; + in3_client_rpc(c, "in3_getConfig", "[]", &result, &error); + if (error) printf("ERROR: %s\n", error); + TEST_ASSERT_NULL(error); + TEST_ASSERT_EQUAL_STRING("{\"autoUpdateList\":true,\"chainId\":42,\"signatureCount\":0,\"finality\":0,\"includeCode\":false,\"maxAttempts\":7,\"keepIn3\":false,\"stats\":true,\"useBinary\":false,\"useHttp\":false,\"maxBlockCache\":0,\"maxCodeCache\":0,\"maxVerifiedHashes\":5,\"timeout\":10000,\"minDeposit\":0,\"nodeProps\":0,\"nodeLimit\":0,\"proof\":\"standard\",\"requestCount\":1,\"nodes\":{\"0x2a\":{\"contract\":\"0x4c396dcf50ac396e5fdea18163251699b5fcca25\",\"registryId\":\"0x92eb6ad5ed9068a24c1c85276cd7eb11eda1e8c50b17fbaffaf3e8396df4becf\",\"needsUpdate\":true,\"avgBlockTime\":6}}}", result); + _free(result); + in3_free(c); +} + +/* + * Main + */ +int main() { + dbg_log("starting cor tests"); + + TESTS_BEGIN(); + RUN_TEST(test_get_config); + return TESTS_END(); +}