From 9b7b2ea17df0caf0ba57f82e02b27d33c05ce101 Mon Sep 17 00:00:00 2001 From: Arseniy Fokin Date: Wed, 28 Mar 2018 02:50:02 +0700 Subject: [PATCH] Feature/swig node (#1098) * First attempt to add NodeJS Swig bindings. Signed-off-by: luckychess * Feature: Shared Library for NodeJS Signed-off-by: Vyacheslav Bikbaev * chore: standard styleguide Signed-off-by: Vyacheslav Bikbaev * Add readme for nodejs; fix .gitignore. Signed-off-by: luckychess * Rename FindNodejs.cmake module * Add initial files for NPM package * Copy build configuration from example/node folder * Change base repo and add contributors to package.json * Ignore build and generated directories * Copy example code from example/node * Fix old-fashion style (as example/node) bild * Fix broken SWIG compilation * Add build dependencies * Add folder structure for binary module * Link binding.gyp targets with package.json * Add module's main js file * Ignore generated directories in Git * Return prepare.sh functionality * Add bunch of develop scripts (prebuild, generate, etc) * Fix example * Add shared_model target to build irohanode shared lib by prebuild.sh * Fix path of Iroha build dir * Autorefactor bindings.gyp * Implement prebuild.sh functionality in GYP manner * Add node-pre-gyp-github publishing. Include pb into NPM module. * Fix CMake configuration to generate irohanode as static lib Signed-off-by: Arseniy Fokin * Link Iroha and third-party libs statically Signed-off-by: Arseniy Fokin * Add xcode_settings for Mac Signed-off-by: Arseniy Fokin * Fix SWIG target name Signed-off-by: Arseniy Fokin * Unignore pb directory for packaging by NPM Signed-off-by: Arseniy Fokin * Add build section to README.md Signed-off-by: Arseniy Fokin * Remove publishing on prepare script Signed-off-by: Arseniy Fokin * Fix .npmignore of protobuf generated files Signed-off-by: Arseniy Fokin * Fix get processor configuration on Mac Signed-off-by: Arseniy Fokin * Disable ccache-swig target Signed-off-by: Arseniy Fokin * Increment package version Signed-off-by: Arseniy Fokin * Remove user-defined PROTOBUF_INSTALL_DIR from build process Signed-off-by: Arseniy Fokin * Add missing Boost headers dependency to Shared Model build system Signed-off-by: Arseniy Fokin * Add README section for Mac users Signed-off-by: Arseniy Fokin * Delete unnecessary local build configuration files Signed-off-by: Arseniy Fokin * Fix example to use iroha-lib package Signed-off-by: Arseniy Fokin * Update contributors and restrictions of the package Signed-off-by: Arseniy Fokin * Generate protobufs in single line Signed-off-by: Arseniy Fokin * Update READMEs Signed-off-by: Arseniy Fokin * Fix NPM scripts Signed-off-by: Arseniy Fokin * Start generate-protobuf.sh automatically on npm install Signed-off-by: Arseniy Fokin * Fixes according Warchant's review on PR #939 Signed-off-by: Arseniy Fokin * Fix clean.sh - it needs to delete only generated content Signed-off-by: Arseniy Fokin * Delete outdated prepare.sh from example folder Signed-off-by: Arseniy Fokin * Add node-gyp as devDependency to allow MacOS builds Signed-off-by: Arseniy Fokin * Return ccache support in FindSWIG module Signed-off-by: Arseniy Fokin * Add minimal version of iroha-lib as v0.0.1 Signed-off-by: Arseniy Fokin * Fix bindings.gyp after merge with new develop Signed-off-by: Arseniy Fokin * Changes according @luckychess review Signed-off-by: Arseniy Fokin * Changes according @laSintez review Signed-off-by: Arseniy Fokin * Get prebuilt sources from official Hyperledger repository Signed-off-by: Arseniy Fokin --- CMakeLists.txt | 3 + cmake/Modules/Findnodejs.cmake | 96 +++++++++ example/node/.gitignore | 61 ++++++ example/node/README.md | 30 +++ example/node/index.js | 185 ++++++++++++++++ example/node/package.json | 20 ++ shared_model/bindings/CMakeLists.txt | 33 ++- shared_model/packages/javascript/.gitignore | 67 ++++++ shared_model/packages/javascript/LICENSE | 201 ++++++++++++++++++ shared_model/packages/javascript/README.md | 72 +++++++ shared_model/packages/javascript/binding.gyp | 131 ++++++++++++ .../javascript/example/admin@test.priv | 1 + .../javascript/example/admin@test.pub | 1 + .../packages/javascript/example/index.js | 185 ++++++++++++++++ shared_model/packages/javascript/index.js | 6 + shared_model/packages/javascript/package.json | 69 ++++++ .../packages/javascript/pb/.npmignore | 2 + .../packages/javascript/scripts/clean.sh | 5 + .../javascript/scripts/generate-protobuf.sh | 20 ++ .../scripts/install-dependencies.sh | 27 +++ 20 files changed, 1214 insertions(+), 1 deletion(-) create mode 100644 cmake/Modules/Findnodejs.cmake create mode 100644 example/node/.gitignore create mode 100644 example/node/README.md create mode 100644 example/node/index.js create mode 100644 example/node/package.json create mode 100644 shared_model/packages/javascript/.gitignore create mode 100644 shared_model/packages/javascript/LICENSE create mode 100644 shared_model/packages/javascript/README.md create mode 100644 shared_model/packages/javascript/binding.gyp create mode 100644 shared_model/packages/javascript/example/admin@test.priv create mode 100644 shared_model/packages/javascript/example/admin@test.pub create mode 100644 shared_model/packages/javascript/example/index.js create mode 100644 shared_model/packages/javascript/index.js create mode 100644 shared_model/packages/javascript/package.json create mode 100644 shared_model/packages/javascript/pb/.npmignore create mode 100755 shared_model/packages/javascript/scripts/clean.sh create mode 100755 shared_model/packages/javascript/scripts/generate-protobuf.sh create mode 100755 shared_model/packages/javascript/scripts/install-dependencies.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index cca6f904ab..b1abdbd0b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ option(SWIG_PYTHON "Generate Swig Python bindings" OFF) option(SWIG_JAVA "Generate Swig Java bindings" OFF) option(SUPPORT_PYTHON2 "ON if Python2, OFF if python3" OFF) option(SWIG_CSHARP "Generate Swig C# bindings" OFF) +option(SWIG_NODE "Generate Swig NodeJS" OFF) option(SHARED_MODEL_DISABLE_COMPATIBILITY "Disable backward compatibility in shared model" OFF) @@ -81,6 +82,7 @@ if(PACKAGE_TGZ OR PACKAGE_ZIP OR PACKAGE_RPM OR PACKAGE_DEB) set(FUZZING OFF) set(SWIG_PYTHON OFF) set(SWIG_JAVA OFF) + set(SWIG_NODE OFF) endif() message(STATUS "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") @@ -97,6 +99,7 @@ message(STATUS "-DSWIG_PYTHON=${SWIG_PYTHON}") message(STATUS "-DSWIG_JAVA=${SWIG_JAVA}") message(STATUS "-DSUPPORT_PYTHON2=${SUPPORT_PYTHON2}") message(STATUS "-DSWIG_CSHARP=${SWIG_CSHARP}") +message(STATUS "-DSWIG_NODE=${SWIG_NODE}") message(STATUS "-DSHARED_MODEL_DISABLE_COMPATIBILITY=${SHARED_MODEL_DISABLE_COMPATIBILITY}") SET(IROHA_SCHEMA_DIR "${PROJECT_SOURCE_DIR}/schema") diff --git a/cmake/Modules/Findnodejs.cmake b/cmake/Modules/Findnodejs.cmake new file mode 100644 index 0000000000..eb2a0a4a59 --- /dev/null +++ b/cmake/Modules/Findnodejs.cmake @@ -0,0 +1,96 @@ + # Macro to add directory to NODEJS_INCLUDE_DIRS if it exists and is not /usr/include + macro(add_include_dir dir) + if (IS_DIRECTORY ${dir} AND NOT ${dir} STREQUAL "/usr/include") + set(NODEJS_INCLUDE_DIRS ${NODEJS_INCLUDE_DIRS} ${dir}) + endif() +endmacro() + + +find_program (NODEJS_EXECUTABLE NAMES node nodejs + HINTS + $ENV{NODE_DIR} + PATH_SUFFIXES bin + DOC "Node.js interpreter" +) + +include (FindPackageHandleStandardArgs) + +# If compat-libuv package exists, it must be at start of include path +find_path (UV_ROOT_DIR "uv.h" PATHS /usr/include/compat-libuv010 NO_DEFAULT_PATH) +if (UV_ROOT_DIR) + # set (NODEJS_INCLUDE_DIRS ${UV_ROOT_DIR}) + add_include_dir(${UV_ROOT_DIR}) +endif() + +# Now look for node. Flag an error if not found +find_path (NODE_ROOT_DIR "include/node/node.h" "include/src/node.h" "src/node.h" + PATHS /usr/include/nodejs /usr/local/include/nodejs /usr/local/include) +if (NODE_ROOT_DIR) + add_include_dir(${NODE_ROOT_DIR}/include/src) + add_include_dir(${NODE_ROOT_DIR}/src) + add_include_dir(${NODE_ROOT_DIR}/include/node) + add_include_dir(${NODE_ROOT_DIR}/include/deps/v8/include) + add_include_dir(${NODE_ROOT_DIR}/deps/v8/include) + add_include_dir(${NODE_ROOT_DIR}/include/deps/uv/include) + add_include_dir(${NODE_ROOT_DIR}/deps/uv/include) +else() + unset(NODEJS_INCLUDE_DIRS) + message(FATAL_ERROR " - node.h not found") +endif() + +# Check that v8.h is in NODEJS_INCLUDE_DIRS +find_path (V8_ROOT_DIR "v8.h" PATHS ${NODEJS_INCLUDE_DIRS}) +if (NOT V8_ROOT_DIR) + unset(NODEJS_INCLUDE_DIRS) + message(FATAL_ERROR " - v8.h not found") +endif() + +# Check that uv.h is in NODEJS_INCLUDE_DIRS +find_path (UV_ROOT_DIR "uv.h" PATHS ${NODEJS_INCLUDE_DIRS}) +if (NOT UV_ROOT_DIR) + unset(NODEJS_INCLUDE_DIRS) + message(FATAL_ERROR " - uv.h not found") +endif() + +find_package_handle_standard_args (Nodejs DEFAULT_MSG + NODEJS_EXECUTABLE + NODEJS_INCLUDE_DIRS +) + +if (NODEJS_EXECUTABLE) + execute_process(COMMAND ${NODEJS_EXECUTABLE} --version + OUTPUT_VARIABLE _VERSION + RESULT_VARIABLE _NODE_VERSION_RESULT) + execute_process(COMMAND ${NODEJS_EXECUTABLE} -e "console.log(process.versions.v8)" + OUTPUT_VARIABLE _V8_VERSION + RESULT_VARIABLE _V8_RESULT) + if (NOT _NODE_VERSION_RESULT AND NOT _V8_RESULT) + string (REPLACE "v" "" NODE_VERSION_STRING "${_VERSION}") + string (REPLACE "." ";" _VERSION_LIST "${NODE_VERSION_STRING}") + list (GET _VERSION_LIST 0 NODE_VERSION_MAJOR) + list (GET _VERSION_LIST 1 NODE_VERSION_MINOR) + list (GET _VERSION_LIST 2 NODE_VERSION_PATCH) + set (V8_VERSION_STRING ${_V8_VERSION}) + string (REPLACE "." ";" _V8_VERSION_LIST "${_V8_VERSION}") + list (GET _V8_VERSION_LIST 0 V8_VERSION_MAJOR) + list (GET _V8_VERSION_LIST 1 V8_VERSION_MINOR) + list (GET _V8_VERSION_LIST 2 V8_VERSION_PATCH) + # we end up with a nasty newline so strip everything that isn't a number + string (REGEX MATCH "^[0-9]*" V8_VERSION_PATCH ${V8_VERSION_PATCH}) + else () + set (NODE_VERSION_STRING "0.10.30") + set (NODE_VERSION_MAJOR "0") + set (NODE_VERSION_MINOR "10") + set (NODE_VERSION_PATCH "30") + set (V8_VERSION_MAJOR "3") + set (V8_VERSION_MINOR"14") + set (V8_VERSION_PATCH "5") + set (V8_VERSION_STRING "3.28.72") + message (STATUS "defaulted to node 0.10.30") + endif () + string (REGEX REPLACE "\n" "" NODE_VERSION_STRING ${NODE_VERSION_STRING}) + string (REGEX REPLACE "\n" "" V8_VERSION_STRING ${V8_VERSION_STRING}) + message (STATUS "INFO - Node version is " ${NODE_VERSION_STRING}) + message (STATUS "INFO - Node using v8 " ${V8_VERSION_STRING}) + mark_as_advanced (NODEJS_EXECUTABLE) +endif () diff --git a/example/node/.gitignore b/example/node/.gitignore new file mode 100644 index 0000000000..9607012b57 --- /dev/null +++ b/example/node/.gitignore @@ -0,0 +1,61 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# NPM lockfile +package-lock.json diff --git a/example/node/README.md b/example/node/README.md new file mode 100644 index 0000000000..ee80968376 --- /dev/null +++ b/example/node/README.md @@ -0,0 +1,30 @@ +# NodeJS client library example + +## Prerequisites + +1. Make sure you have running iroha on your machine. You can follow [this](https://hyperledger.github.io/iroha-api/#run-the-daemon-irohad) guide to launch iroha daemon. Please use keys for iroha from *iroha/example* folder, since this example uses keys from there. + +2. If you are a lucky owner of a processor with the x64 architecture, you can install **iroha-lib** from the NPM repository with a simple command: + +```sh +npm install iroha-lib +``` + +In other cases, you need to download the complete Iroha repository (in which you are now), go to the *shared_model/packages/javascript* folder and build the package on your system manually using the instructions from **README.md**. +In such case, you need to change the import paths in this example to *shared_model/packages/javascript*. + +## Launch example + +Script `index.js` does the following: +1. Assemble transaction from several commands using tx builder +2. Sign it using keys from iroha/example folder +3. Send it to iroha +4. Wait 5 secs and check transaction's status using its hash +5. Assemble query using query builder +6. Send query to iroha +7. Read query response + +Launch it: +```sh +node index.js +``` diff --git a/example/node/index.js b/example/node/index.js new file mode 100644 index 0000000000..41d4ce3b86 --- /dev/null +++ b/example/node/index.js @@ -0,0 +1,185 @@ +/** + * Copyright 2018 HUAWEI. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +'use strict' + +function blob2array (blob) { + var bytearray = new Uint8Array(blob.size()) + for (let i = 0; i < blob.size(); ++i) { + bytearray[i] = blob.get(i) + } + return bytearray +} + +var iroha = require('iroha-lib') +var txBuilder = new iroha.ModelTransactionBuilder() +var queryBuilder = new iroha.ModelQueryBuilder() +var crypto = new iroha.ModelCrypto() +var protoTxHelper = new iroha.ModelProtoTransaction() +var protoQueryHelper = new iroha.ModelProtoQuery() +var fs = require('fs') +var adminPriv = fs.readFileSync('../admin@test.priv').toString() +var adminPub = fs.readFileSync('../admin@test.pub').toString() + +var keys = crypto.convertFromExisting(adminPub, adminPriv) + +var currentTime = Date.now() +var startTxCounter = 1 +var startQueryCounter = 1 +var creator = 'admin@test' + +// build transaction +var tx = txBuilder + .creatorAccountId(creator) + .txCounter(startTxCounter) + .createdTime(currentTime) + .createDomain('ru', 'user') + .createAsset('dollar', 'ru', 2) + .build() + +// sign transaction and get its binary representation (Blob) +var txblob = protoTxHelper.signAndAddSignature(tx, keys).blob() +var txArray = blob2array(txblob) +// create proto object and send to iroha +var blockTransaction = require('iroha-lib/pb/block_pb.js').Transaction // block_pb2.Transaction() +var protoTx = blockTransaction.deserializeBinary(txArray) +console.log(protoTx.getPayload().getCreatorAccountId()) + +var grpc = require('grpc') +var endpointGrpc = require('iroha-lib/pb/endpoint_grpc_pb.js') +var client = new endpointGrpc.CommandServiceClient( + 'localhost:50051', + grpc.credentials.createInsecure() +) +var txHashBlob = tx.hash().blob() +var txHash = blob2array(txHashBlob) +var p = new Promise((resolve, reject) => { + console.log('Submit transaction...') + client.torii(protoTx, (err, data) => { + if (err) { + reject(err) + } else { + console.log('Submitted transaction successfully') + resolve() + } + }) +}) + +p + .then(() => { + console.log('Sleep 5 seconds...') + return sleep(5000) + }) + .then(() => { + console.log('Send transaction status request...') + return new Promise((resolve, reject) => { + // create status request + var endpointPb = require('iroha-lib/pb/endpoint_pb.js') + var request = new endpointPb.TxStatusRequest() + request.setTxHash(txHash) + client.status(request, (err, response) => { + if (err) { + reject(err) + } else { + let status = response.getTxStatus() + let TxStatus = require('iroha-lib/pb/endpoint_pb.js').TxStatus + let statusName = getProtoEnumName( + TxStatus, + 'iroha.protocol.TxStatus', + status + ) + console.log('Got transaction status: ' + statusName) + if (statusName !== 'COMMITTED') { + reject(new Error("Your transaction wasn't committed")) + } else { + resolve() + } + } + }) + }) + }) + .then(() => { + console.log('Query transaction...') + let query = queryBuilder + .creatorAccountId(creator) + .createdTime(Date.now()) + .queryCounter(startQueryCounter) + .getAssetInfo('dollar#ru') + .build() + let queryBlob = protoQueryHelper.signAndAddSignature(query, keys).blob() + let pbQuery = require('iroha-lib/pb/queries_pb.js').Query + let queryArray = blob2array(queryBlob) + let protoQuery = pbQuery.deserializeBinary(queryArray) + let client = new endpointGrpc.QueryServiceClient( + 'localhost:50051', + grpc.credentials.createInsecure() + ) + return new Promise((resolve, reject) => { + client.find(protoQuery, (err, response) => { + if (err) { + reject(err) + } else { + console.log('Submitted transaction successfully') + let type = response.getResponseCase() + let responsePb = require('iroha-lib/pb/responses_pb.js') + let name = getProtoEnumName( + responsePb.QueryResponse.ResponseCase, + 'iroha.protocol.QueryResponse', + type + ) + if (name !== 'ASSET_RESPONSE') { + reject(new Error('Query response error')) + } else { + let asset = response.getAssetResponse().getAsset() + console.log( + 'Asset Id = ' + + asset.getAssetId() + + ' , Precision = ' + + asset.getPrecision() + ) + resolve() + } + } + }) + }) + }) + .then(() => { + console.log('done!') + }) + .catch(err => { + console.log(err) + }) + +function sleep (ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +var protoEnumName = {} +function getProtoEnumName (obj, key, value) { + if (protoEnumName.hasOwnProperty(key)) { + if (protoEnumName[key].length < value) { + return 'unknown' + } else { + return protoEnumName[key][value] + } + } else { + protoEnumName[key] = [] + for (var k in obj) { + let idx = obj[k] + if (isNaN(idx)) { + console.log( + 'getProtoEnumName:wrong enum value, now is type of ' + + typeof idx + + ' should be integer' + ) + } else { + protoEnumName[key][idx] = k + } + } + return getProtoEnumName(obj, key, value) + } +} diff --git a/example/node/package.json b/example/node/package.json new file mode 100644 index 0000000000..16ee00c1d5 --- /dev/null +++ b/example/node/package.json @@ -0,0 +1,20 @@ +{ + "name": "iroha-example", + "version": "1.0.0", + "description": "Example of how to use Iroha Library with Node.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "node-gyp": "node-gyp" + }, + "repository": "git+https://github.com/hyperledger/iroha.git", + "author": "", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/hyperledger/iroha/issues" + }, + "homepage": "https://github.com/hyperledger/iroha#readme", + "dependencies": { + "iroha-lib": "^0.0.1" + }, + "main": "index.js" +} diff --git a/shared_model/bindings/CMakeLists.txt b/shared_model/bindings/CMakeLists.txt index 659d0ff3ec..4e21695fe4 100644 --- a/shared_model/bindings/CMakeLists.txt +++ b/shared_model/bindings/CMakeLists.txt @@ -29,7 +29,7 @@ target_link_libraries(bindings -if (SWIG_PYTHON OR SWIG_JAVA OR SWIG_CSHARP) +if (SWIG_PYTHON OR SWIG_JAVA OR SWIG_CSHARP OR SWIG_NODE) find_package(swig REQUIRED) include(${SWIG_USE_FILE}) @@ -80,3 +80,34 @@ if (SWIG_CSHARP) swig_link_libraries(libirohacs bindings) add_custom_target(irohacs DEPENDS ${SWIG_MODULE_libirohacs_REAL_NAME}) endif() + +if (SWIG_NODE) + find_package (nodejs REQUIRED) + include_directories ( + ${NODEJS_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ) + + set (V8_VERSION_HEX 0x0${V8_VERSION_MAJOR}${V8_VERSION_MINOR}${V8_VERSION_PATCH}) + string (LENGTH "${V8_VERSION_HEX}" V8_VERSION_HEX_length) + while (V8_VERSION_HEX_length LESS 8) + set (V8_VERSION_HEX "${V8_VERSION_HEX}0") + message (STATUS "INFO - Padded V8 version to match SWIG format") + string (LENGTH "${V8_VERSION_HEX}" V8_VERSION_HEX_length) + endwhile () + + if (${CMAKE_SYSTEM_NAME} STREQUAL Darwin) + set(MAC_OPTS "-flat_namespace -undefined suppress") + endif() + + set_property(SOURCE bindings.i PROPERTY SWIG_FLAGS "-node" "-DV8_VERSION=${V8_VERSION_HEX}") + + # Build SWIG library always statically for the subsequent assembly by GYP + swig_add_library(irohanode + TYPE STATIC + LANGUAGE javascript + SOURCES bindings.i + ) + set_target_properties(irohanode PROPERTIES PREFIX ${CMAKE_STATIC_LIBRARY_PREFIX}) + target_link_libraries(irohanode bindings ${MAC_OPTS}) +endif() diff --git a/shared_model/packages/javascript/.gitignore b/shared_model/packages/javascript/.gitignore new file mode 100644 index 0000000000..a578af2687 --- /dev/null +++ b/shared_model/packages/javascript/.gitignore @@ -0,0 +1,67 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# Build and generated directories +build/* +pb/* +lib/* + +package-lock.json + +!.npmignore diff --git a/shared_model/packages/javascript/LICENSE b/shared_model/packages/javascript/LICENSE new file mode 100644 index 0000000000..40f9c24091 --- /dev/null +++ b/shared_model/packages/javascript/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Soramitsu LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/shared_model/packages/javascript/README.md b/shared_model/packages/javascript/README.md new file mode 100644 index 0000000000..8a5ad2f7cf --- /dev/null +++ b/shared_model/packages/javascript/README.md @@ -0,0 +1,72 @@ +# iroha-js + +Official Iroha JavaScript Library. https://github.com/hyperledger/iroha + +## Usage + +You can use regular Node.js style to import **iroha-lib** package and related protobufs: + +```javascript +const iroha = require('iroha-lib') + +const blockTransaction = require('iroha-lib/pb/block_pb.js').Transaction +const endpointGrpc = require('iroha-lib/pb/endpoint_grpc_pb.js') + +... + +``` + +Watch usage in *example* folder. + +## Build + +You need this section if you want to build **iroha-lib** manually for publishing or if your architecture/OS not supported yet. + +### Prerequisities + +** +WARNING! +If you have already installed SWIG, you MUST install patched version instead using [this patch](https://github.com/swig/swig/pull/968.patch). +Or just delete global installed SWIG - Iroha be able to pull and compile it automatically. +** + +In order to build NPM package by `node-gyp` on your machine you need some global installed dependencies: + +1. CMake (>=3.8.2) + +2. Protobuf (>=3.5.1) + +3. Boost (>=1.65.1) + +#### For Mac users + +To build **iroha-lib** on Mac the following dependencies should be installed: + +```sh +brew install node cmake # Common dependencies +brew install autoconf automake ccache # SWIG dependencies +brew install protobuf boost # Iroha dependencies +``` + +### Build process + +1. Clone full Iroha repository + +```sh +git clone -b develop --depth=1 https://github.com/hyperledger/iroha + +``` + +2. Go to the NPM package directory and start build + +```sh +cd iroha/shared_model/packages/javascript +npm install +``` + +That's pretty all. + +--- + + +This NPM package is in deep pre-alfa phase, so if you have any troubles, feel free to create a new issue or contact contributors from *package.json*. diff --git a/shared_model/packages/javascript/binding.gyp b/shared_model/packages/javascript/binding.gyp new file mode 100644 index 0000000000..644d6fd768 --- /dev/null +++ b/shared_model/packages/javascript/binding.gyp @@ -0,0 +1,131 @@ +{ + 'variables': { + 'iroha_home_dir': '../../../' + }, + 'targets': [ + { + 'target_name': 'shared_model', + 'type': 'none', + 'actions': [ + { + 'action_name': 'configure', + 'message': 'Generate CMake build configuration for shared_model...', + 'inputs': [ + '<(iroha_home_dir)/shared_model/bindings/CMakeLists.txt' + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/Makefile', + ], + 'action': [ + 'cmake', + '-H<(iroha_home_dir)', + '-B<(SHARED_INTERMEDIATE_DIR)', + '-DSWIG_NODE=ON', + '-DENABLE_LIBS_PACKAGING=OFF', + '-DSHARED_MODEL_DISABLE_COMPATIBILITY=ON', + '-DCMAKE_POSITION_INDEPENDENT_CODE=ON', + '-DCMAKE_BUILD_TYPE=Release' + ], + }, + { + 'action_name': 'build', + 'message': 'Build shared_model libraries by CMake...', + 'inputs': [ + '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/Makefile', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/bindingsJAVASCRIPT_wrap.cxx', + '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/libirohanode.a', + '<(SHARED_INTERMEDIATE_DIR)/shared_model/bindings/libbindings.a' + ], + 'action': [ + 'cmake', + '--build', '<(SHARED_INTERMEDIATE_DIR)', + '--target', 'irohanode', + '--', + '-j { + console.log('Submit transaction...') + client.torii(protoTx, (err, data) => { + if (err) { + reject(err) + } else { + console.log('Submitted transaction successfully') + resolve() + } + }) +}) + +p + .then(() => { + console.log('Sleep 5 seconds...') + return sleep(5000) + }) + .then(() => { + console.log('Send transaction status request...') + return new Promise((resolve, reject) => { + // create status request + var endpointPb = require('../pb/endpoint_pb.js') + var request = new endpointPb.TxStatusRequest() + request.setTxHash(txHash) + client.status(request, (err, response) => { + if (err) { + reject(err) + } else { + let status = response.getTxStatus() + let TxStatus = require('../pb/endpoint_pb.js').TxStatus + let statusName = getProtoEnumName( + TxStatus, + 'iroha.protocol.TxStatus', + status + ) + console.log('Got transaction status: ' + statusName) + if (statusName !== 'COMMITTED') { + reject(new Error("Your transaction wasn't committed")) + } else { + resolve() + } + } + }) + }) + }) + .then(() => { + console.log('Query transaction...') + let query = queryBuilder + .creatorAccountId(creator) + .createdTime(Date.now()) + .queryCounter(startQueryCounter) + .getAssetInfo('dollar#ru') + .build() + let queryBlob = protoQueryHelper.signAndAddSignature(query, keys).blob() + let pbQuery = require('../pb/queries_pb.js').Query + let queryArray = blob2array(queryBlob) + let protoQuery = pbQuery.deserializeBinary(queryArray) + let client = new endpointGrpc.QueryServiceClient( + 'localhost:50051', + grpc.credentials.createInsecure() + ) + return new Promise((resolve, reject) => { + client.find(protoQuery, (err, response) => { + if (err) { + reject(err) + } else { + console.log('Submitted transaction successfully') + let type = response.getResponseCase() + let responsePb = require('../pb/responses_pb.js') + let name = getProtoEnumName( + responsePb.QueryResponse.ResponseCase, + 'iroha.protocol.QueryResponse', + type + ) + if (name !== 'ASSET_RESPONSE') { + reject(new Error('Query response error')) + } else { + let asset = response.getAssetResponse().getAsset() + console.log( + 'Asset Id = ' + + asset.getAssetId() + + ' , Precision = ' + + asset.getPrecision() + ) + resolve() + } + } + }) + }) + }) + .then(() => { + console.log('done!') + }) + .catch(err => { + console.log(err) + }) + +function sleep (ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +var protoEnumName = {} +function getProtoEnumName (obj, key, value) { + if (protoEnumName.hasOwnProperty(key)) { + if (protoEnumName[key].length < value) { + return 'unknown' + } else { + return protoEnumName[key][value] + } + } else { + protoEnumName[key] = [] + for (var k in obj) { + let idx = obj[k] + if (isNaN(idx)) { + console.log( + 'getProtoEnumName:wrong enum value, now is type of ' + + typeof idx + + ' should be integer' + ) + } else { + protoEnumName[key][idx] = k + } + } + return getProtoEnumName(obj, key, value) + } +} diff --git a/shared_model/packages/javascript/index.js b/shared_model/packages/javascript/index.js new file mode 100644 index 0000000000..8121ce6332 --- /dev/null +++ b/shared_model/packages/javascript/index.js @@ -0,0 +1,6 @@ +var binary = require('node-pre-gyp') +var path = require('path') +var bindingPath = binary.find(path.resolve(path.join(__dirname, './package.json'))) +var binding = require(bindingPath) + +module.exports = binding diff --git a/shared_model/packages/javascript/package.json b/shared_model/packages/javascript/package.json new file mode 100644 index 0000000000..f2cd435c3f --- /dev/null +++ b/shared_model/packages/javascript/package.json @@ -0,0 +1,69 @@ +{ + "name": "iroha-lib", + "version": "0.0.4", + "description": "Modern JavaScript library for Iroha. https://github.com/hyperledger/iroha", + "main": "index.js", + "scripts": { + "prepare": "sh scripts/generate-protobuf.sh", + "prepublishOnly": "npm ls", + "install": "node-pre-gyp install --fallback-to-build", + "test": "echo \"Error: no test specified\" && exit 1", + "build": "node-pre-gyp build", + "rebuild": "node-pre-gyp rebuild" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hyperledger/iroha-javascript.git" + }, + "keywords": [ + "iroha", + "iroha-js", + "irohajs", + "iroha-node", + "iroha-nodejs", + "iroha-javascript", + "iroha-client", + "iroha-lib" + ], + "contributors": [ + "Arseniy Fokin (https://github.com/stinger112/)", + "Viacheslav Bikbaev (https://github.com/lasintez/)" + ], + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/hyperledger/iroha/issues" + }, + "homepage": "https://github.com/hyperledger/iroha#readme", + "os": [ + "darwin", + "linux" + ], + "cpu": [ + "x64" + ], + "dependencies": { + "google-protobuf": "^3.5.0", + "grpc": "^1.9.1", + "node-pre-gyp": "^0.6.39" + }, + "devDependencies": { + "grpc-tools": "^1.6.6", + "node-gyp": "^3.6.2", + "node-pre-gyp-github": "^1.3.1", + "standard": "^11.0.1" + }, + "bundledDependencies": [ + "node-pre-gyp" + ], + "directories": { + "example": "example", + "lib": "lib" + }, + "binary": { + "module_name": "iroha_lib", + "module_path": "lib/{node_abi}-{platform}-{arch}-{libc}/", + "host": "https://github.com/hyperledger/iroha-javascript/releases/download/", + "remote_path": "{version}", + "package_name": "{node_abi}-{platform}-{arch}-{libc}.tar.gz" + } +} diff --git a/shared_model/packages/javascript/pb/.npmignore b/shared_model/packages/javascript/pb/.npmignore new file mode 100644 index 0000000000..89771d0e7a --- /dev/null +++ b/shared_model/packages/javascript/pb/.npmignore @@ -0,0 +1,2 @@ +# Add all generated content into NPM package +!* diff --git a/shared_model/packages/javascript/scripts/clean.sh b/shared_model/packages/javascript/scripts/clean.sh new file mode 100755 index 0000000000..b53ed466cf --- /dev/null +++ b/shared_model/packages/javascript/scripts/clean.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +rm -rf build +rm -rf lib +rm -rf pb/*.js diff --git a/shared_model/packages/javascript/scripts/generate-protobuf.sh b/shared_model/packages/javascript/scripts/generate-protobuf.sh new file mode 100755 index 0000000000..bdbada99e5 --- /dev/null +++ b/shared_model/packages/javascript/scripts/generate-protobuf.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +CURDIR="$(cd "$(dirname "$0")"; pwd)" +IROHA_HOME="$(dirname $(dirname $(dirname $(dirname "${CURDIR}"))))" + +# Check if we inside Iroha repository +if [ -d "$IROHA_HOME/schema" ]; then + echo ------------------------- + echo "Generating Protobuf JS files..." + echo "IROHA_HOME: $IROHA_HOME" + + ./node_modules/.bin/grpc_tools_node_protoc --proto_path=$IROHA_HOME/schema \ + --plugin=protoc-gen-grpc=./node_modules/grpc-tools/bin/grpc_node_plugin \ + --js_out=import_style=commonjs,binary:./pb \ + --grpc_out=./pb \ + endpoint.proto yac.proto ordering.proto loader.proto block.proto primitive.proto commands.proto queries.proto responses.proto + + echo "Success!" + echo ------------------------- +fi diff --git a/shared_model/packages/javascript/scripts/install-dependencies.sh b/shared_model/packages/javascript/scripts/install-dependencies.sh new file mode 100755 index 0000000000..27cc229bcf --- /dev/null +++ b/shared_model/packages/javascript/scripts/install-dependencies.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# This script build and install source-based dependencies. +# By default it builds static versions of used libs, so it CAN BE used for NPM package publishing. + +# CMake 3.7 +git clone https://gitlab.kitware.com/cmake/cmake.git /tmp/cmake; \ + (cd /tmp/cmake ; git checkout 64130a7e793483e24c1d68bdd234f81d5edb2d51); \ + (cd /tmp/cmake ; /tmp/cmake/bootstrap --parallel="$(getconf _NPROCESSORS_ONLN)" --enable-ccache); \ + make -j"$(getconf _NPROCESSORS_ONLN)" -C /tmp/cmake; \ + make -C /tmp/cmake install; \ + rm -rf /tmp/cmake + +# Boost (static) +git clone https://github.com/boostorg/boost /tmp/boost; \ + (cd /tmp/boost ; git checkout 436ad1dfcfc7e0246141beddd11c8a4e9c10b146); \ + (cd /tmp/boost ; git submodule init); \ + (cd /tmp/boost ; git submodule update --recursive -j "$(getconf _NPROCESSORS_ONLN)"; \ + (cd /tmp/boost ; /tmp/boost/bootstrap.sh --with-libraries=system,filesystem); \ + (cd /tmp/boost ; /tmp/boost/b2 headers); \ + (cd /tmp/boost ; sudo /tmp/boost/b2 link=static cxxflags="-std=c++14" -j "$(getconf _NPROCESSORS_ONLN)" install); \ + rm -rf /tmp/boost + +# Protobuf (static) +git clone --depth 1 --branch v3.5.1 https://github.com/google/protobuf +cd protobuf +cmake -Hcmake/ -Bbuild -Dprotobuf_BUILD_TESTS=OFF -Dprotobuf_BUILD_SHARED_LIBS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release +sudo cmake --build build/ --target install -- -j"$(getconf _NPROCESSORS_ONLN)"