Hyperledger Fabric | Understand chaincode

Hyperledger Fabric | Understand chaincode

In our previous posts, we talk about chaincode and state database but we haven't covered anything on coding chaincodes and this post will touch base on that and explain how chaincode looks like and how you can create chaincode in hyperledger fabric.

Very first thing, you may be asking, Is it easy to write and understand chaincodes in hyperledger fabric ?, answer to this question is yes, it is simple to understand and write chaincodes but you need to understand the basics first.

How many ways to write chaincode in hyperledger fabric ?

Right now, you can write chaincode in Go and nodejs. Java will be supported in upcoming version.

In this post, we will talk about chaincode in Go language. Go is very powerful language from Google.

Let's get to work and understand chaincode.

At high level, you can categories chaincode in below sections.

  1. Import section
  2. Struct section
  3. Init function section
  4. Invoke function section
  5. Custom functions section
  6. Main function section

 

Import Section

As the name suggests, this section contains the details about import statements as shown below.

package main

import (

"fmt"

"github.com/hyperledger/fabric/core/chaincode/shim"

)

Here

fmt - contains Println for debugging/logging

github.com/hyperledger/fabric/core/chaincode/shim - contains the definition for the chaincode interface and the chaincode stub, which you will need to interact with the ledger, as we described in the Chaincode Key APIs section

 

Struct Section

In this section, you define the Chaincode type. In below example, TestChaincode is type that will implement the chaincode functions, as shown in the following snippet.

type TestChaincode struct {

}

 

Init function Section

In this section, Init function of Chaincode interface is implemented. Init is called during the chaincode instantiation to initialize data required by the application.

func (t *TestChaincode) Init(stub shim.ChainCodeStubInterface) peer.Response {

//write required logic here

return shim.Success(nil)

//Below statement can be used in case of error

//return shim.Error("Error Message")

}

 

Invoke function Section

In this section, Invoke function of Chaincode is implemented. Invoke method, which gets called when a transaction is proposed by a client application.

func (t *TestChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {

// Extract the function and args from the transaction proposal

fn, args := stub.GetFunctionAndParameters()

var result string

var err error

if fn == "setValue" {

result, err = setValue(stub, args)

} else if  fn == "getValue" {

result, err = getValue(stub, args)

}

if err != nil { //Failed to get function and/or arguments from transaction proposal

return shim.Error(err.Error())

}

// Return the result as success payload

return shim.Success([]byte(result))

}

As shown above, GetFunctionAndParameters function from shim package helps to get the function name and arguments to that function. In this example, SDK can either call setValue() function or getValue() function with required arguments.

function name is getting saved in fn and arguments in args. Later you can check the function name and based upon that invoke custom functions such as setValue or getValue.

 

Custom function Section

As shown above, Invoke function can further call custom defined functions. These custom functions can be defined as per the requirement.

Below is definition of setValue() function, that set the key and value.

func setValue(stub shim.ChaincodeStubInterface, args []string) (string, error) {

if len(args) != 2 {

return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")

}

err := stub.PutState(args[0], []byte(args[1]))

if err != nil {

return "", fmt.Errorf("Failed to set values: %s", args[0])

}

return args[1], nil

}

 

The setValue method will modify the world state to include the key/value pair specified. If the key exists, it will override the value with the new one, using the PutState method; otherwise, a new asset will be created with the specified value.

Below is definition of getValue() function, that provide the value based upon key.

func getValue(stub shim.ChaincodeStubInterface, args []string) (string, error) {

if len(args) != 1 {

return "", fmt.Errorf("Incorrect arguments. Expecting a key")

}

value, err := stub.GetState(args[0])

if err != nil {

return "", fmt.Errorf("Failed to get value: %s with error: %s", args[0], err)

}

if value == nil {

return "", fmt.Errorf("Value not found: %s", args[0])

}

return string(value), nil

}

 

The getValue method will attempt to retrieve the value for the specified key. If key is not passed in the arguments then error is returned and if key is there then GetState function is called and that in turn query the state database and get the value for that key.

 

Main function Section

Last section is main function section and this is the starting point of chaincode.

func main() {

err := shim.Start(new(TestChaincode))

if err != nil {

fmt.Println("Could not start TestChaincode")

} else {

fmt.Println("TestChaincode successfully started")

}

}

No Comments

Post a Reply

Inquire Now
close slider