## Balance transfer This is a sample Node.js application written using typescript which demonstrates the **__fabric-client__** and **__fabric-ca-client__** Node.js SDK APIs for typescript. ### Prerequisites and setup: * [Docker](https://www.docker.com/products/overview) - v1.12 or higher * [Docker Compose](https://docs.docker.com/compose/overview/) - v1.8 or higher * [Git client](https://git-scm.com/downloads) - needed for clone commands * **Node.js** v6.9.0 - 6.10.0 ( __Node v7+ is not supported__ ) * [Download Docker images](http://hyperledger-fabric.readthedocs.io/en/latest/samples.html#binaries) ``` cd fabric-samples/balance-transfer/ ``` Once you have completed the above setup, you will have provisioned a local network with the following docker container configuration: * 2 CAs * A SOLO orderer * 4 peers (2 peers per Org) #### Artifacts * Crypto material has been generated using the **cryptogen** tool from Hyperledger Fabric and mounted to all peers, the orderering node and CA containers. More details regarding the cryptogen tool are available [here](http://hyperledger-fabric.readthedocs.io/en/latest/build_network.html#crypto-generator). * An Orderer genesis block (genesis.block) and channel configuration transaction (mychannel.tx) has been pre generated using the **configtxgen** tool from Hyperledger Fabric and placed within the artifacts folder. More details regarding the configtxgen tool are available [here](http://hyperledger-fabric.readthedocs.io/en/latest/build_network.html#configuration-transaction-generator). ## Running the sample program There are two options available for running the balance-transfer sample as shown below. ### Option 1 ##### Terminal Window 1 ``` cd fabric-samples/balance-transfer/typescript ./runApp.sh ``` This performs the following steps: * lauches the required network on your local machine * installs the fabric-client and fabric-ca-client node modules * starts the node app on PORT 4000 ##### Terminal Window 2 NOTE: In order for the following shell script to properly parse the JSON, you must install ``jq``. See instructions at [https://stedolan.github.io/jq/](https://stedolan.github.io/jq/). Test the APIs as follows: ``` cd fabric-samples/balance-transfer/typescript ./testAPIs.sh ``` ### Option 2 is a more manual approach ##### Terminal Window 1 * Launch the network using docker-compose ``` docker-compose -f artifacts/docker-compose.yaml up ``` ##### Terminal Window 2 * Install the fabric-client and fabric-ca-client node modules ``` npm install ``` *** NOTE - If running this before the new version of the node SDK is published which includes the typescript definition files, you will need to do the following: ``` cp types/fabric-client/index.d.tx node_modules/fabric-client/index.d.ts cp types/fabric-ca-client/index.d.tx node_modules/fabric-ca-client/index.d.ts ``` * Start the node app on PORT 4000 ``` PORT=4000 ts-node app.ts ``` ##### Terminal Window 3 * Execute the REST APIs from the section [Sample REST APIs Requests](https://github.com/hyperledger/fabric-samples/tree/master/balance-transfer#sample-rest-apis-requests) ## Sample REST APIs Requests ### Login Request * Register and enroll new users in Organization - **Org1**: `curl -s -X POST http://localhost:4000/users -H "content-type: application/x-www-form-urlencoded" -d 'username=Jim&orgName=org1'` **OUTPUT:** ``` { "success": true, "secret": "RaxhMgevgJcm", "message": "Jim enrolled Successfully", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" } ``` The response contains the success/failure status, an **enrollment Secret** and a **JSON Web Token (JWT)** that is a required string in the Request Headers for subsequent requests. ### Create Channel request ``` curl -s -X POST \ http://localhost:4000/channels \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" \ -d '{ "channelName":"mychannel", "channelConfigPath":"../artifacts/channel/mychannel.tx" }' ``` Please note that the Header **authorization** must contain the JWT returned from the `POST /users` call ### Join Channel request ``` curl -s -X POST \ http://localhost:4000/channels/mychannel/peers \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" \ -d '{ "peers": ["peer1","peer2"] }' ``` ### Install chaincode ``` curl -s -X POST \ http://localhost:4000/chaincodes \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" \ -d '{ "peers": ["peer1","peer2"], "chaincodeName":"mycc", "chaincodePath":"github.com/example_cc/go", "chaincodeVersion":"v0" }' ``` ### Instantiate chaincode ``` curl -s -X POST \ http://localhost:4000/channels/mychannel/chaincodes \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" \ -d '{ "chaincodeName":"mycc", "chaincodeVersion":"v0", "args":["a","100","b","200"] }' ``` ### Invoke request ``` curl -s -X POST \ http://localhost:4000/channels/mychannel/chaincodes/mycc \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" \ -d '{ "fcn":"move", "args":["a","b","10"] }' ``` **NOTE:** Ensure that you save the Transaction ID from the response in order to pass this string in the subsequent query transactions. ### Chaincode Query ``` curl -s -X GET \ "http://localhost:4000/channels/mychannel/chaincodes/mycc?peer=peer1&fcn=query&args=%5B%22a%22%5D" \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" ``` ### Query Block by BlockNumber ``` curl -s -X GET \ "http://localhost:4000/channels/mychannel/blocks/1?peer=peer1" \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" ``` ### Query Transaction by TransactionID ``` curl -s -X GET http://localhost:4000/channels/mychannel/transactions/TRX_ID?peer=peer1 \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" ``` **NOTE**: Here the TRX_ID can be from any previous invoke transaction ### Query ChainInfo ``` curl -s -X GET \ "http://localhost:4000/channels/mychannel?peer=peer1" \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" ``` ### Query Installed chaincodes ``` curl -s -X GET \ "http://localhost:4000/chaincodes?peer=peer1&type=installed" \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" ``` ### Query Instantiated chaincodes ``` curl -s -X GET \ "http://localhost:4000/chaincodes?peer=peer1&type=instantiated" \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" ``` ### Query Channels ``` curl -s -X GET \ "http://localhost:4000/channels?peer=peer1" \ -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ -H "content-type: application/json" ``` ### Network configuration considerations You have the ability to change configuration parameters by either directly editing the network-config.json file or provide an additional file for an alternative target network. The app uses an optional environment variable "TARGET_NETWORK" to control the configuration files to use. For example, if you deployed the target network on Amazon Web Services EC2, you can add a file "network-config-aws.json", and set the "TARGET_NETWORK" environment to 'aws'. The app will pick up the settings inside the "network-config-aws.json" file. #### IP Address** and PORT information If you choose to customize your docker-compose yaml file by hardcoding IP Addresses and PORT information for your peers and orderer, then you MUST also add the identical values into the network-config.json file. The paths shown below will need to be adjusted to match your docker-compose yaml file. ``` "orderer": { "url": "grpcs://x.x.x.x:7050", "server-hostname": "orderer0", "tls_cacerts": "../artifacts/tls/orderer/ca-cert.pem" }, "org1": { "ca": "http://x.x.x.x:7054", "peer1": { "requests": "grpcs://x.x.x.x:7051", "events": "grpcs://x.x.x.x:7053", ... }, "peer2": { "requests": "grpcs://x.x.x.x:7056", "events": "grpcs://x.x.x.x:7058", ... } }, "org2": { "ca": "http://x.x.x.x:8054", "peer1": { "requests": "grpcs://x.x.x.x:8051", "events": "grpcs://x.x.x.x:8053", ... }, "peer2": { "requests": "grpcs://x.x.x.x:8056", "events": "grpcs://x.x.x.x:8058", ... } } ``` #### Discover IP Address To retrieve the IP Address for one of your network entities, issue the following command: ``` # The following will return the IP Address for peer0 docker inspect peer0 | grep IPAddress ``` Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License.