Skip to content

Commit

Permalink
Feature/swig node (hyperledger-iroha#1098)
Browse files Browse the repository at this point in the history
* First attempt to add NodeJS Swig bindings.

Signed-off-by: luckychess <[email protected]>

* Feature: Shared Library for NodeJS

Signed-off-by: Vyacheslav Bikbaev <[email protected]>

* chore: standard styleguide

Signed-off-by: Vyacheslav Bikbaev <[email protected]>

* Add readme for nodejs; fix .gitignore.

Signed-off-by: luckychess <[email protected]>

* 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 <[email protected]>

* Link Iroha and third-party libs statically

Signed-off-by: Arseniy Fokin <[email protected]>

* Add xcode_settings for Mac

Signed-off-by: Arseniy Fokin <[email protected]>

* Fix SWIG target name

Signed-off-by: Arseniy Fokin <[email protected]>

* Unignore pb directory for packaging by NPM

Signed-off-by: Arseniy Fokin <[email protected]>

* Add build section to README.md

Signed-off-by: Arseniy Fokin <[email protected]>

* Remove publishing on prepare script

Signed-off-by: Arseniy Fokin <[email protected]>

* Fix .npmignore of protobuf generated files

Signed-off-by: Arseniy Fokin <[email protected]>

* Fix get processor configuration on Mac

Signed-off-by: Arseniy Fokin <[email protected]>

* Disable ccache-swig target

Signed-off-by: Arseniy Fokin <[email protected]>

* Increment package version

Signed-off-by: Arseniy Fokin <[email protected]>

* Remove user-defined PROTOBUF_INSTALL_DIR from build process

Signed-off-by: Arseniy Fokin <[email protected]>

* Add missing Boost headers dependency to Shared Model build system

Signed-off-by: Arseniy Fokin <[email protected]>

* Add README section for Mac users

Signed-off-by: Arseniy Fokin <[email protected]>

* Delete unnecessary local build configuration files

Signed-off-by: Arseniy Fokin <[email protected]>

* Fix example to use iroha-lib package

Signed-off-by: Arseniy Fokin <[email protected]>

* Update contributors and restrictions of the package

Signed-off-by: Arseniy Fokin <[email protected]>

* Generate protobufs in single line

Signed-off-by: Arseniy Fokin <[email protected]>

* Update READMEs

Signed-off-by: Arseniy Fokin <[email protected]>

* Fix NPM scripts

Signed-off-by: Arseniy Fokin <[email protected]>

* Start generate-protobuf.sh automatically on npm install

Signed-off-by: Arseniy Fokin <[email protected]>

* Fixes according Warchant's review on PR hyperledger-iroha#939

Signed-off-by: Arseniy Fokin <[email protected]>

* Fix clean.sh - it needs to delete only generated content

Signed-off-by: Arseniy Fokin <[email protected]>

* Delete outdated prepare.sh from example folder

Signed-off-by: Arseniy Fokin <[email protected]>

* Add node-gyp as devDependency to allow MacOS builds

Signed-off-by: Arseniy Fokin <[email protected]>

* Return ccache support in FindSWIG module

Signed-off-by: Arseniy Fokin <[email protected]>

* Add minimal version of iroha-lib as v0.0.1

Signed-off-by: Arseniy Fokin <[email protected]>

* Fix bindings.gyp after merge with new develop

Signed-off-by: Arseniy Fokin <[email protected]>

* Changes according @luckychess review

Signed-off-by: Arseniy Fokin <[email protected]>

* Changes according @laSinteZ review

Signed-off-by: Arseniy Fokin <[email protected]>

* Get prebuilt sources from official Hyperledger repository

Signed-off-by: Arseniy Fokin <[email protected]>
  • Loading branch information
stinger112 authored and x3medima17 committed Mar 30, 2018
1 parent cb4ee11 commit 9b7b2ea
Show file tree
Hide file tree
Showing 20 changed files with 1,214 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand All @@ -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}")
Expand All @@ -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")
Expand Down
96 changes: 96 additions & 0 deletions cmake/Modules/Findnodejs.cmake
Original file line number Diff line number Diff line change
@@ -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 ()
61 changes: 61 additions & 0 deletions example/node/.gitignore
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions example/node/README.md
Original file line number Diff line number Diff line change
@@ -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
```
185 changes: 185 additions & 0 deletions example/node/index.js
Original file line number Diff line number Diff line change
@@ -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('../[email protected]').toString()
var adminPub = fs.readFileSync('../[email protected]').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)
}
}
Loading

0 comments on commit 9b7b2ea

Please sign in to comment.