Certificate Credentialing Service, Writing Chaincode: Blog 2

Muhammed Musthafa Shahal V
Impelsys
Published in
9 min readNov 3, 2023

Hi Folks, This is a continuation of the “Certificate Credentialing Service using Blockchain Technology-Hyperledger Fabric“ blog posted in September written by Anubhav R. To understand the basics of Hyperledger Fabric and our business requirements, I highly recommend reading our previous article and doing the test network setup. In the previous blog, we have set up a test network, and our test network is up and running.

If you do not wish to download all the fabric samples and set them up, you can use managed blockchain networks provided by Amazon or any other tech giant, however, you have to pay for their service. Follow along if you have your own network or if you only need to learn about blockchains; otherwise, set up the test network and come back.

Let’s begin, What is a Chaincode?

A Chaincode also referred to as a “smart contract” in other blockchain platforms, is responsible for defining and executing the business logic of transactions and governing the state of the ledger.

Our Requirement

To write a chaincode that can create a certificate, store it in the ledger, retrieve certificate details upon providing the certificate number, and display all certificates in the ledger.

Let’s start writing the Chaincode

As mentioned earlier, this blog is the continuation of the “Certificate Credentialing Service using Blockchain technology-Hyperledger Fabric“ blog. If you do not have a test network setup, please follow the earlier blog, complete the test network setup, and then come back here.

Now, head over to the fabric-samples/chaincode/fabcar/javascript/lib/fabcar.js file inside our test network, remove all the code inside “index.js” and let us start from scratch.

Firstly, start by importing the fabric contract API. Use the code shown below. Also, use the “contract API” or “fabric shim” API; both of these APIs are critical components that provide a bridge between the system chaincode and the underlying Hyperledger Fabric platform.

'use strict';

const { Contract } = require('fabric-contract-api');

Next, create a class called FabCar and export it, refer to the below code. If required change the class name but you have to change the test network configuration and the chaincode SDK as well. As I’m using the test network provided by the Hyperledger Foundation, I’m keeping the same name, and changing the entire name is out the of scope of this blog.

class FabCar extends Contract {

}

module.exports = FabCar;

Now create the chaincode functions as per our business requirements and create 4 functions inside the Fabcar Class in the same order.

  1. Init Function
  2. Create a Certificate Function
  3. Query Certificate Function
  4. Query All Certificate Function

Init Function

As a first step, we will proceed with the Init function. If necessary, you can leave the Init function empty. When the opportunity arises for Chaincode deployment, this method will be executed automatically to ensure successful deployment. It is a good practice to keep some code or initialize some values in the Init method to determine if the deployment has been successful or not.

To write data into the ledger, you can use the “putState” method. This method will update the ledger and write the key-value pair into it.

In this case, I will assign a dummy certificate value as the initial value.

async initLedger(ctx) {
console.info('============= START : Initialize Ledger ===========');
const certificate =
{
"name": "Blackacre",
"email": "Blackacre@aha.com",
"course": "BLS Provider",
"orgCode": "TC0101",
"courseCode": "2022-1234",
"completionDate": "10-10-2023",
"expiryDate": "10-10-2025"
}
const key ="CERT123";
await ctx.stub.putState(key, Buffer.from(JSON.stringify(certificate)));
console.info('Added <--> ', certificate);
console.info('============= END : Initialize Ledger ===========');
}

Create Certificate Function

Next, write the Create Certificate Function which accepts the certificate number and the certificate details as parameters.

async createCertificate(ctx, certNO, name, email, course, orgCode, courseCode, completionDate, expiryDate) {
console.info('============= START : Create Certificate ===========');

const certificateData = {
name,
email,
course,
orgCode,
courseCode,
completionDate,
expiryDate,
};

await ctx.stub.putState(certNO, Buffer.from(JSON.stringify(certificateData)));
console.info('============= END : Create Certificate ===========');
}

Query Certificate Function

Now, write the Query Certificate Function. This function accepts the certificate number as a parameter and returns the matching certificate data. If no matching data is found, the function returns the value ‘No data exists’. The “putState” method can be used to write a certificate. In order to retrieve the state of a certificate from the ledger, the “getState” method can be used. This method retrieves the state from the ledger for a particular key.

async queryCertificate(ctx, certNO) {
const certificateAsBytes = await ctx.stub.getState(certNO); // get the certificate from chaincode state
if (!certificateAsBytes || certificateAsBytes.length === 0) {
throw new Error(`${certNO} does not exist`);
}
console.log(certificateAsBytes.toString());
return certificateAsBytes.toString();
}

Query All Certificate Function

Finally, use the Query All Certificates Function. This function does not accept any parameters but will return all the certificate information available in the database.

async queryAllCertificates(ctx) {
const startKey = '';
const endKey = '';
const allResults = [];
for await (const {key, value} of ctx.stub.getStateByRange(startKey, endKey)) {
const strValue = Buffer.from(value).toString('utf8');
let record;
try {
record = JSON.parse(strValue);
} catch (err) {
console.log(err);
record = strValue;
}
allResults.push({ Key: key, Record: record });
}
console.info(allResults);
return JSON.stringify(allResults);
}

Below is the consolidated chaincode block :

'use strict';

const {
Contract
} = require('fabric-contract-api');

