devblockchain

Writing your first simple Hyperledger Fabric Chaincode in Go

In this article, we are going to understand the basic terminologies involved while writing a basic chaincode.

Karthik Kamalakannan

Karthik Kamalakannan

Writing your first simple Hyperledger Fabric Chaincode in Go

What is a Chaincode ?

Chaincode is the 'smart contract' of Hyperledger Fabric. It usually controls the business logic and decides who can invoke a particular function in a specific network. In simpler terms, Chaincode is the code that does the logical operations in a Blockchain network.

Is writing Chaincode easy ?

Yes, because the Chaincode shim API makes the logical tasks easier by providing most of the inbuilt functions.

So what are we going to code ?

Always creativity helps to understand things easily. We are considering a super-awesome scenario here.

My friend started a company that sells paintings of artists. And he claims that this is the genuine-first-version of the painter's work. So to prove that, he creates a Blockchain with a smart-contract (yeah, this is our Chaincode).

What does our Chaincode do?

  1. Tell the information of the art with the details of artist.
  2. Tells the current holder of the artwork because everytime someone sells the artwork to the next person, it is assumed to be updated in the Blockchain. (like, when you sell your vehicle to the next person, you update the necessary documents)

Let's get started.

There are three important pieces in Fabric Chaincode,

  1. Init
  2. Invoke
  3. Query

So do they really serve a purpose? Yeah. I'll take a tour into the three 'MASTER' pieces of Fabric Chaincode.

Before that just open your chaincode.go file and let's import few necessary libraries.

import (
  "bytes"
  "encoding/json"
  "fmt"
  "time"
  "strconv"
  "github.com/hyperledger/fabric/core/chaincode/shim"
  "github.com/hyperledger/fabric/core/chaincode/lib/cid"
  sc "github.com/hyperledger/fabric/protos/peer"
)
 
// Represents our smartcontract.
type SmartContract struct {
}
 
type Person struct {
  Id string `json: "id"`
  Class string `json: "class"`
  Name string `json: "name"`
  Email string `json: "email"`
}
 
type Art struct {
  Id string `json: "id"`
  Class string `json: "class"`
  Name string `json: "name"`
  Description string `json: "description"`
  Artist string `json: "artist"`
  Owner string `json: "owner"`
  CreatedAt time.Time `json: "created_at"`
}

Init is called during chaincode instantiation to initialize any data. Upgrade also calls this function to reset or to migrate data. You can bootstrap your ledger data with few initial data. One can create an admin asset that can be a logical validation to perform few operations.

In our scenario, we didn't really want to bootstrap with any initial data. So we are keeping it simple.

// Init function
func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
  return shim.Success(nil)
}

Explanation: Init function takes ChaincodeStubInterface as parameter, which when you call this function using CLI or the ClientSDK, it will be passed into. The function responds back with Peer.Response type which is the most efficient way back to display the message.

Invoke function is like a POST-call which is used in web. Most of the 'Go-do-it' operations are invoked through the invoke function.

func (s *SmartContract) CreateUser(APIstub shim.ChaincodeStubInterface) sc.Response {
  Id := "user-"+utils.RandStringBytes(32) // utils is a custom package. You can write your own too :P
  var user:Person{Class: "Person", Id: Id, Name: args[0], Email: args[1]}
  UserBytes, _ := json.Marshal(user)
  APIstub.PutState(Id, UserBytes)
  return shim.Success(nil)
}

Explanation:

So what's happening here. Line 2 is just a utility function that returns a random string of length 'n'. Here it is 32. Line 3 is where we are creating our Person (a struct we defined in prior) Line 4 uses json package to marshal the Person Go-type to bytes which represents the string notation of the Person object Line 5 pushes the newly created data to our ledger. Line 6, we return the success statement.

Simple right?

The last 'Master'piece of Chaincode is Query Most of the times, we will be in a situation to query the ledger, filter results based on particular logic and so on. Query returns the string which will be an array of results.

func (s *SmartContract) queryUser(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
  if len(args) != 1 {
    return shim.Error("Incorrect number of arguments. Expecting UserID")
  }
  UserBytes, err := APIstub.GetState(args[0])
  if err != nil {
    return shim.Error(err.Error())
  }
  return shim.Success(UserBytes)
}

We can see the Line 2-4 and 6-8 handles the validation part. This helps us to return the appropriate error message when the function fails to execute the expected result. GetState function takes one argument which is the ID that we used in PutState function.

That's all.

To know how I execute and validate on the peer, follow our blog to check out the article 'Setting up your Hyperledger Fabric development environment that helps rapid development'

Last updated: January 23rd, 2024 at 1:50:36 PM GMT+0

Subscribe

Get notified about new updates on Product Management, Building SaaS, and more.

Skcript 10th Anniversary

Consultants for ambitious businesses.

Copyright © 2024 Skcript Technologies Pvt. Ltd.