diff --git a/.gitignore b/.gitignore index 4b705ae4..d42cb944 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ /config .DS_Store .project +node_modules diff --git a/chaincode/abac/node/abac.js b/chaincode/abac/node/abac.js new file mode 100644 index 00000000..0dbd64b4 --- /dev/null +++ b/chaincode/abac/node/abac.js @@ -0,0 +1,145 @@ +/* +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +*/ + +const shim = require('fabric-shim'); +const util = require('util'); + +// this is exact same chaincode as balance-transfer app except that +// it adds ABAC (Attribute Based Access Control) to the Init method +var Chaincode = class { + + // Initialize the chaincode + async Init(stub) { + console.info('========= abac Init ========='); + let ret = stub.getFunctionAndParameters(); + console.info(ret); + let cid = new shim.ClientIdentity(stub); + if (!cid.assertAttributeValue("abac.init", "true")) { + return shim.error("Insufficient privileges"); + } + let args = ret.params; + // initialise only if 4 parameters passed. + if (args.length != 4) { + return shim.error('Incorrect number of arguments. Expecting 4'); + } + + let A = args[0]; + let B = args[2]; + let Aval = args[1]; + let Bval = args[3]; + + if (typeof parseInt(Aval) !== 'number' || typeof parseInt(Bval) !== 'number') { + return shim.error('Expecting integer value for asset holding'); + } + + try { + await stub.putState(A, Buffer.from(Aval)); + try { + await stub.putState(B, Buffer.from(Bval)); + return shim.success(); + } catch (err) { + return shim.error(err); + } + } catch (err) { + return shim.error(err); + } + } + + async Invoke(stub) { + let ret = stub.getFunctionAndParameters(); + console.info(ret); + let method = this[ret.fcn]; + if (!method) { + console.log('no method of name:' + ret.fcn + ' found'); + return shim.success(); + } + try { + let payload = await method(stub, ret.params); + return shim.success(payload); + } catch (err) { + console.log(err); + return shim.error(err); + } + } + + // Transaction makes payment of X units from A to B + async invoke(stub, args) { + if (args.length != 3) { + throw new Error('Incorrect number of arguments. Expecting 3'); + } + + let A = args[0]; + let B = args[1]; + if (!A || !B) { + throw new Error('asset holding must not be empty'); + } + + // Get the state from the ledger + let Avalbytes = await stub.getState(A); + if (!Avalbytes) { + throw new Error('Failed to get state of asset holder A'); + } + let Aval = parseInt(Avalbytes.toString()); + + let Bvalbytes = await stub.getState(B); + if (!Bvalbytes) { + throw new Error('Failed to get state of asset holder B'); + } + + let Bval = parseInt(Bvalbytes.toString()); + // Perform the execution + let amount = parseInt(args[2]); + if (typeof amount !== 'number') { + throw new Error('Expecting integer value for amount to be transaferred'); + } + + Aval = Aval - amount; + Bval = Bval + amount; + console.info(util.format('Aval = %d, Bval = %d\n', Aval, Bval)); + + // Write the states back to the ledger + await stub.putState(A, Buffer.from(Aval.toString())); + await stub.putState(B, Buffer.from(Bval.toString())); + + } + + // Deletes an entity from state + async delete(stub, args) { + if (args.length != 1) { + throw new Error('Incorrect number of arguments. Expecting 1'); + } + + let A = args[0]; + + // Delete the key from the state in ledger + await stub.deleteState(A); + } + + // query callback representing the query of a chaincode + async query(stub, args) { + if (args.length != 1) { + throw new Error('Incorrect number of arguments. Expecting name of the person to query') + } + + let jsonResp = {}; + let A = args[0]; + + // Get the state from the ledger + let Avalbytes = await stub.getState(A); + if (!Avalbytes) { + jsonResp.error = 'Failed to get state for ' + A; + throw new Error(JSON.stringify(jsonResp)); + } + + jsonResp.name = A; + jsonResp.amount = Avalbytes.toString(); + console.info('Query Response:'); + console.info(jsonResp); + return Avalbytes; + } +}; + +shim.start(new Chaincode()); diff --git a/chaincode/abac/node/package.json b/chaincode/abac/node/package.json new file mode 100644 index 00000000..f3d360d2 --- /dev/null +++ b/chaincode/abac/node/package.json @@ -0,0 +1,15 @@ +{ + "name": "abac", + "version": "1.0.0", + "description": "attribute based access control chaincode implemented in node.js", + "engines": { + "node": ">=8.4.0", + "npm": ">=5.3.0" + }, + "scripts": { "start" : "node abac.js" }, + "engine-strict": true, + "license": "Apache-2.0", + "dependencies": { + "fabric-shim": "1.3.1" + } +} diff --git a/fabric-ca/makeDocker.sh b/fabric-ca/makeDocker.sh index f55bbba4..55d9aa64 100755 --- a/fabric-ca/makeDocker.sh +++ b/fabric-ca/makeDocker.sh @@ -93,7 +93,7 @@ function writeRunFabric { image: hyperledger/fabric-ca-tools environment: - GOPATH=/opt/gopath - command: /bin/bash -c 'sleep 3;/scripts/run-fabric.sh 2>&1 | tee /$RUN_LOGFILE; sleep 99999' + command: /bin/bash -c 'sleep 3;/scripts/run-fabric.sh -l ${LANGUAGE} 2>&1 | tee /$RUN_LOGFILE; sleep 99999' volumes: - ./scripts:/scripts - ./$DATA:/$DATA diff --git a/fabric-ca/scripts/run-fabric.sh b/fabric-ca/scripts/run-fabric.sh index c812f961..3824a7a1 100755 --- a/fabric-ca/scripts/run-fabric.sh +++ b/fabric-ca/scripts/run-fabric.sh @@ -8,9 +8,39 @@ set -e source $(dirname "$0")/env.sh +LANGUAGE=golang +while getopts ":l:" opt; do + case "$opt" in + l) + LANGUAGE=$OPTARG + ;; + : ) + echo "Invalid option: $OPTARG requires an argument" 1>&2 + exit 1; + ;; + esac +done +if [ "$LANGUAGE" = "go" ]; then + LANGUAGE = "golang" +fi +if [[ "$LANGUAGE" != "node" && "$LANGUAGE" != "golang" ]]; then + echo "LANGUAGE = ${LANGUAGE} is not supported" + exit 1; +fi function main { + + logr "LANGUAGE=${LANGUAGE}" + if [ "$LANGUAGE" = "golang" ]; then + CC_SRC_PATH="github.com/hyperledger/fabric-samples/chaincode/abac/go" + elif [ "$LANGUAGE" = "node" ]; then + CC_SRC_PATH="github.com/hyperledger/fabric-samples/chaincode/abac/node" + fi + + logr "CC_SRC_PATH=${CC_SRC_PATH}" + + done=false # Wait for setup to complete and then wait another 10 seconds for the orderer and peers to start @@ -62,7 +92,7 @@ function main { initPeerVars ${PORGS[1]} 1 switchToAdminIdentity logr "Instantiating chaincode on $PEER_HOST ..." - peer chaincode instantiate -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "$POLICY" $ORDERER_CONN_ARGS + peer chaincode instantiate -C $CHANNEL_NAME -n mycc -v 1.0 -l ${LANGUAGE} -c '{"Args":["init","a","100","b","200"]}' -P "$POLICY" $ORDERER_CONN_ARGS # Query chaincode from the 1st peer of the 1st org initPeerVars ${PORGS[0]} 1 @@ -215,7 +245,8 @@ function makePolicy { function installChaincode { switchToAdminIdentity logr "Installing chaincode on $PEER_HOST ..." - peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric-samples/chaincode/abac/go + logr "peer chaincode install -n mycc -v 1.0 -l ${LANGUAGE} -p ${CC_SRC_PATH}" + peer chaincode install -n mycc -v 1.0 -l ${LANGUAGE} -p ${CC_SRC_PATH} } function fetchConfigBlock { diff --git a/fabric-ca/start.sh b/fabric-ca/start.sh index 34dbeb18..7b46546b 100755 --- a/fabric-ca/start.sh +++ b/fabric-ca/start.sh @@ -11,6 +11,28 @@ set -e +# use golang as the default language for chaincode +export LANGUAGE=golang +while getopts ":l:" opt; do + case "$opt" in + l) + LANGUAGE=$OPTARG + ;; + : ) + echo "Invalid option: $OPTARG requires an argument" 1>&2 + exit 1; + ;; + esac +done +if [ "$LANGUAGE" = "go" ]; then + LANGUAGE = "golang" +fi +if [[ "$LANGUAGE" != "node" && "$LANGUAGE" != "golang" ]]; then + echo "LANGUAGE = ${LANGUAGE} is not supported" + exit 1; +fi +echo "start.sh: LANGUAGE = ${LANGUAGE}" + SDIR=$(dirname "$0") source ${SDIR}/scripts/env.sh