diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index cf87dd8..62b0585 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -5,5 +5,18 @@ find_package(Threads) add_executable(name name.cpp) target_link_libraries(name abieos ${CMAKE_THREAD_LIBS_INIT}) +add_library(abieos_util STATIC ../src/abieos.cpp ../src/abi.cpp ../src/crypto.cpp ../include/eosio/fpconv.c) +target_include_directories(abieos_util PUBLIC + "$/../src" + "$/../include" + "$/../external/rapidjson/include" + "$") + +add_executable(generate_hex_from_json util_generate_hex_from_json.cpp) +target_link_libraries(generate_hex_from_json abieos_util ${CMAKE_THREAD_LIBS_INIT}) + +add_executable(generate_json_from_hex util_generate_json_from_hex.cpp) +target_link_libraries(generate_json_from_hex abieos_util ${CMAKE_THREAD_LIBS_INIT}) + add_custom_command( TARGET name POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $ ${CMAKE_CURRENT_BINARY_DIR}/name2num ) add_custom_command( TARGET name POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $ ${CMAKE_CURRENT_BINARY_DIR}/num2name ) diff --git a/tools/test_utils.sh b/tools/test_utils.sh new file mode 100755 index 0000000..4c22df3 --- /dev/null +++ b/tools/test_utils.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +Transaction_ABI="../external/eosjs/src/transaction.abi.json" + +###################################### +PREFIX="Hex from JSON:" +if [ -f ../build/tools/generate_hex_from_json ]; then + # no args test ############### + if ../build/tools/generate_hex_from_json 2>>/dev/null ; then + echo "error: no arguments and still success ../build/tools/generate_hex_from_json" + exit 1 + fi + echo "${PREFIX} Passed No Args Test" + + # Bool test ################### + true_byte=$(../build/tools/generate_hex_from_json -f ${Transaction_ABI} -x bool -j true) + if [ "${true_byte}" != "01" ] ; then + echo "error: ${PREFIX} expected byte 01 from bool true but got ${true_byte}" + exit 1 + fi + echo "${PREFIX} Passed Bool Test" + + # Int test #################### + two_byte=$(../build/tools/generate_hex_from_json -f ${Transaction_ABI} -x int8 -j 2) + if [ "${two_byte}" != "02" ] ; then + echo "error: ${PREFIX} expected byte 02 from 2 int8 but got ${two_byte}" + exit 1 + fi + echo "${PREFIX} Passed Int8 as 2 Test" + # Int test #################### + neg_two_byte=$(../build/tools/generate_hex_from_json -f ${Transaction_ABI} -x int8 -j -2) + if [ "${neg_two_byte}" != "FE" ] ; then + echo "error: ${PREFIX} expected byte FE from -2 int8 but got ${neg_two_byte}" + exit 1 + fi + echo "${PREFIX} Passed Int8 as -2 Test" + # name test ################### + name_byte=$(../build/tools/generate_hex_from_json -f ${Transaction_ABI} -x name -j '"bigliontest1"') + if [ "${name_byte}" != "103256795217993B" ] ; then + echo "error: ${PREFIX} expected byte 103256795217993B from name but got ${neg_two_byte}" + exit 1 + fi + echo "${PREFIX} Passed Name Test" +else + echo "Not Found ../build/tools/generate_hex_from_json" + exit 1 +fi + + +################################################### + + +PREFIX="JSON from HEX:" +if [ -f ../build/tools/generate_json_from_hex ]; then + # no args test ############### + if ../build/tools/generate_json_from_hex 2>>/dev/null ; then + echo "error: no arguments and still success ../build/tools/generate_json_from_hex" + exit 1 + fi + echo "${PREFIX} Passed No Args Test" + + # Bool test ##################### + true_byte=$(../build/tools/generate_json_from_hex -f ${Transaction_ABI} -x bool -h 01) + if [ "${true_byte}" != "true" ] ; then + echo "error: ${PREFIX} expected true from bool 01 but got ${true_byte}" + exit 1 + fi + echo "${PREFIX} Passed Bool Test" + + # Int test ##################### + two_byte=$(../build/tools/generate_json_from_hex -f ${Transaction_ABI} -x int8 -h 02) + if [ "${two_byte}" != "2" ] ; then + echo "error: ${PREFIX} expected 2 from 02 int8 but got ${two_byte}" + exit 1 + fi + echo "${PREFIX} Passed Int8 as 02 Test" + # Int test ##################### + neg_two_byte=$(../build/tools/generate_json_from_hex -f ${Transaction_ABI} -x int8 -h FE) + if [ "${neg_two_byte}" != "-2" ] ; then + echo "error: ${PREFIX} expected -2 from FE int8 but got ${neg_two_byte}" + exit 1 + fi + echo "${PREFIX} Passed Int8 as FE Test" + # name test ##################### + name_byte=$(../build/tools/generate_json_from_hex -f ${Transaction_ABI} -x name -h 103256795217993B) + if [ "${name_byte}" != '"bigliontest1"' ] ; then + echo "error: ${PREFIX} expected \"bigliontest1\" from name but got ${neg_two_byte}" + exit 1 + fi + echo "${PREFIX} Passed Name Test" +else + echo "Not Found ../build/tools/generate_json_from_hex" + exit 1 +fi + +echo "Success all tests passed" diff --git a/tools/util_generate_hex_from_json.cpp b/tools/util_generate_hex_from_json.cpp new file mode 100644 index 0000000..bf5c116 --- /dev/null +++ b/tools/util_generate_hex_from_json.cpp @@ -0,0 +1,147 @@ +// +// Purpose: command line option to generate hex code from JSON +// may be used to generate serialization tests cases in other languages and external packages +// + +#include "abieos.h" +#include +#include +#include +#include +#include +#include + +// Main work done here four steps +// 1) create empty context +// 2) set the context +// 3) parse json to binary +// 4) create hex from bin +// abiContractName: enum with contract names +// schema: the name of the data type or reference to schema in the ABI contract +// json: the name and values +// verbose: flag to print out step by step messages +std::string generate_hex_from_json(const char* abi_definition, const char* contract_name, const char* schema, const char* json, bool verbose) { + if (verbose) std::cerr << "Schema is: " << schema << " and json is " << json << std::endl << std::endl; + + // create empty context + using unique_abieos = std::unique_ptr; + unique_abieos context( abieos_create(), &abieos_destroy ); + if (! context) throw std::runtime_error("unable to create context"); + if (verbose) std::cerr << "step 1 of 4: created empty ABI context" << std::endl; + + // set the transaction context. + // first get the contract_id + uint64_t contract_id = abieos_string_to_name(context.get(), contract_name); + if (contract_id == 0) { + std::cerr << "Error: abieos_string_to_name " << abieos_get_error(context.get()) << std::endl; + throw std::runtime_error("unable to set context"); + } + // use our id and set the ABI + bool successSettingAbi = abieos_set_abi(context.get(), contract_id, abi_definition); + if (! successSettingAbi) { + std::cerr << "Error: abieos_set_abi " << abieos_get_error(context.get()) << std::endl; + throw std::runtime_error("unable to set context"); + } + if (verbose) std::cerr << "step 2 of 4: established context for transactions, packed transactions, and state history" << std::endl; + + // convert from json to binary. binary stored with context + // get contract id returns integer for the ABI contract we passed in by name + bool successJsonToBin = abieos_json_to_bin_reorderable( + context.get(), + contract_id, + schema, + json + ); + if (!successJsonToBin) { + std::cerr << "failed in step 3: using context " << contract_name << std::endl; + throw std::runtime_error(abieos_get_error(context.get())); + } + if (verbose) std::cerr << "step 3 of 4: completed parsing json to binary" << std::endl; + + // now time to return the hex string + std::string hex = abieos_get_bin_hex(context.get()); + if (verbose) std::cerr << "step 4 of 4: converted binary to hex" << std::endl << std::endl; + return hex; +} + +// prints usage +void help(const char* exec_name) { + std::cerr << "Usage " << exec_name << ": -f -j JSON -x type [-v]\n"; + std::cerr << "\t-f file with ABI definition\n"; + std::cerr << "\t-v verbose, print out steps\n"; + std::cerr << "\t-j json: string to convert to hex\n"; + std::cerr << "\t-x type: a specific data type or schema section (example uint16, action, name, uint8[])\n"; + std::cerr << "\texample: generate_hex_from_json -f ./transaction.abi -x bool -j true\n" << std::endl; +} + +// reads file returning string of contents +std::string retrieveFileContents(const std::string &filename ) { + try { + std::ifstream ifs(filename, std::ios::in); + std::string string_of_file_contents((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + return string_of_file_contents; + } catch(std::filesystem::filesystem_error const& ex) { + std::cerr << "unable to read ABI file at path: " << filename << std::endl; + exit(EXIT_FAILURE); + } +} + +int main(int argc, char* argv[]) { + // static string for our contract id + static const char* contract_name = "eosio"; + // input string to transform to hex code + std::string json; + // schema name ex: bool + std::string type; + // hex value returned ex: 01 + std::string hex; + // file containing ABI definition + std::string abiFileName; + // string with contents of ABI file + std::string abiDefinition; + // default verbose setting + bool verbose = false; + int opt; + + try { + while ((opt = getopt(argc, argv, "vf:j:x:")) != -1) { + switch (opt) { + case 'f': abiFileName = optarg; break; + case 'v': verbose = true; break; + case 'j': json = optarg; break; + case 'x': type = optarg; break; + case '?': + exit(EXIT_FAILURE); + default: + exit(EXIT_FAILURE); + } + } + + // check for required params + if (json.empty() || type.empty() || abiFileName.empty()) { + help(*argv); + exit(EXIT_FAILURE); + } + + // this is an important step + // here we grab the ABI definition and stuff it in a string + abiDefinition = retrieveFileContents(abiFileName); + + hex = generate_hex_from_json( + abiDefinition.c_str(), + contract_name, + type.c_str(), + json.c_str(), + verbose + ); + if (hex.length() > 0) { + std::cout << hex << std::endl; + } else { + std::cerr << "returned empty" << std::endl; + } + return 0; + } catch (std::exception& e) { + std::cerr << "Could not compute hex value: " << e.what() << std::endl; + return 1; + } +} diff --git a/tools/util_generate_json_from_hex.cpp b/tools/util_generate_json_from_hex.cpp new file mode 100644 index 0000000..265d9b2 --- /dev/null +++ b/tools/util_generate_json_from_hex.cpp @@ -0,0 +1,137 @@ +// +// Purpose: command line option to generate JSON from Hex code +// may be used to generate deserialization tests cases in other languages and external packages +// + +#include "abieos.h" +#include +#include +#include +#include +#include +#include + +// Main work done here four steps +// 1) create empty context +// 2) set the context +// 4) create json from hex +// abiContractName: enum with contract names +// schema: the name of the data type or reference to schema in the ABI contract +// hex: hex encoded schema +// verbose: flag to print out step by step messages +std::string generate_json_from_hex(const char* abi_definition, const char* contract_name, const char* schema, const char* hex, bool verbose) { + if (verbose) std::cerr << "Schema is: " << schema << " and hex is " << hex << std::endl << std::endl; + + // create empty context + using unique_abieos = std::unique_ptr; + unique_abieos context( abieos_create(), &abieos_destroy ); + if (! context) throw std::runtime_error("unable to create context"); + if (verbose) std::cerr << "step 1 of 3: created empty ABI context" << std::endl; + + // set the transaction context. + // first get the contract_id + uint64_t contract_id = abieos_string_to_name(context.get(), contract_name); + if (contract_id == 0) { + std::cerr << "Error: abieos_string_to_name " << abieos_get_error(context.get()) << std::endl; + throw std::runtime_error("unable to set context"); + } + // use our id and set the ABI + bool successSettingAbi = abieos_set_abi(context.get(), contract_id, abi_definition); + if (! successSettingAbi) { + std::cerr << "Error: abieos_set_abi " << abieos_get_error(context.get()) << std::endl; + throw std::runtime_error("unable to set context"); + } + if (verbose) std::cerr << "step 2 of 3: established context for transactions, packed transactions, and state history" << std::endl; + + // convert hex to json + std::string json = abieos_hex_to_json( + context.get(), + contract_id, + schema, + hex + ); + if (verbose) std::cerr << "step 3 of 3: converted hex to json" << std::endl << std::endl; + return json; +} + +// prints usage +void help(const char* exec_name) { + std::cerr << "Usage " << exec_name << ": -f -h hex -x type [-v]\n"; + std::cerr << "\t-f file with ABI definition\n"; + std::cerr << "\t-v verbose, print out steps\n"; + std::cerr << "\t-h hex: string to convert to json\n"; + std::cerr << "\t-x type: a specific data type or schema section (example uint16, action, name, uint8[])\n"; + std::cerr << "\texample: generate_hex_from_json -f ./transaction.abi -x bool -h 01\n" << std::endl; +} + +// reads file returning string of contents +std::string retrieveFileContents(const std::string &filename ) { + try { + std::ifstream ifs(filename, std::ios::in); + std::string string_of_file_contents((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + return string_of_file_contents; + } catch(std::filesystem::filesystem_error const& ex) { + std::cerr << "unable to read ABI file at path: " << filename << std::endl; + exit(EXIT_FAILURE); + } +} + +int main(int argc, char* argv[]) { + // static string for our contract id + static const char* contract_name = "eosio"; + // string returned + std::string json; + // schema name ex: bool + std::string type; + // hex value ex: 01 + std::string hex; + // file containing ABI definition + std::string abiFileName; + // string with contents of ABI file + std::string abiDefinition; + // default verbose setting + bool verbose = false; + int opt; + + try { + while ((opt = getopt(argc, argv, "vf:h:x:")) != -1) { + switch (opt) { + case 'f': abiFileName = optarg; break; + case 'v': verbose = true; break; + case 'h': hex = optarg; break; + case 'x': type = optarg; break; + case '?': + exit(EXIT_FAILURE); + default: + exit(EXIT_FAILURE); + } + } + + // check for required params + if (hex.empty() || type.empty() || abiFileName.empty()) { + help(*argv); + exit(EXIT_FAILURE); + } + + // this is an important step + // here we grab the ABI definition and stuff it in a string + abiDefinition = retrieveFileContents(abiFileName); + + json = generate_json_from_hex( + abiDefinition.c_str(), + contract_name, + type.c_str(), + hex.c_str(), + verbose + ); + if (json.length() > 0) { + std::cout << json << std::endl; + } else { + std::cerr << "returned empty" << std::endl; + } + return 0; + } catch (std::exception& e) { + std::cerr << "Could not compute json value: " << e.what() << std::endl; + return 1; + } +}