The chaincode in ngo.js supports donors donating funds to an NGO (i.e. non-profit), while tracking the spending of those funds and the allocation of the spending records against each donation. Donors are therefore able to track how their donations are being spent. The Fabric Chaincode is written in Node.js.
To run and test this application locally you will need a Hyperledger Fabric network running, either locally on your laptop or on an EC2 instance.
Follow the instructions here to install the fabric-samples repo, and download the Fabric binaries and Docker images. This will download the latest version of Fabric:
https://hyperledger-fabric.readthedocs.io/en/latest/install.html
Now, we need to install a slightly different version of fabric-samples. The default fabric-samples repo above does not start a fabric-ca, which we need to run our application.
The fabric-samples version we run below has been modified to start a fabric-ca.
cd ~
mv fabric-samples fabric-samples-1.3
git clone https://github.com/mahoney1/fabric-samples.git
cd fabric-samples
git checkout multi-org
cp -R ../fabric-samples-1.3/bin .
cp -R ../fabric-samples-1.3/config .
Change to the fabric-samples directory. We will use the first network so change to this directory:
cd first-network
The first time you start the network you should generate the keys/certs:
./byfn.sh generate
Then start the network. The -a
argument below will start fabric-ca in addition to the other Fabric components:
./byfn.sh up -s couchdb -l node -a
Now, how do we get the chaincode into the Fabric network so we can install and instantiate it?
We'll install the chaincode from the CLI container. This container maps a chaincode
folder from your local host
into the container, so we simply need to copy our chaincode to this folder.
Clone this repo, which contains the chaincode:
cd ~
git clone https://github.com/aws-samples/non-profit-blockchain.git
In your terminal window, change to the directory containing the chaincode:
cd ~
cd non-profit-blockchain/ngo-chaincode
Update the REPODIR variable in the statements below to match the location of your fabric-samples repo, and then execute all the commands in your terminal window:
If installing on Mac:
cd ~/Documents/apps/non-profit-blockchain/ngo-chaincode
REPODIR=/Users/edgema/Documents/apps/fabric-samples
rm $REPODIR/chaincode/ngo/*
mkdir -p $REPODIR/chaincode/ngo
cp src/package.json $REPODIR/chaincode/ngo
cp src/ngo.js $REPODIR/chaincode/ngo
If installing on an EC2 instance:
cd ~/non-profit-blockchain/ngo-chaincode
REPODIR=/home/ubuntu/fabric-samples
rm $REPODIR/chaincode/ngo/*
mkdir -p $REPODIR/chaincode/ngo
cp src/package.json $REPODIR/chaincode/ngo
cp src/ngo.js $REPODIR/chaincode/ngo
To confirm that this is now visible inside your CLI container, exec into the container:
docker exec -it cli bash
Inside the CLI container:
# ls /opt/gopath/src/github.com/chaincode/ngo
package.json ngo.js
You can repeat this process whenever you update the chaincode.
Install the chaincode on the peer nodes. You'll install it to each peer node separately by setting the appropriate ENV variables
before doing a peer chaincode install
. This simply copies the chaincode to the peer node. If you are upgrading the chaincode,
i.e. creating a new version, you 'll need to increment the version number in the statements below (the -v option).
In the byfn network there are 4 peer nodes. You'll copy to each one individually by changing the ENV variables as appropriate
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
peer chaincode install -n ngo -v 7.0 -l node -p /opt/gopath/src/github.com/chaincode/ngo
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer1.org1.example.com:7051
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt
peer chaincode install -n ngo -v 7.0 -l node -p /opt/gopath/src/github.com/chaincode/ngo
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer0.org2.example.com:7051
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
peer chaincode install -n ngo -v 7.0 -l node -p /opt/gopath/src/github.com/chaincode/ngo
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer1.org2.example.com:7051
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt
peer chaincode install -n ngo -v 7.0 -l node -p /opt/gopath/src/github.com/chaincode/ngo
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
peer chaincode upgrade -C mychannel -n ngo -l node -v 7.0 -c '{"Args":["init"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')" -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
peer chaincode install -n ngo -v 7.0 -l node -p /opt/gopath/src/github.com/chaincode/ngo
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer1.org1.example.com:7051
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt
peer chaincode install -n ngo -v 7.0 -l node -p /opt/gopath/src/github.com/chaincode/ngo
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer0.org2.example.com:7051
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
peer chaincode install -n ngo -v 7.0 -l node -p /opt/gopath/src/github.com/chaincode/ngo
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer1.org2.example.com:7051
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt
peer chaincode install -n ngo -v 7.0 -l node -p /opt/gopath/src/github.com/chaincode/ngo
The chaincode only needs to be instantiated on a single peer node. Since we are using TLS we pass the TLS cert, which is conveniently stored in the CLI.
If you are upgrading the chaincode, you'll use the upgrade
command below instead of the instantiated
command:
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
peer chaincode instantiate -C mychannel -n ngo -l node -v 7.0 -c '{"Args":["init"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')" -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
peer chaincode upgrade -C mychannel -n ngo -l node -v 7.0 -c '{"Args":["init"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')" -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
Check that the chaincode was instantiated on the channel. You can run this after either an upgrade
or an instantiate
# peer chaincode list --instantiated -C mychannel -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
Get instantiated chaincodes on channel mychannel:
Name: mycc, Version: 7.0, Path: /opt/gopath/src/github.com/chaincode/chaincode_example02/node/, Escc: escc, Vscc: vscc
Name: ngo, Version: 7.0, Path: /opt/gopath/src/github.com/chaincode/ngo, Escc: escc, Vscc: vscc
To invoke transaction must have installed the chaincode on the two peers used in the first-network: peer0.org1 and peer0.org2.
The statements for querying the chaincode and invoking transactions can be found in ./test-chaincode.sh
docker exec -it cli bash
Find the list of CouchDB databases:
curl couchdb0:5984/_all_dbs
Check the docs in the db:
curl couchdb0:5984/mychannel_ngo/_all_docs
curl -X POST 'http://couchdb0:5984/mychannel_ngo/_find' -H 'Content-Type: application/json' --data '{"selector": {"docType": "donor"}}'
curl -X POST 'http://couchdb0:5984/mychannel_ngo/_find' -H 'Content-Type: application/json' --data '{"selector": {"docType": "donation", "ngo":"1234"}}'
./byfn.sh down
docker rm -f $(docker ps -aq)
docker network prune
Chaincode endorsed by multiple endorsing peers may throw this error:
Error: could not assemble transaction: ProposalResponsePayloads do not match - proposal response: version:1 response:<status:200 > payload: ...
The usual reason for this is different values in the write set produced by different peers. This can arise if your
chaincode tries to generate a value, for example, if you use new Date()
to assign a timestamp to a field in your
chaincode. The date on each endorsing peer node could be slightly different, which causes the Fabric validation process to fail.
Another cause could be trying to generate a sequence number or unique ID (such as a UUID) in chaincode. Again, the results
may differ on the endorsing peers and the transaction proposal will fail the validation checks.