Writing the Smart Contract
Fabric calls a smart contract as "chaincode".
Programming Language: Go
Fabric allows a smart contract to be written in Javascript and Go. In this tutorial, we will implement the smart contract in Go.
Write the smart contract
Open a terminal, ssh to node-1
ssh node-1.cse.cuhk.edu.hk
Create a working directory for the source code of the smart contract
mkdir ~/fabric_lab_code
cd ~/fabric_lab_code
Create a file main.go
in the working directory
vim main.go
Copy the following code into main.go
. The smart contract has 3 functions: Issue, Report, and Refund.
package main
import (
"bytes"
"encoding/gob"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// Define a struct of a insurance
type Insurance struct {
InsuranceNo string
Issuer string
IsReported bool
IsRefund bool
}
// Define a struct of a Item
type Item struct {
Owner string
itemSerialNo string
Insurances map[string]Insurance
IsRefund bool
}
// Define a struct for smart contract which inhert super class
type InsuranceContract struct {
contractapi.Contract
}
// Utility function to serialize `Item` into byte such that we can persist it
// into the statedb
func Serializeitem(item Item) ([]byte, error) {
b := bytes.Buffer{}
e := gob.NewEncoder(&b)
err := e.Encode(item)
if err != nil {
return nil, err
}
return b.Bytes(), nil
}
// Utility function to deserialize `Item` from byte such taht we can load it
// from the statedb adn ledger
func Deserializeitem(by []byte) (Item, error) {
m := Item{}
b := bytes.Buffer{}
b.Write(by)
d := gob.NewDecoder(&b)
err := d.Decode(&m)
if err != nil {
return m, err
}
return m, nil
}
func main() {
//gob is Go library for (de)-serialization
gob.Register(Item{})
gob.Register(Insurance{})
chaincode, err := contractapi.NewChaincode(new(InsuranceContract))
if err != nil {
fmt.Printf("Error starting Insurance: %s", err)
}
//start this contract and wait for others to invoke its function (e.g., Issue)
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting Insurance: %s", err.Error())
}
}
//for others to invoke to create a new insurance for a Item
func (t *InsuranceContract) Issue(ctx contractapi.TransactionContextInterface, owner, issuer, itemSerialNo, insuranceNo string) error {
var item Item
//access the smart contract's key-value store by GetState
serializeditem, err := ctx.GetStub().GetState(itemSerialNo)
if err != nil {
return fmt.Errorf("Can't read Item %s from statedb\n", itemSerialNo)
}
//Get Item object from ledger, otherwise create one
if serializeditem != nil{
item, err = Deserializeitem(serializeditem)
if err != nil{
return fmt.Errorf("Fail to deserialize Item %s\n", itemSerialNo)
}
//Do some checking
if item.Owner != owner{
return fmt.Errorf("Item %s does not belong to %s\n", itemSerialNo, owner)
}
if item.IsRefund{
return fmt.Errorf("Item %s has already been refunded\n", itemSerialNo)
}
}else{
item = Item{
owner,
itemSerialNo,
make(map[string]Insurance),
false,
}
}
//Attach insurance
insurance := Insurance {
insuranceNo,
issuer,
false,
false,
}
item.Insurances[insuranceNo] = insurance
serializeditem, err = Serializeitem(item)
if err != nil{
return fmt.Errorf("Fail to serialize Item %s\n", itemSerialNo)
}
//Put to the smart contract's key-value store
err = ctx.GetStub().PutState(itemSerialNo, serializeditem)
if err != nil{
return fmt.Errorf("Fail to persist Item %s\n", itemSerialNo)
}
return nil
}
func (t *InsuranceContract) Report(ctx contractapi.TransactionContextInterface, itemSerialNo, insuranceNo string) error {
serializeditem, err := ctx.GetStub().GetState(itemSerialNo)
if err != nil {
return fmt.Errorf("Item %s doesn't exist\n", itemSerialNo)
}
item, err := Deserializeitem(serializeditem)
if err != nil {
return fmt.Errorf("Fail to deserialize Item %s\n", serializeditem)
}
//Do some checking
if item.IsRefund{
return fmt.Errorf("Item %s has already been refunded\n", itemSerialNo)
}
insurance, exist := item.Insurances[insuranceNo]
if !exist {
return fmt.Errorf("Insurance %s doesn't exist\n", insuranceNo)
}
insurance.IsReported = true
item.Insurances[insuranceNo] = insurance
serializeditem, err = Serializeitem(item)
if err != nil{
return fmt.Errorf("Fail to serialize Item %s\n", itemSerialNo)
}
err = ctx.GetStub().PutState(itemSerialNo, serializeditem)
if err != nil{
return fmt.Errorf("Fail to persist Item %s\n", itemSerialNo)
}
return nil
}
func (t *InsuranceContract) Refund(ctx contractapi.TransactionContextInterface, itemSerialNo, insuranceNo string) error {
serializeditem, err := ctx.GetStub().GetState(itemSerialNo)
if err != nil {
return fmt.Errorf("Item %s doesn't exist\n", itemSerialNo)
}
item, err := Deserializeitem(serializeditem)
if err != nil {
return fmt.Errorf("Fail to deserialize Item %s\n", serializeditem)
}
insurance, exist := item.Insurances[insuranceNo]
if !exist {
return fmt.Errorf("Insurance %s doesn't exist\n", insuranceNo)
}
if item.IsRefund {
return fmt.Errorf("Item %s has already been refunded\n", itemSerialNo)
}
if !insurance.IsReported {
return fmt.Errorf("Insurance %s is not reported\n", insuranceNo)
}
if insurance.IsRefund {
return fmt.Errorf("Insurance %s is refunded\n", insuranceNo)
}
//set Item and Insurance = refunded
item.IsRefund = true
insurance.IsRefund = true
item.Insurances[insuranceNo] = insurance
serializeditem, err = Serializeitem(item)
if err != nil {
return fmt.Errorf("Fail to serialize Item %s\n", itemSerialNo)
}
err = ctx.GetStub().PutState(itemSerialNo, serializeditem)
if err != nil {
return fmt.Errorf("Fail to persist Item %s\n", itemSerialNo)
}
return nil
}
Save main.go
.
Copy the following code into go.mod
.
module smartcontract
go 1.15
require github.com/hyperledger/fabric-contract-api-go v1.1.1
require (
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/spec v0.20.9 // indirect
github.com/gobuffalo/envy v1.10.2 // indirect
github.com/gobuffalo/packd v1.0.2 // indirect
github.com/hyperledger/fabric-chaincode-go v0.0.0-20230731094759-d626e9ab09b9 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/grpc v1.59.0 // indirect
)
Save go.mod
.
Execute the following command to tidy the Go module.
go mod tidy
Last updated