hyperledger-fabrichyperledger-chaincode

Is it possible to divide chaincode in multiple files by taking advantage of module system in Go?


I currently have my chaincode in a single file and it's getting bigger as I am adding more functions, so it's really frustrating to maintain it.

I thought it's good idea to take advantage of module system in Go, But I can't figure out the way.

What I have:

my_chaincode
├── go.mod
├── go.sum
└── chaincode.go (whole chaincode)

AND

my_chaincode
├── go.mod
├── go.sum
├── modules
│   └── smartcontract.go  (All other functions)
└── chaincode.go          (Only main function)

What I want:

my_chaincode
├── go.mod
├── go.sum
├── modules
│   ├── admin.go   (Some functions)
│   ├── system.go  (Some functions)
│   └── user.go    (Some functions)
└── chaincode.go   (Only main function)

(*Note: I have a basic knowledge of creating and using Go modules.)

I tried dividing the chaincode (like What I want) and deployed the chaincode, but when I try to execute a transaction the chaincode container crashes with error -

status:500 message:"error in simulation: failed to execute transaction

This doesn't happen with my previous two attempts. I am sure that I am lacking some knowledge regarding Go modules.

What am I missing? Some reference like a GitHub repository with structure like what I want will be great?


Solution

  • Yes, it is possible using Go modules.

    Let me explain using a example, consider file structure like below

    my_chaincode        (not necessary to use modules as folder name)
    ├── go.mod          (created after executing `go mod init`)
    ├── go.sum          (created after executing `go mod tidy`)
    ├── modules         (not necessary to use modules as folder name)
    │   ├── module1.go  (Some functions)
    │   ├── module2.go  (Some functions)
    │   └── module3.go  (Some functions)
    └── main.go         (not necessary to use "main" as file name)
    
    // main.go
    
    package main
    import (
        "fmt"
        "github.com/hyperledger/fabric-contract-api-go/contractapi"
    
        "my_chaincode/modules"  // importing all files from modules folder
    )
    
    func main() {
        chaincode, err := contractapi.NewChaincode(new(modules.SmartContract))
    
        if err != nil {
            fmt.Printf("Error create chaincode: %s", err.Error())
            return
        }
        if err := chaincode.Start(); err != nil {
            fmt.Printf("Error starting chaincode: %s", err.Error())
        }
    }
    
    // modules/module1.go
    
    package modules
    
    type SmartContract struct {
        contractapi.Contract
    }
    
    type User struct {
        // anything
    }
    
    func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
        // your logic
    }
    
    // modules/module2.go
    
    package modules
    
    import (
        "github.com/hyperledger/fabric-contract-api-go/contractapi"
    )
    
    // private function (because name starts with a small letter)
    func validateUid (uid string) (bool, error) {
        // your logic
    }
    
    // public function (because name starts with a capital letter)
    func (s *SmartContract) Create_User (ctx contractapi.TransactionContextInterface, uid string, name string, age int) error {
        // your logic
        // you can use "validateUid(uid)" function here
    }
    
    // modules/module3.go
    
    import (
        "github.com/hyperledger/fabric-contract-api-go/contractapi"
    )
    
    func (s *SmartContract) Create_Record (ctx contractapi.TransactionContextInterface, rid int, uid int) error {
        // your logic
    
        // you can use "validateUid(uid)" function here
        // you can use "User" struct here
        // you can use "ctx.Create_User(uid, 'user1', '20')" function here
    
        // all above uses may seem irrelevant, but my intention is to show
        // that you can directly use them without again defining in this file, 
        // which is the main goal. So, the whole chaincode can be divided 
        // into small modules.
    }