Module 5: Advanced Topics and Use Cases
5.3 Real-World Applications and Case Studies
Hyperledger Fabric has been deployed across various industries to solve real-world business problems. This section explores several case studies and applications that demonstrate the practical value of Hyperledger Fabric.
Supply Chain Management
Supply chain management is one of the most prominent use cases for blockchain technology, and Hyperledger Fabric provides an ideal platform for implementing supply chain solutions.
IBM Food Trust
IBM Food Trust is a production blockchain network built on Hyperledger Fabric that connects participants across the food supply chain through a permissioned, permanent, and shared record of food system data.
Key Features: - Complete food traceability from farm to consumer - Improved food safety through rapid identification of contamination sources - Reduced waste by optimizing the supply chain - Enhanced sustainability through transparent sourcing
Technical Implementation: - Multi-organization network with growers, processors, distributors, retailers, and regulators - Private data collections for sensitive business information - Smart contracts for automated compliance and certification - Integration with IoT devices for real-time tracking
Architecture Diagram:
+-------------------+ +-------------------+ +-------------------+
| | | | | |
| Producers | | Processors | | Distributors |
| | | | | |
+--------+----------+ +--------+----------+ +--------+----------+
| | |
| | |
v v v
+-------------------------------------------------------------------+
| |
| Hyperledger Fabric Network |
| |
+-------------------------------------------------------------------+
^ ^ ^
| | |
| | |
+--------+----------+ +--------+----------+ +--------+----------+
| | | | | |
| Retailers | | Regulators | | Consumers |
| | | | | |
+-------------------+ +-------------------+ +-------------------+
Sample Chaincode for Product Tracking:
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// FoodProduct represents a food product in the supply chain
type FoodProduct struct {
ID string `json:"id"`
ProductType string `json:"productType"`
Producer string `json:"producer"`
ProductionDate time.Time `json:"productionDate"`
ExpirationDate time.Time `json:"expirationDate"`
BatchNumber string `json:"batchNumber"`
CurrentLocation string `json:"currentLocation"`
Status string `json:"status"`
CertificateIDs []string `json:"certificateIds"`
History []Event `json:"history"`
}
// Event represents a supply chain event
type Event struct {
Timestamp time.Time `json:"timestamp"`
Location string `json:"location"`
Action string `json:"action"`
Actor string `json:"actor"`
Details string `json:"details"`
}
// Certificate represents a certification or compliance document
type Certificate struct {
ID string `json:"id"`
Type string `json:"type"`
Issuer string `json:"issuer"`
IssuedTo string `json:"issuedTo"`
IssuedDate time.Time `json:"issuedDate"`
ExpiryDate time.Time `json:"expiryDate"`
DocumentHash string `json:"documentHash"`
}
// SmartContract provides functions for managing the food supply chain
type SmartContract struct {
contractapi.Contract
}
// CreateProduct creates a new food product in the ledger
func (s *SmartContract) CreateProduct(ctx contractapi.TransactionContextInterface, id string, productType string, producer string, productionDate string, expirationDate string, batchNumber string) error {
exists, err := s.ProductExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the product %s already exists", id)
}
// Parse dates
prodDate, err := time.Parse(time.RFC3339, productionDate)
if err != nil {
return fmt.Errorf("invalid production date format: %v", err)
}
expDate, err := time.Parse(time.RFC3339, expirationDate)
if err != nil {
return fmt.Errorf("invalid expiration date format: %v", err)
}
// Get client identity for the producer
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Create the product
product := FoodProduct{
ID: id,
ProductType: productType,
Producer: producer,
ProductionDate: prodDate,
ExpirationDate: expDate,
BatchNumber: batchNumber,
CurrentLocation: producer,
Status: "PRODUCED",
CertificateIDs: []string{},
History: []Event{
{
Timestamp: time.Now(),
Location: producer,
Action: "PRODUCED",
Actor: clientID,
Details: fmt.Sprintf("Product %s was produced", id),
},
},
}
productJSON, err := json.Marshal(product)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, productJSON)
}
// ReadProduct returns the product stored in the world state with given id
func (s *SmartContract) ReadProduct(ctx contractapi.TransactionContextInterface, id string) (*FoodProduct, error) {
productJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if productJSON == nil {
return nil, fmt.Errorf("the product %s does not exist", id)
}
var product FoodProduct
err = json.Unmarshal(productJSON, &product)
if err != nil {
return nil, err
}
return &product, nil
}
// UpdateProductLocation updates the current location of a product
func (s *SmartContract) UpdateProductLocation(ctx contractapi.TransactionContextInterface, id string, newLocation string) error {
product, err := s.ReadProduct(ctx, id)
if err != nil {
return err
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Update the product location
product.CurrentLocation = newLocation
// Add event to history
product.History = append(product.History, Event{
Timestamp: time.Now(),
Location: newLocation,
Action: "LOCATION_UPDATE",
Actor: clientID,
Details: fmt.Sprintf("Product moved to %s", newLocation),
})
productJSON, err := json.Marshal(product)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, productJSON)
}
// UpdateProductStatus updates the status of a product
func (s *SmartContract) UpdateProductStatus(ctx contractapi.TransactionContextInterface, id string, newStatus string) error {
product, err := s.ReadProduct(ctx, id)
if err != nil {
return err
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Update the product status
product.Status = newStatus
// Add event to history
product.History = append(product.History, Event{
Timestamp: time.Now(),
Location: product.CurrentLocation,
Action: "STATUS_UPDATE",
Actor: clientID,
Details: fmt.Sprintf("Product status changed to %s", newStatus),
})
productJSON, err := json.Marshal(product)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, productJSON)
}
// AddCertificate adds a new certificate to the ledger
func (s *SmartContract) AddCertificate(ctx contractapi.TransactionContextInterface, id string, certType string, issuer string, issuedTo string, issuedDate string, expiryDate string, documentHash string) error {
exists, err := s.CertificateExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the certificate %s already exists", id)
}
// Parse dates
issueDate, err := time.Parse(time.RFC3339, issuedDate)
if err != nil {
return fmt.Errorf("invalid issued date format: %v", err)
}
expDate, err := time.Parse(time.RFC3339, expiryDate)
if err != nil {
return fmt.Errorf("invalid expiry date format: %v", err)
}
// Create the certificate
certificate := Certificate{
ID: id,
Type: certType,
Issuer: issuer,
IssuedTo: issuedTo,
IssuedDate: issueDate,
ExpiryDate: expDate,
DocumentHash: documentHash,
}
certificateJSON, err := json.Marshal(certificate)
if err != nil {
return err
}
return ctx.GetStub().PutState(fmt.Sprintf("CERT_%s", id), certificateJSON)
}
// LinkCertificateToProduct links a certificate to a product
func (s *SmartContract) LinkCertificateToProduct(ctx contractapi.TransactionContextInterface, productID string, certificateID string) error {
// Check if product exists
product, err := s.ReadProduct(ctx, productID)
if err != nil {
return err
}
// Check if certificate exists
exists, err := s.CertificateExists(ctx, certificateID)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the certificate %s does not exist", certificateID)
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Add certificate to product
product.CertificateIDs = append(product.CertificateIDs, certificateID)
// Add event to history
product.History = append(product.History, Event{
Timestamp: time.Now(),
Location: product.CurrentLocation,
Action: "CERTIFICATE_LINKED",
Actor: clientID,
Details: fmt.Sprintf("Certificate %s linked to product", certificateID),
})
productJSON, err := json.Marshal(product)
if err != nil {
return err
}
return ctx.GetStub().PutState(productID, productJSON)
}
// GetProductHistory returns the history of a product
func (s *SmartContract) GetProductHistory(ctx contractapi.TransactionContextInterface, id string) ([]Event, error) {
product, err := s.ReadProduct(ctx, id)
if err != nil {
return nil, err
}
return product.History, nil
}
// QueryProductsByType returns all products of a specific type
func (s *SmartContract) QueryProductsByType(ctx contractapi.TransactionContextInterface, productType string) ([]*FoodProduct, error) {
queryString := fmt.Sprintf(`{"selector":{"productType":"%s"}}`, productType)
return s.getQueryResultForQueryString(ctx, queryString)
}
// QueryProductsByProducer returns all products from a specific producer
func (s *SmartContract) QueryProductsByProducer(ctx contractapi.TransactionContextInterface, producer string) ([]*FoodProduct, error) {
queryString := fmt.Sprintf(`{"selector":{"producer":"%s"}}`, producer)
return s.getQueryResultForQueryString(ctx, queryString)
}
// QueryProductsByStatus returns all products with a specific status
func (s *SmartContract) QueryProductsByStatus(ctx contractapi.TransactionContextInterface, status string) ([]*FoodProduct, error) {
queryString := fmt.Sprintf(`{"selector":{"status":"%s"}}`, status)
return s.getQueryResultForQueryString(ctx, queryString)
}
// ProductExists returns true when product with given ID exists in world state
func (s *SmartContract) ProductExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
productJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return productJSON != nil, nil
}
// CertificateExists returns true when certificate with given ID exists in world state
func (s *SmartContract) CertificateExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
certificateJSON, err := ctx.GetStub().GetState(fmt.Sprintf("CERT_%s", id))
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return certificateJSON != nil, nil
}
// Helper function to get client identity
func (s *SmartContract) getClientIdentity(ctx contractapi.TransactionContextInterface) (string, error) {
clientID, err := ctx.GetClientIdentity().GetID()
if err != nil {
return "", fmt.Errorf("failed to get client identity: %v", err)
}
return clientID, nil
}
// Helper function for rich queries
func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*FoodProduct, error) {
resultsIterator, err := ctx.GetStub().GetQueryResult(queryString)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
var products []*FoodProduct
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var product FoodProduct
err = json.Unmarshal(queryResponse.Value, &product)
if err != nil {
return nil, err
}
products = append(products, &product)
}
return products, nil
}
func main() {
chaincode, err := contractapi.NewChaincode(&SmartContract{})
if err != nil {
fmt.Printf("Error creating food supply chain chaincode: %v\n", err)
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting food supply chain chaincode: %v\n", err)
}
}
Trade Finance
Trade finance is another area where Hyperledger Fabric has been successfully deployed to streamline processes, reduce fraud, and increase transparency.
we.trade
we.trade is a blockchain-based platform built on Hyperledger Fabric that simplifies international trade by connecting banks, buyers, and sellers.
Key Features: - Digital trade contracts with automated enforcement - Bank payment undertaking (BPU) to reduce risk - Real-time tracking of trade transactions - Simplified KYC and regulatory compliance
Technical Implementation: - Multi-bank consortium network - Smart contracts for trade agreements and payment obligations - Integration with existing banking systems - Regulatory reporting capabilities
Process Flow:
- Buyer and seller negotiate terms and create a digital trade agreement
- Buyer's bank issues a BPU, guaranteeing payment
- Seller ships goods and uploads proof of shipment
- Smart contract verifies conditions are met
- Payment is automatically triggered based on agreed terms
Sample Chaincode for Letter of Credit:
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// LetterOfCredit represents a letter of credit in trade finance
type LetterOfCredit struct {
ID string `json:"id"`
Applicant string `json:"applicant"` // Buyer
ApplicantBank string `json:"applicantBank"` // Buyer's bank
Beneficiary string `json:"beneficiary"` // Seller
BeneficiaryBank string `json:"beneficiaryBank"` // Seller's bank
IssuanceDate time.Time `json:"issuanceDate"`
ExpiryDate time.Time `json:"expiryDate"`
Amount float64 `json:"amount"`
Currency string `json:"currency"`
DocumentsRequired []string `json:"documentsRequired"`
Status string `json:"status"`
ShipmentDate time.Time `json:"shipmentDate"`
DocumentsSubmitted []string `json:"documentsSubmitted"`
History []Event `json:"history"`
}
// Event represents a trade finance event
type Event struct {
Timestamp time.Time `json:"timestamp"`
Action string `json:"action"`
Actor string `json:"actor"`
Details string `json:"details"`
}
// Document represents a trade document
type Document struct {
ID string `json:"id"`
Type string `json:"type"`
IssuedBy string `json:"issuedBy"`
IssuedDate time.Time `json:"issuedDate"`
Description string `json:"description"`
FileHash string `json:"fileHash"`
Status string `json:"status"`
}
// SmartContract provides functions for managing trade finance
type SmartContract struct {
contractapi.Contract
}
// IssueLOC issues a new letter of credit
func (s *SmartContract) IssueLOC(ctx contractapi.TransactionContextInterface, id string, applicant string, applicantBank string, beneficiary string, beneficiaryBank string, issuanceDate string, expiryDate string, amount float64, currency string, documentsRequired string) error {
exists, err := s.LOCExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the letter of credit %s already exists", id)
}
// Parse dates
issueDate, err := time.Parse(time.RFC3339, issuanceDate)
if err != nil {
return fmt.Errorf("invalid issuance date format: %v", err)
}
expDate, err := time.Parse(time.RFC3339, expiryDate)
if err != nil {
return fmt.Errorf("invalid expiry date format: %v", err)
}
// Parse documents required
var docs []string
err = json.Unmarshal([]byte(documentsRequired), &docs)
if err != nil {
return fmt.Errorf("invalid documents required format: %v", err)
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Verify that the client is from the applicant bank
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
if clientMSPID != applicantBank {
return fmt.Errorf("client from %s is not authorized to issue LOC for %s", clientMSPID, applicantBank)
}
// Create the letter of credit
loc := LetterOfCredit{
ID: id,
Applicant: applicant,
ApplicantBank: applicantBank,
Beneficiary: beneficiary,
BeneficiaryBank: beneficiaryBank,
IssuanceDate: issueDate,
ExpiryDate: expDate,
Amount: amount,
Currency: currency,
DocumentsRequired: docs,
Status: "ISSUED",
History: []Event{
{
Timestamp: time.Now(),
Action: "ISSUED",
Actor: clientID,
Details: fmt.Sprintf("Letter of Credit %s was issued", id),
},
},
}
locJSON, err := json.Marshal(loc)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, locJSON)
}
// ReadLOC returns the letter of credit stored in the world state with given id
func (s *SmartContract) ReadLOC(ctx contractapi.TransactionContextInterface, id string) (*LetterOfCredit, error) {
locJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if locJSON == nil {
return nil, fmt.Errorf("the letter of credit %s does not exist", id)
}
var loc LetterOfCredit
err = json.Unmarshal(locJSON, &loc)
if err != nil {
return nil, err
}
return &loc, nil
}
// SubmitDocument submits a trade document for a letter of credit
func (s *SmartContract) SubmitDocument(ctx contractapi.TransactionContextInterface, locID string, docID string, docType string, issuedBy string, issuedDate string, description string, fileHash string) error {
// Check if LOC exists
loc, err := s.ReadLOC(ctx, locID)
if err != nil {
return err
}
// Check if document is required
isRequired := false
for _, requiredDoc := range loc.DocumentsRequired {
if requiredDoc == docType {
isRequired = true
break
}
}
if !isRequired {
return fmt.Errorf("document type %s is not required for this LOC", docType)
}
// Parse date
issueDate, err := time.Parse(time.RFC3339, issuedDate)
if err != nil {
return fmt.Errorf("invalid issued date format: %v", err)
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Create the document
document := Document{
ID: docID,
Type: docType,
IssuedBy: issuedBy,
IssuedDate: issueDate,
Description: description,
FileHash: fileHash,
Status: "SUBMITTED",
}
documentJSON, err := json.Marshal(document)
if err != nil {
return err
}
// Store the document
err = ctx.GetStub().PutState(fmt.Sprintf("DOC_%s", docID), documentJSON)
if err != nil {
return fmt.Errorf("failed to put document in world state: %v", err)
}
// Update the LOC
loc.DocumentsSubmitted = append(loc.DocumentsSubmitted, docID)
// Add event to history
loc.History = append(loc.History, Event{
Timestamp: time.Now(),
Action: "DOCUMENT_SUBMITTED",
Actor: clientID,
Details: fmt.Sprintf("Document %s of type %s was submitted", docID, docType),
})
// Check if all required documents are submitted
if len(loc.DocumentsSubmitted) >= len(loc.DocumentsRequired) {
loc.Status = "DOCUMENTS_RECEIVED"
}
locJSON, err := json.Marshal(loc)
if err != nil {
return err
}
return ctx.GetStub().PutState(locID, locJSON)
}
// VerifyDocuments verifies the submitted documents for a letter of credit
func (s *SmartContract) VerifyDocuments(ctx contractapi.TransactionContextInterface, locID string, approved bool, comments string) error {
// Check if LOC exists
loc, err := s.ReadLOC(ctx, locID)
if err != nil {
return err
}
// Check if documents have been submitted
if loc.Status != "DOCUMENTS_RECEIVED" {
return fmt.Errorf("documents have not been fully submitted yet")
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Verify that the client is from the applicant bank
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
if clientMSPID != loc.ApplicantBank {
return fmt.Errorf("client from %s is not authorized to verify documents for %s", clientMSPID, loc.ApplicantBank)
}
// Update the LOC status based on verification result
if approved {
loc.Status = "APPROVED"
} else {
loc.Status = "REJECTED"
}
// Add event to history
loc.History = append(loc.History, Event{
Timestamp: time.Now(),
Action: fmt.Sprintf("DOCUMENTS_%s", loc.Status),
Actor: clientID,
Details: comments,
})
locJSON, err := json.Marshal(loc)
if err != nil {
return err
}
return ctx.GetStub().PutState(locID, locJSON)
}
// MakePayment processes payment for an approved letter of credit
func (s *SmartContract) MakePayment(ctx contractapi.TransactionContextInterface, locID string, paymentReference string) error {
// Check if LOC exists
loc, err := s.ReadLOC(ctx, locID)
if err != nil {
return err
}
// Check if LOC is approved
if loc.Status != "APPROVED" {
return fmt.Errorf("letter of credit is not approved for payment")
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Verify that the client is from the applicant bank
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
if clientMSPID != loc.ApplicantBank {
return fmt.Errorf("client from %s is not authorized to make payment for %s", clientMSPID, loc.ApplicantBank)
}
// Update the LOC status
loc.Status = "PAID"
// Add event to history
loc.History = append(loc.History, Event{
Timestamp: time.Now(),
Action: "PAYMENT_MADE",
Actor: clientID,
Details: fmt.Sprintf("Payment made with reference %s", paymentReference),
})
locJSON, err := json.Marshal(loc)
if err != nil {
return err
}
return ctx.GetStub().PutState(locID, locJSON)
}
// QueryLOCsByApplicant returns all letters of credit for a specific applicant
func (s *SmartContract) QueryLOCsByApplicant(ctx contractapi.TransactionContextInterface, applicant string) ([]*LetterOfCredit, error) {
queryString := fmt.Sprintf(`{"selector":{"applicant":"%s"}}`, applicant)
return s.getQueryResultForQueryString(ctx, queryString)
}
// QueryLOCsByBeneficiary returns all letters of credit for a specific beneficiary
func (s *SmartContract) QueryLOCsByBeneficiary(ctx contractapi.TransactionContextInterface, beneficiary string) ([]*LetterOfCredit, error) {
queryString := fmt.Sprintf(`{"selector":{"beneficiary":"%s"}}`, beneficiary)
return s.getQueryResultForQueryString(ctx, queryString)
}
// QueryLOCsByStatus returns all letters of credit with a specific status
func (s *SmartContract) QueryLOCsByStatus(ctx contractapi.TransactionContextInterface, status string) ([]*LetterOfCredit, error) {
queryString := fmt.Sprintf(`{"selector":{"status":"%s"}}`, status)
return s.getQueryResultForQueryString(ctx, queryString)
}
// LOCExists returns true when letter of credit with given ID exists in world state
func (s *SmartContract) LOCExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
locJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return locJSON != nil, nil
}
// Helper function to get client identity
func (s *SmartContract) getClientIdentity(ctx contractapi.TransactionContextInterface) (string, error) {
clientID, err := ctx.GetClientIdentity().GetID()
if err != nil {
return "", fmt.Errorf("failed to get client identity: %v", err)
}
return clientID, nil
}
// Helper function for rich queries
func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*LetterOfCredit, error) {
resultsIterator, err := ctx.GetStub().GetQueryResult(queryString)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
var locs []*LetterOfCredit
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var loc LetterOfCredit
err = json.Unmarshal(queryResponse.Value, &loc)
if err != nil {
return nil, err
}
locs = append(locs, &loc)
}
return locs, nil
}
func main() {
chaincode, err := contractapi.NewChaincode(&SmartContract{})
if err != nil {
fmt.Printf("Error creating trade finance chaincode: %v\n", err)
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting trade finance chaincode: %v\n", err)
}
}
Healthcare
Hyperledger Fabric is being used in healthcare to improve data sharing while maintaining privacy and compliance with regulations.
MiPasa
MiPasa is a blockchain-based platform built on Hyperledger Fabric that enables secure sharing of COVID-19 data across healthcare organizations.
Key Features: - Secure sharing of anonymized health data - Data verification and validation - Privacy-preserving analytics - Integration with existing healthcare systems
Technical Implementation: - Multi-organization network with healthcare providers, researchers, and government agencies - Private data collections for sensitive health information - Zero-knowledge proofs for privacy-preserving analytics - Integration with data analytics tools
Sample Chaincode for Health Data Sharing:
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// HealthDataRecord represents a health data record
type HealthDataRecord struct {
ID string `json:"id"`
PatientID string `json:"patientId"`
RecordType string `json:"recordType"`
Provider string `json:"provider"`
CreationDate time.Time `json:"creationDate"`
LastModifiedDate time.Time `json:"lastModifiedDate"`
DataHash string `json:"dataHash"`
AccessPermissions []string `json:"accessPermissions"`
Status string `json:"status"`
History []Event `json:"history"`
}
// Event represents a health data event
type Event struct {
Timestamp time.Time `json:"timestamp"`
Action string `json:"action"`
Actor string `json:"actor"`
Details string `json:"details"`
}
// ConsentRecord represents a patient consent record
type ConsentRecord struct {
ID string `json:"id"`
PatientID string `json:"patientId"`
OrganizationID string `json:"organizationId"`
Purpose string `json:"purpose"`
GrantedDate time.Time `json:"grantedDate"`
ExpiryDate time.Time `json:"expiryDate"`
Status string `json:"status"`
}
// SmartContract provides functions for managing health data
type SmartContract struct {
contractapi.Contract
}
// CreateHealthRecord creates a new health data record
func (s *SmartContract) CreateHealthRecord(ctx contractapi.TransactionContextInterface, id string, patientID string, recordType string, provider string, dataHash string) error {
exists, err := s.HealthRecordExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the health record %s already exists", id)
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Verify that the client is from the provider organization
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
if clientMSPID != provider {
return fmt.Errorf("client from %s is not authorized to create records for %s", clientMSPID, provider)
}
// Check if consent exists for this patient and provider
consentExists, err := s.checkConsent(ctx, patientID, provider)
if err != nil {
return err
}
if !consentExists {
return fmt.Errorf("no valid consent exists for patient %s and provider %s", patientID, provider)
}
// Create the health record
now := time.Now()
record := HealthDataRecord{
ID: id,
PatientID: patientID,
RecordType: recordType,
Provider: provider,
CreationDate: now,
LastModifiedDate: now,
DataHash: dataHash,
AccessPermissions: []string{provider},
Status: "ACTIVE",
History: []Event{
{
Timestamp: now,
Action: "CREATED",
Actor: clientID,
Details: fmt.Sprintf("Health record %s was created", id),
},
},
}
recordJSON, err := json.Marshal(record)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, recordJSON)
}
// ReadHealthRecord returns the health record stored in the world state with given id
func (s *SmartContract) ReadHealthRecord(ctx contractapi.TransactionContextInterface, id string) (*HealthDataRecord, error) {
recordJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if recordJSON == nil {
return nil, fmt.Errorf("the health record %s does not exist", id)
}
var record HealthDataRecord
err = json.Unmarshal(recordJSON, &record)
if err != nil {
return nil, err
}
// Check if the client has permission to access this record
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return nil, fmt.Errorf("failed to get client MSPID: %v", err)
}
hasAccess := false
for _, org := range record.AccessPermissions {
if org == clientMSPID {
hasAccess = true
break
}
}
if !hasAccess {
return nil, fmt.Errorf("client from %s is not authorized to access this record", clientMSPID)
}
return &record, nil
}
// UpdateHealthRecord updates an existing health record
func (s *SmartContract) UpdateHealthRecord(ctx contractapi.TransactionContextInterface, id string, dataHash string) error {
record, err := s.ReadHealthRecord(ctx, id)
if err != nil {
return err
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Verify that the client is from the provider organization
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
if clientMSPID != record.Provider {
return fmt.Errorf("client from %s is not authorized to update records for %s", clientMSPID, record.Provider)
}
// Update the record
record.DataHash = dataHash
record.LastModifiedDate = time.Now()
// Add event to history
record.History = append(record.History, Event{
Timestamp: time.Now(),
Action: "UPDATED",
Actor: clientID,
Details: fmt.Sprintf("Health record %s was updated", id),
})
recordJSON, err := json.Marshal(record)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, recordJSON)
}
// GrantAccess grants access to a health record for a specific organization
func (s *SmartContract) GrantAccess(ctx contractapi.TransactionContextInterface, id string, organizationID string) error {
record, err := s.ReadHealthRecord(ctx, id)
if err != nil {
return err
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Verify that the client is from the provider organization or is the patient
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
// In a real implementation, you would verify that the client is either the provider or the patient
// For simplicity, we're just checking if the client has access to the record
hasAccess := false
for _, org := range record.AccessPermissions {
if org == clientMSPID {
hasAccess = true
break
}
}
if !hasAccess {
return fmt.Errorf("client from %s is not authorized to grant access to this record", clientMSPID)
}
// Check if the organization already has access
for _, org := range record.AccessPermissions {
if org == organizationID {
return fmt.Errorf("organization %s already has access to this record", organizationID)
}
}
// Grant access
record.AccessPermissions = append(record.AccessPermissions, organizationID)
// Add event to history
record.History = append(record.History, Event{
Timestamp: time.Now(),
Action: "ACCESS_GRANTED",
Actor: clientID,
Details: fmt.Sprintf("Access granted to organization %s", organizationID),
})
recordJSON, err := json.Marshal(record)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, recordJSON)
}
// RevokeAccess revokes access to a health record for a specific organization
func (s *SmartContract) RevokeAccess(ctx contractapi.TransactionContextInterface, id string, organizationID string) error {
record, err := s.ReadHealthRecord(ctx, id)
if err != nil {
return err
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Verify that the client is from the provider organization or is the patient
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
// In a real implementation, you would verify that the client is either the provider or the patient
// For simplicity, we're just checking if the client has access to the record
hasAccess := false
for _, org := range record.AccessPermissions {
if org == clientMSPID {
hasAccess = true
break
}
}
if !hasAccess {
return fmt.Errorf("client from %s is not authorized to revoke access to this record", clientMSPID)
}
// Cannot revoke access from the provider
if organizationID == record.Provider {
return fmt.Errorf("cannot revoke access from the provider organization")
}
// Revoke access
var newPermissions []string
for _, org := range record.AccessPermissions {
if org != organizationID {
newPermissions = append(newPermissions, org)
}
}
// Check if the organization had access
if len(newPermissions) == len(record.AccessPermissions) {
return fmt.Errorf("organization %s does not have access to this record", organizationID)
}
record.AccessPermissions = newPermissions
// Add event to history
record.History = append(record.History, Event{
Timestamp: time.Now(),
Action: "ACCESS_REVOKED",
Actor: clientID,
Details: fmt.Sprintf("Access revoked from organization %s", organizationID),
})
recordJSON, err := json.Marshal(record)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, recordJSON)
}
// CreateConsent creates a new consent record
func (s *SmartContract) CreateConsent(ctx contractapi.TransactionContextInterface, id string, patientID string, organizationID string, purpose string, expiryDate string) error {
exists, err := s.ConsentExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the consent record %s already exists", id)
}
// Parse expiry date
expDate, err := time.Parse(time.RFC3339, expiryDate)
if err != nil {
return fmt.Errorf("invalid expiry date format: %v", err)
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// In a real implementation, you would verify that the client is the patient or an authorized representative
// For simplicity, we're skipping this check
// Create the consent record
consent := ConsentRecord{
ID: id,
PatientID: patientID,
OrganizationID: organizationID,
Purpose: purpose,
GrantedDate: time.Now(),
ExpiryDate: expDate,
Status: "ACTIVE",
}
consentJSON, err := json.Marshal(consent)
if err != nil {
return err
}
return ctx.GetStub().PutState(fmt.Sprintf("CONSENT_%s", id), consentJSON)
}
// RevokeConsent revokes a consent record
func (s *SmartContract) RevokeConsent(ctx contractapi.TransactionContextInterface, id string) error {
consentKey := fmt.Sprintf("CONSENT_%s", id)
consentJSON, err := ctx.GetStub().GetState(consentKey)
if err != nil {
return fmt.Errorf("failed to read from world state: %v", err)
}
if consentJSON == nil {
return fmt.Errorf("the consent record %s does not exist", id)
}
var consent ConsentRecord
err = json.Unmarshal(consentJSON, &consent)
if err != nil {
return err
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// In a real implementation, you would verify that the client is the patient or an authorized representative
// For simplicity, we're skipping this check
// Revoke the consent
consent.Status = "REVOKED"
consentJSON, err = json.Marshal(consent)
if err != nil {
return err
}
return ctx.GetStub().PutState(consentKey, consentJSON)
}
// QueryHealthRecordsByPatient returns all health records for a specific patient
func (s *SmartContract) QueryHealthRecordsByPatient(ctx contractapi.TransactionContextInterface, patientID string) ([]*HealthDataRecord, error) {
queryString := fmt.Sprintf(`{"selector":{"patientId":"%s"}}`, patientID)
return s.getQueryResultForQueryString(ctx, queryString)
}
// QueryHealthRecordsByProvider returns all health records from a specific provider
func (s *SmartContract) QueryHealthRecordsByProvider(ctx contractapi.TransactionContextInterface, provider string) ([]*HealthDataRecord, error) {
queryString := fmt.Sprintf(`{"selector":{"provider":"%s"}}`, provider)
return s.getQueryResultForQueryString(ctx, queryString)
}
// HealthRecordExists returns true when health record with given ID exists in world state
func (s *SmartContract) HealthRecordExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
recordJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return recordJSON != nil, nil
}
// ConsentExists returns true when consent record with given ID exists in world state
func (s *SmartContract) ConsentExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
consentJSON, err := ctx.GetStub().GetState(fmt.Sprintf("CONSENT_%s", id))
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return consentJSON != nil, nil
}
// Helper function to check if valid consent exists
func (s *SmartContract) checkConsent(ctx contractapi.TransactionContextInterface, patientID string, organizationID string) (bool, error) {
queryString := fmt.Sprintf(`{"selector":{"patientId":"%s","organizationId":"%s","status":"ACTIVE"}}`, patientID, organizationID)
resultsIterator, err := ctx.GetStub().GetQueryResult(queryString)
if err != nil {
return false, err
}
defer resultsIterator.Close()
// Check if there's at least one valid consent
if resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return false, err
}
var consent ConsentRecord
err = json.Unmarshal(queryResponse.Value, &consent)
if err != nil {
return false, err
}
// Check if the consent is expired
if consent.ExpiryDate.Before(time.Now()) {
return false, nil
}
return true, nil
}
return false, nil
}
// Helper function to get client identity
func (s *SmartContract) getClientIdentity(ctx contractapi.TransactionContextInterface) (string, error) {
clientID, err := ctx.GetClientIdentity().GetID()
if err != nil {
return "", fmt.Errorf("failed to get client identity: %v", err)
}
return clientID, nil
}
// Helper function for rich queries
func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*HealthDataRecord, error) {
resultsIterator, err := ctx.GetStub().GetQueryResult(queryString)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
var records []*HealthDataRecord
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var record HealthDataRecord
err = json.Unmarshal(queryResponse.Value, &record)
if err != nil {
return nil, err
}
records = append(records, &record)
}
return records, nil
}
func main() {
chaincode, err := contractapi.NewChaincode(&SmartContract{})
if err != nil {
fmt.Printf("Error creating health data chaincode: %v\n", err)
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting health data chaincode: %v\n", err)
}
}
Government and Public Sector
Hyperledger Fabric is being used by governments to improve transparency, efficiency, and trust in public services.
Land Registry
Several countries are implementing land registry systems on Hyperledger Fabric to improve property rights management and reduce fraud.
Key Features: - Immutable record of land ownership and transfers - Transparent property transactions - Reduced fraud and disputes - Streamlined property registration process
Technical Implementation: - Multi-organization network with government agencies, notaries, and financial institutions - Smart contracts for property registration and transfer - Integration with existing land registry systems - Public access for property verification
Sample Chaincode for Land Registry:
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// Property represents a land property
type Property struct {
ID string `json:"id"`
Location Location `json:"location"`
Area float64 `json:"area"`
OwnerID string `json:"ownerId"`
RegistrationDate time.Time `json:"registrationDate"`
Value float64 `json:"value"`
Status string `json:"status"`
History []Event `json:"history"`
}
// Location represents a property location
type Location struct {
Address string `json:"address"`
City string `json:"city"`
State string `json:"state"`
Country string `json:"country"`
PostalCode string `json:"postalCode"`
Coordinates string `json:"coordinates"`
}
// Event represents a property event
type Event struct {
Timestamp time.Time `json:"timestamp"`
Action string `json:"action"`
Actor string `json:"actor"`
Details string `json:"details"`
}
// TransferRequest represents a property transfer request
type TransferRequest struct {
ID string `json:"id"`
PropertyID string `json:"propertyId"`
CurrentOwnerID string `json:"currentOwnerId"`
NewOwnerID string `json:"newOwnerId"`
RequestDate time.Time `json:"requestDate"`
TransferAmount float64 `json:"transferAmount"`
Status string `json:"status"`
ApprovalStatus map[string]string `json:"approvalStatus"`
}
// SmartContract provides functions for managing land registry
type SmartContract struct {
contractapi.Contract
}
// RegisterProperty registers a new property
func (s *SmartContract) RegisterProperty(ctx contractapi.TransactionContextInterface, id string, address string, city string, state string, country string, postalCode string, coordinates string, area float64, ownerID string, value float64) error {
exists, err := s.PropertyExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the property %s already exists", id)
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Verify that the client is from the registry authority
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
if clientMSPID != "RegistryAuthorityMSP" {
return fmt.Errorf("client from %s is not authorized to register properties", clientMSPID)
}
// Create the property
property := Property{
ID: id,
Location: Location{
Address: address,
City: city,
State: state,
Country: country,
PostalCode: postalCode,
Coordinates: coordinates,
},
Area: area,
OwnerID: ownerID,
RegistrationDate: time.Now(),
Value: value,
Status: "REGISTERED",
History: []Event{
{
Timestamp: time.Now(),
Action: "REGISTERED",
Actor: clientID,
Details: fmt.Sprintf("Property %s was registered", id),
},
},
}
propertyJSON, err := json.Marshal(property)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, propertyJSON)
}
// ReadProperty returns the property stored in the world state with given id
func (s *SmartContract) ReadProperty(ctx contractapi.TransactionContextInterface, id string) (*Property, error) {
propertyJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if propertyJSON == nil {
return nil, fmt.Errorf("the property %s does not exist", id)
}
var property Property
err = json.Unmarshal(propertyJSON, &property)
if err != nil {
return nil, err
}
return &property, nil
}
// UpdatePropertyValue updates the value of a property
func (s *SmartContract) UpdatePropertyValue(ctx contractapi.TransactionContextInterface, id string, newValue float64) error {
property, err := s.ReadProperty(ctx, id)
if err != nil {
return err
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Verify that the client is from the registry authority
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
if clientMSPID != "RegistryAuthorityMSP" {
return fmt.Errorf("client from %s is not authorized to update property values", clientMSPID)
}
// Update the property value
property.Value = newValue
// Add event to history
property.History = append(property.History, Event{
Timestamp: time.Now(),
Action: "VALUE_UPDATED",
Actor: clientID,
Details: fmt.Sprintf("Property value updated to %.2f", newValue),
})
propertyJSON, err := json.Marshal(property)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, propertyJSON)
}
// CreateTransferRequest creates a new property transfer request
func (s *SmartContract) CreateTransferRequest(ctx contractapi.TransactionContextInterface, id string, propertyID string, currentOwnerID string, newOwnerID string, transferAmount float64) error {
exists, err := s.TransferRequestExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the transfer request %s already exists", id)
}
// Check if property exists
property, err := s.ReadProperty(ctx, propertyID)
if err != nil {
return err
}
// Verify that the current owner is correct
if property.OwnerID != currentOwnerID {
return fmt.Errorf("the current owner ID does not match the property's owner")
}
// Verify that the property is not already in a transfer process
if property.Status == "TRANSFER_PENDING" {
return fmt.Errorf("the property is already in a transfer process")
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Create the transfer request
transferRequest := TransferRequest{
ID: id,
PropertyID: propertyID,
CurrentOwnerID: currentOwnerID,
NewOwnerID: newOwnerID,
RequestDate: time.Now(),
TransferAmount: transferAmount,
Status: "PENDING",
ApprovalStatus: map[string]string{
"RegistryAuthorityMSP": "PENDING",
"NotaryMSP": "PENDING",
"BankMSP": "PENDING",
},
}
transferRequestJSON, err := json.Marshal(transferRequest)
if err != nil {
return err
}
// Update property status
property.Status = "TRANSFER_PENDING"
// Add event to property history
property.History = append(property.History, Event{
Timestamp: time.Now(),
Action: "TRANSFER_REQUESTED",
Actor: clientID,
Details: fmt.Sprintf("Transfer request %s created", id),
})
propertyJSON, err := json.Marshal(property)
if err != nil {
return err
}
// Update the world state
err = ctx.GetStub().PutState(fmt.Sprintf("TRANSFER_%s", id), transferRequestJSON)
if err != nil {
return fmt.Errorf("failed to put transfer request in world state: %v", err)
}
return ctx.GetStub().PutState(propertyID, propertyJSON)
}
// ApproveTransferRequest approves a property transfer request
func (s *SmartContract) ApproveTransferRequest(ctx contractapi.TransactionContextInterface, id string) error {
// Get the transfer request
transferRequestJSON, err := ctx.GetStub().GetState(fmt.Sprintf("TRANSFER_%s", id))
if err != nil {
return fmt.Errorf("failed to read from world state: %v", err)
}
if transferRequestJSON == nil {
return fmt.Errorf("the transfer request %s does not exist", id)
}
var transferRequest TransferRequest
err = json.Unmarshal(transferRequestJSON, &transferRequest)
if err != nil {
return err
}
// Check if the transfer request is still pending
if transferRequest.Status != "PENDING" {
return fmt.Errorf("the transfer request is not in PENDING status")
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Get client MSPID
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
// Check if the client's organization is involved in the approval process
_, exists := transferRequest.ApprovalStatus[clientMSPID]
if !exists {
return fmt.Errorf("client from %s is not involved in the approval process", clientMSPID)
}
// Update the approval status
transferRequest.ApprovalStatus[clientMSPID] = "APPROVED"
// Check if all approvals are complete
allApproved := true
for _, status := range transferRequest.ApprovalStatus {
if status != "APPROVED" {
allApproved = false
break
}
}
// If all approvals are complete, finalize the transfer
if allApproved {
transferRequest.Status = "APPROVED"
// Get the property
property, err := s.ReadProperty(ctx, transferRequest.PropertyID)
if err != nil {
return err
}
// Update the property
property.OwnerID = transferRequest.NewOwnerID
property.Status = "REGISTERED"
property.Value = transferRequest.TransferAmount
// Add event to property history
property.History = append(property.History, Event{
Timestamp: time.Now(),
Action: "TRANSFERRED",
Actor: clientID,
Details: fmt.Sprintf("Property transferred to %s", transferRequest.NewOwnerID),
})
propertyJSON, err := json.Marshal(property)
if err != nil {
return err
}
err = ctx.GetStub().PutState(transferRequest.PropertyID, propertyJSON)
if err != nil {
return fmt.Errorf("failed to update property in world state: %v", err)
}
}
transferRequestJSON, err = json.Marshal(transferRequest)
if err != nil {
return err
}
return ctx.GetStub().PutState(fmt.Sprintf("TRANSFER_%s", id), transferRequestJSON)
}
// RejectTransferRequest rejects a property transfer request
func (s *SmartContract) RejectTransferRequest(ctx contractapi.TransactionContextInterface, id string, reason string) error {
// Get the transfer request
transferRequestJSON, err := ctx.GetStub().GetState(fmt.Sprintf("TRANSFER_%s", id))
if err != nil {
return fmt.Errorf("failed to read from world state: %v", err)
}
if transferRequestJSON == nil {
return fmt.Errorf("the transfer request %s does not exist", id)
}
var transferRequest TransferRequest
err = json.Unmarshal(transferRequestJSON, &transferRequest)
if err != nil {
return err
}
// Check if the transfer request is still pending
if transferRequest.Status != "PENDING" {
return fmt.Errorf("the transfer request is not in PENDING status")
}
// Get client identity
clientID, err := s.getClientIdentity(ctx)
if err != nil {
return err
}
// Get client MSPID
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
if err != nil {
return fmt.Errorf("failed to get client MSPID: %v", err)
}
// Check if the client's organization is involved in the approval process
_, exists := transferRequest.ApprovalStatus[clientMSPID]
if !exists {
return fmt.Errorf("client from %s is not involved in the approval process", clientMSPID)
}
// Update the approval status
transferRequest.ApprovalStatus[clientMSPID] = "REJECTED"
transferRequest.Status = "REJECTED"
// Get the property
property, err := s.ReadProperty(ctx, transferRequest.PropertyID)
if err != nil {
return err
}
// Update the property status
property.Status = "REGISTERED"
// Add event to property history
property.History = append(property.History, Event{
Timestamp: time.Now(),
Action: "TRANSFER_REJECTED",
Actor: clientID,
Details: fmt.Sprintf("Transfer request rejected: %s", reason),
})
propertyJSON, err := json.Marshal(property)
if err != nil {
return err
}
err = ctx.GetStub().PutState(transferRequest.PropertyID, propertyJSON)
if err != nil {
return fmt.Errorf("failed to update property in world state: %v", err)
}
transferRequestJSON, err = json.Marshal(transferRequest)
if err != nil {
return err
}
return ctx.GetStub().PutState(fmt.Sprintf("TRANSFER_%s", id), transferRequestJSON)
}
// QueryPropertiesByOwner returns all properties owned by a specific owner
func (s *SmartContract) QueryPropertiesByOwner(ctx contractapi.TransactionContextInterface, ownerID string) ([]*Property, error) {
queryString := fmt.Sprintf(`{"selector":{"ownerId":"%s"}}`, ownerID)
return s.getQueryResultForQueryString(ctx, queryString)
}
// QueryPropertiesByLocation returns all properties in a specific location
func (s *SmartContract) QueryPropertiesByLocation(ctx contractapi.TransactionContextInterface, city string, state string) ([]*Property, error) {
queryString := fmt.Sprintf(`{"selector":{"location.city":"%s","location.state":"%s"}}`, city, state)
return s.getQueryResultForQueryString(ctx, queryString)
}
// PropertyExists returns true when property with given ID exists in world state
func (s *SmartContract) PropertyExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
propertyJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return propertyJSON != nil, nil
}
// TransferRequestExists returns true when transfer request with given ID exists in world state
func (s *SmartContract) TransferRequestExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
transferRequestJSON, err := ctx.GetStub().GetState(fmt.Sprintf("TRANSFER_%s", id))
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return transferRequestJSON != nil, nil
}
// Helper function to get client identity
func (s *SmartContract) getClientIdentity(ctx contractapi.TransactionContextInterface) (string, error) {
clientID, err := ctx.GetClientIdentity().GetID()
if err != nil {
return "", fmt.Errorf("failed to get client identity: %v", err)
}
return clientID, nil
}
// Helper function for rich queries
func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]*Property, error) {
resultsIterator, err := ctx.GetStub().GetQueryResult(queryString)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
var properties []*Property
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var property Property
err = json.Unmarshal(queryResponse.Value, &property)
if err != nil {
return nil, err
}
properties = append(properties, &property)
}
return properties, nil
}
func main() {
chaincode, err := contractapi.NewChaincode(&SmartContract{})
if err != nil {
fmt.Printf("Error creating land registry chaincode: %v\n", err)
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting land registry chaincode: %v\n", err)
}
}
Lessons Learned from Real-World Implementations
Based on these case studies and other real-world implementations, several key lessons have emerged:
- Start Small and Scale:
- Begin with a minimum viable product (MVP) focused on core functionality
- Validate the solution with a small group of users before expanding
-
Incrementally add features based on user feedback
-
Focus on Business Value:
- Clearly define the business problem and how blockchain solves it
- Quantify the expected benefits (cost savings, time reduction, etc.)
-
Ensure all stakeholders understand the value proposition
-
Address Governance Early:
- Establish clear governance structures for network management
- Define roles, responsibilities, and decision-making processes
-
Create policies for onboarding new participants and handling disputes
-
Plan for Integration:
- Design for integration with existing systems from the start
- Use standard APIs and data formats
-
Implement proper error handling and data validation
-
Consider Privacy and Regulatory Compliance:
- Understand and address relevant regulations (GDPR, HIPAA, etc.)
- Use private data collections for sensitive information
-
Implement proper access controls and audit trails
-
Invest in User Experience:
- Develop intuitive user interfaces that hide blockchain complexity
- Provide clear documentation and training
-
Gather and incorporate user feedback
-
Plan for Maintenance and Upgrades:
- Establish processes for chaincode upgrades
- Implement monitoring and alerting
- Create backup and recovery procedures
By learning from these real-world implementations, organizations can increase their chances of success when deploying Hyperledger Fabric solutions in production environments.