## Balance transfer
A sample Node.js app to demonstrate **__fabric-client__** & **__fabric-ca-client__** Node.js SDK APIs
### 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
```
cd fabric-sdk-node/examples/balance-transfer/
docker-compose -f artifacts/docker-compose.yaml pull
```
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
### Option 1:
##### Terminal Window 1
* Launch the network using docker-compose
```
docker-compose -f artifacts/docker-compose.yaml up
```
##### Terminal Window 2
* Execute the REST APIs from the section [Sample REST APIs Requests](https://github.com/hyperledger/fabric-sdk-node/tree/master/examples/balance-transfer#running-the-sample-program)
### Option 2:
##### Terminal Window 1
```
cd fabric-sdk-node/examples/balance-transfer
./runApp.sh
```
* This lauches the required network on your local machine
* Installs the fabric-client and fabric-ca-client node modules
* And, starts the node app on PORT 4000
##### Terminal Window 2
In order for the following shell script to properly parse the JSON, you must install ``jq``:
instructions [https://stedolan.github.io/jq/](https://stedolan.github.io/jq/)
With the application started in terminal 1, next, test the APIs by executing the script - **testAPIs.sh**:
```
cd fabric-sdk-node/examples/balance-transfer
./testAPIs.sh
```
## 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": ["localhost:7051","localhost:7056"]
}'
```
### 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": ["localhost:7051","localhost:7056"],
"chaincodeName":"mycc",
"chaincodePath":"github.com/example_cc",
"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 '{
"peers": ["localhost:7051"],
"chaincodeName":"mycc",
"chaincodeVersion":"v0",
"fcn":"init",
"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 '{
"peers": ["localhost:7051", "localhost:7056"],
"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 editing the network-config.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:
```
# this will return the IP Address for peer0
docker inspect peer0 | grep IPAddress
```

This work is licensed under a Creative Commons Attribution 4.0 International License.