class FabCar extends Contract {
async initLedger(ctx) {
console.info('============= START : Initialize Ledger ===========');
const certificate = {
"name": "Blackacre",
"email": "Blackacre@aha.com",
"course": "BLS Provider",
"orgCode": "TC0101",
"courseCode": "2022-1234",
"completionDate": "10-10-2023",
"expiryDate": "10-10-2025"
}

await ctx.stub.putState(CERT123, Buffer.from(JSON.stringify(certificate)));
console.info('Added <--> ', certificate);
console.info('============= END : Initialize Ledger ===========');
}

async createCertificate(ctx, certNO, name, email, course, orgCode, courseCode, completionDate, expiryDate) {
console.info('============= START : Create Certificate ===========');

const certificateData = {
name,
email,
course,
orgCode,
courseCode,
completionDate,
expiryDate,
};

await ctx.stub.putState(certNO, Buffer.from(JSON.stringify(certificateData)));
console.info('============= END : Create Certificate ===========');
}

async queryCertificate(ctx, certNO) {
const certificateAsBytes = await ctx.stub.getState(certNO); // get the certificate from chaincode state
if (!certificateAsBytes || certificateAsBytes.length === 0) {
throw new Error(`${certNO} does not exist`);
}
console.log(certificateAsBytes.toString());
return certificateAsBytes.toString();
}

async queryAllCertificates(ctx) {
const startKey = '';
const endKey = '';
const allResults = [];
for await (const {
key,
value
}
of ctx.stub.getStateByRange(startKey, endKey)) {
const strValue = Buffer.from(value).toString('utf8');
let record;
try {
record = JSON.parse(strValue);
} catch (err) {
console.log(err);
record = strValue;
}
allResults.push({
Key: key,
Record: record
});
}
console.info(allResults);
return JSON.stringify(allResults);
}
}

module.exports = FabCar;

Deploying the Chaincode

We have now finished writing the chaincode. Let us now deploy the chaincode to the test network and execute some transactions. I am using the Hyperledger Foundation’s test network. Start the network by running the “startFabric.sh” script inside the fabcar folder. JavaScript is used as the chaincode language.

The script will deploy the Go version of chaincode by default if you did not mention javascript. If you would like to use any other programming language, please indicate it.

fabric-samples/fabcar$ ./startFabric.sh javascript

The “startFabric.sh” script will start the network and deploy our chaincode. It will create two organizations and one peer for each. Then it will create a test channel, package our chaincode, and deploy it. Refer to the below Chain code. It provides the channel name, chaincode name, chaincode language, etc.

After packaging the chaincode, it will wait for approval from each peer in the network. Once it’s approved, it will deploy the chaincode.

After the execution is complete, confirm your network is up and running by executing the following command:

fabric-samples/fabcar$ docker ps

The command will list all the containers running.

The test network by default uses the Couchdb as the ledger in the BlockChain. In the containers list, you can see two instances of the CouchDB database created by the Fabric test network for each organization on PORTs 5984 and 7984, respectively. To confirm your chaincode is successfully deployed head-over to any of the Couchdb database ports mentioned, use the result provided by the “docker ps” command.

I’m using the first instance of Couchdb running on port 5984. Head over to “http://localhost:5984/_utils” and login using the default username “admin” and password “adminpw”.

Go inside “my channel” fabcar ledger. This is the ledger created by the network for us. Inside that, you can see “CERT123.” Click on it, and then you will be able to see the entire certificate details that we mentioned in the chaincode Init Function. This indicates that our chaincode was successfully deployed and the certificate was added to the ledger.

Creating the Admin & End User

After successfully deploying our chaincode, it is now time to invoke it and execute transactions. Rather than writing the code on the command line for executing transactions, Hyperledger Fabric provides SDKs for each programming language for automating transaction execution. First, we will create an admin user, and then, utilizing the admin user, we will create an end user to interact with the chaincode.

1. Create an Admin user for getting access to the network

Head over to “fabric-samples/fabcar/javascript/” and run “npm install” to install the dependencies.

Now run the “enrollAdmin.js” file using node.

You have now successfully created an admin.

2. Create an End user for interacting with the chaincode

After creating the admin, we can set up a user to interact with the chaincode. To do this, run the “registerUser.js” file using node. If required to change the username, edit the file and replace the default username “appUser” with the name you desire.

Invoking the Chaincode to create a certificate.

When you create a transaction that requires writing data into the ledger, you invoke the chaincode. Invoking the chaincode for submitting a transaction requires editing the “invoke.js” file. Simply replace the line “await contract.submitTransaction()” as follows, or provide the certificate values of your choice.

await contract.submitTransaction('createCertificate', 'CERT111', 'Adam John', 'adamjohn@gmail.com', 'test provider', 'TEST001', '2023-2024','18-11-2023','01-01-2028');

Once done, save the file and run the “invoke.js” file to submit the transaction to the blockchain. It will sum up the transaction. When the transaction has been endorsed, you will receive the following response.

Once the transaction has been submitted, you will be able to view the newly created certificate when you refresh the Couchdb website.

Query Chaincode to retrieve certificate

The query method is used when you want to receive data from the blockchain.

In the “query.js” file, replace the “const result = await contract.evaluateTransaction():” with the following to query all certificates:

const result = await contract.evaluateTransaction('queryAllCertificates');

Save the file and run it using “node query.js”. All the certificates will be printed on the console.

To query a particular certificate — give the queryCertificate function and a key as parameters.

const result = await contract.evaluateTransaction('queryCertificate','CERT111');

Save the file and run it using “node query.js”. If the certificate is present with the key you provided you will see the following response.

If the certificate does not exist with the key you provided, you will get an error message showing that the “key does not exist”.

Conclusion:

We have successfully written our first chaincode and deployed it to our test network. Happy Coding, and Good Luck.

--

--