diff --git a/examples/balance-transfer/README.md b/examples/balance-transfer/README.md new file mode 100644 index 0000000000..82190b903c --- /dev/null +++ b/examples/balance-transfer/README.md @@ -0,0 +1,284 @@ +## Balance transfer + +A sample node-based app to demonstrate fabric-client & fabric-ca-client Node SDK APIs + +### Prerequisites and setup: + +Follow the Getting Started [instructions](http://hyperledger-fabric.readthedocs.io/en/latest/getting_started.html#prerequisites-and-setup) in the Hyperledger Fabric documentation. + +* Additionally install Nodejs v6.2.0 - 6.10.0 (Node v7+ is not supported) + +Once you have completed the above setup, you will be provisioned a local network with following configuration: + +* 2 CAs +* A solo orderer +* 4 peers (2 peers per Org) + +#### Artifacts +* Crypto material has been generated using the cryptogen tool from fabric and mounted to all peers, the orderering node and CA org containers. +* An Orderer genesis block (orderer.block) and channel configuration transaction (mychannel.tx) has been pre generated using the configtxgen tool and placed within the artifacts folder. + + +### Running the sample program + +##### Terminal Window 1 + +``` +cd fabric-sdk-node/examples/balance-transfer + +./runApp.sh + +``` + +* This lauches the required network on your local machine +* Installs the "alpha" tagged 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``: + +Install [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 + +``` + +### Login Request + +* Register and enroll new users in Organization - **Org1**: + +`curl -s -X POST http://localhost:4000/users -H "cache-control: no-cache" -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 "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -d '{ + "channelName":"mychannel", + "channelConfigPath":"../artifacts/channel/mychannel.tx" +}' +``` + +Please note that the Headers **x-access-token** and **authorization** must contain the JWT + +### Join Channel request + +``` +curl -s -X POST \ + http://localhost:4000/channels/mychannel/peers \ + -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -H "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -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 "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -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 "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -d '{ + "peers": ["localhost:7051"], + "chaincodeName":"mycc", + "chaincodePath":"github.com/example_cc", + "chaincodeVersion":"v0", + "functionName":"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 "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -d '{ + "peers": ["localhost:7051", "localhost:7056"], + "chaincodeVersion":"v0", + "functionName":"invoke", + "args":["move","a","b","10"] +}' +``` +**NOTE:** Ensure that you save the Transaction ID 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&args=%5B%22query%22%2C%22a%22%5D&chaincodeVersion=v0" \ + -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -H "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" +``` + +### 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 "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" +``` + +### 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 "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" +``` +**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 "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" +``` + +### Query Installed chaincodes + +``` +curl -s -X GET \ + "http://localhost:4000/chaincodes?peer=peer1&type=installed" \ + -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -H "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" +``` + +### Query Instantiated chaincodes + +``` +curl -s -X GET \ + "http://localhost:4000/chaincodes?peer=peer1&type=instantiated" \ + -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -H "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" +``` + +### Query Channels + +``` +curl -s -X GET \ + "http://localhost:4000/channels?peer=peer1" \ + -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" \ + -H "cache-control: no-cache" \ + -H "content-type: application/json" \ + -H "x-access-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0OTQ4NjU1OTEsInVzZXJuYW1lIjoiSmltIiwib3JnTmFtZSI6Im9yZzEiLCJpYXQiOjE0OTQ4NjE5OTF9.yWaJhFDuTvMQRaZIqg20Is5t-JJ_1BP58yrNLOKxtNI" +``` + +### 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 +```