Scaffolded Go Chaincode Project

Blockchain App Builder takes the input from your specification file and generates a fully-functional scaffolded chaincode project.

If the chaincode project is in the Go language, the scaffolded project contains three main files:
  • main.go
  • <chaincodeName>.model.go
  • <chaincodeName>.controller.go
All the necessary libraries are installed and packaged.

The <chaincodeName>.model.go contains multiple asset definitions and <chaincodeName>.controller.go contains the asset's behavior and CRUD methods. The various Go struct tags and packages in model.go and controller.go provide support for features like automatic validation of arguments, marshalling/unmarshalling of arguments, transparent persistence capability (ORM) and calling rich queries.

The scaffolded project can be found in $GOPATH/src/example.com/<chaincodeName>

Validators

Id
id:"true"
This validator identifies the property which uniquely defines the underlying model. The asset is saved by the value in this key. This validator automatically applies when a new Go project is scaffolded.
In the below screenshot "SupplierId" is the key for the supplier asset and has a tag property id:"true" for the SupplierId property.
type Supplier struct {
    Supplierld              string        'json:"Supplierld" validate:"string,mandatory" id:"true"' 
    RawMaterialAvailable    int           'json:"RawMaterialAvailable" validate:"int,min=0"'
    License                 string        'json:"License" validate:"string,min=10"'
    ExpiryDate              date.Date     'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                  bool          'json:"Active" validate:"bool" default :"true"'
    Metadata                interface{}   'json:"Metadata,omitempty"'   
}
Derived
derived:"strategy,algorithm,format"
This decorator is used for defining the attribute derived from other properties. This decorator has two mandatory parameters:
  • strategy: takes values of concat or hash. Requires an additional parameter algorithm if hash is selected. The default algorithm is sha256; md5 is also supported.
  • format: takes an array of specification strings and values to be used by the strategy.
type Supplier struct{
   AssetType string 'json:"AssetType" final:"chaincode1.Supplier"'
   SupplierId   string  'json:"SupplierId" validate:"string" id:"true" mandatory:"true" derived:"strategy=hash,algorith=sha256,format=IND%1%2,License,Name"'
   Name         string  'json:"Name" validate:"string,min=2,max=4"'
   License      string  'json:"License" validate:"string,min=2,max=4"'
}
Mandatory
validate:"mandatory"
This marks the following property as mandatory and cannot be skipped while saving to the ledger. If skipped it throws an error. In the below example, "SupplierId" has a validate:"mandatory" tag.
Type Supplier struct {
    Supplierld              string      'json:"Supplierld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable    int         'json:"RawMaterialAvailable" validate:"int,min=0"'
    License                 string      'json:"License" validate:"string,min=10"'
    ExpiryDate              date.Date   'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                  bool        'json:"Active" validate:"bool" default :"true"'
    Metadata                interface{} 'json:"Metadata,omitempty"'
}
Default
default:"<param>"
This states that the following property can have a default value. The default value in the default tag is used when the property is skipped while saving to the ledger. In the below example property, Active has a default value of true, provided as tag default:"true"
Type Supplier struct {
    Supplierld            string       'json:"Supplierld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable  int          'json:"RawMaterialAvailable" validate:"int,min=0"'
    License               string       'json:"License" validate:"string,min=10"'
    ExpiryDate            date.Date    'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                bool         'json:"Active" validate:"bool" default :"true"'
    Metadata              interface{}  'json:"Metadata,omitempty"'
}
Validate types
Basic Go types are validated for a property by defining a validate tag. These are the validate tags based on types:
  • string: validate: "string"
  • date: validate: "date"
  • number: validate: "int"
  • boolean: validate: "bool"
Min validator
validate:"min=<param>"
Using the min validator, minimum value can be set for a property of type number and string.
For type int: In the example, RawMaterialAvailable property has a minimum value of 0 and if a value less than 0 is applied to RawMaterialAvailable an error will be returned.
For type string: For the string type minimum validator will check the length of the string with the provided value. Therefore, in the below example the License property has to be minimum 10 characters long.
Example:
Type Supplier struct {
    Supplierld             string       'json:"Supplierld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable   int          'json:"RawMaterialAvailable" validate:"int,min=0"'
    License                string       'json:"License" validate:"string,min=10"'
    ExpiryDate             date.Date    'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                 bool         'json:"Active" validate:"bool" default :"true"'
    Metadata               interface{}  'json:"Metadata,omitempty"'
}
Max validator
validate:"max=<param>"
Using the max validator, the maximum value can be set for a property of type number and string.
For type int: Like the min validator, for type int, if a value provided for the structfield is greater than the value provided in the validator then an error will be returned.
For type string: Like the min validator, max validator will also check the length of the string with given value. In the example, the Domian property has a maximum value of 50, so if the Domain property has a string length more than 50 characters, then an error message will be returned.
type Retailer struct {
    Retailerld         string        'json:"Retailerld" validate:"string,mandatory" id:"true"'   
    ProductsOrdered    int           'json:"ProductsOrdered" validate:"int,mandatory"'
    ProductsAvailable  int           'json:"ProductsAvailable" validate:"int" default:"1"' 
    ProductsSold       int           'json:"ProductsSold" validate:"int"'
    Remarks            string        'json:"Remarks" validate:"string" default :"open for business"'
    Items              []int         'json:"Items" validate:"array=int,range=l-5"'
    Domain             string        'json:"Domain" validate:"url,min=30,max=50"'
    Metadata           interface{}   'json:"Metadata,omitempty"'
}
Date validators
Before validator:
validate:"before=<param>"
The before validator validates a property of type date to have a value less than the specified in parameter.
In this example, the ExpiryDate property should be before "2020-06-26" and if not it will return an error.
Type Supplier struct {
    Supplierld            string       'json:"Supplierld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable  int          'json:"RawMaterialAvailable" validate:"int,min=0"'
    License               string       'json:"License" validate:"string,min=10"'
    ExpiryDate            date.Date    'json:"ExpiryDate" validate:"date,before=2020-06-26"'
    Active                bool         'json:"Active" validate:"bool" default :"true"'
    Metadata              interface{}  'json:"Metadata,omitempty"'
}
After validator:
validate:"after=<param>"
The before validator validates a property of type date to have a value greater than the specified in parameter.
In this example, the CompletionDate property should be after "2020-06-26" and if not it will return an error.
Type Supplier struct {
    Manufacturerld        string       'json:"Manufacturerld" validate:"string,mandatory" id:"true"'
    RawMaterialAvailable  int          'json:"RawMaterialAvailable" validate:"int,max=8"'
    ProductsAvailable     int          'json:"ProductsAvailable" validate:"int"'
    CompletionDate        date.Date    'json:"CompletionDate" validate:"date,after=2020-06-26"'
    Metadata              interface{}  'json:"Metadata,omitempty"'
}
URL validator
validate:"url"
The URL validator will validate a property for URL strings.
In this example, the Domain property has to be a valid URL.
type Retailer struct {
    Retailerld         string        'json:"Retailerld" validate:"string,mandatory" id:"true"'   
    ProductsOrdered    int           'json:"ProductsOrdered" validate:"int,mandatory"'
    ProductsAvailable  int           'json:"ProductsAvailable" validate:"int" default:"1"' 
    ProductsSold       int           'json:"ProductsSold" validate:"int"'
    Remarks            string        'json:"Remarks" validate:"string" default :"open for business"'
    Items              []int         'json:"Items" validate:"array=int,range=l-5"'
    Domain             string        'json:"Domain" validate:"string,url,min=30,max=50"'
    Metadata           interface{}   'json:"Metadata,omitempty"'
}
Regexp validator
validate:"regexp=<param>"
Regexp validator will validate property for the input regular expression.
In this example, the PhoneNumber property will validate for a mobile number as per the regular expression.
type Customer struct {
Customerld      string       'json:"Customerld" validate:"string,mandatory" id:"true"'
Name            string       'json:"Name" validate:"string,mandatory"'
ProductsBought  int          'json:"ProductsBought" validate:"int"'
OfferApplied    int          'json:"OfferApplied" validate :"int,nax=0"'
PhoneNumber     string       'json:"PhoneNumber" validate:"string,regexp=A\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$"'
Received        bool         'json:"Received" validate:"bool“'
Metadata        interface{}  'json:"Metadata,omitempty"'
}
Multiple validators
Multiple validators can be applied a property.
In this example, the Domain property has validation for a string, URL, and min and max string length.
type Retailer struct {
    Retailerld         string        'json:"Retailerld" validate:"string,mandatory" id:"true"'   
    ProductsOrdered    int           'json:"ProductsOrdered" validate:"int,mandatory"'
    ProductsAvailable  int           'json:"ProductsAvailable" validate:"int" default:"1"' 
    ProductsSold       int           'json:"ProductsSold" validate:"int"'
    Remarks            string        'json:"Remarks" validate:"string" default :"open for business"'
    Items              []int         'json:"Items" validate:"array=int,range=l-5"'
    Domain             string        'json:"Domain" validate:"string,url,min=30,max=50"'
    Metadata           interface{}   'json:"Metadata,omitempty"'
}

Model

Asset Type Property

By default every struct will have an additional property called AssetType. This property can be useful in fetching only assets of this type. Any changes to this property is ignored during create and update of asset. The property value by default is <chaincodeName>.<modelName>.
type Supplier struct {
AssetType string 'json:"AssetType" default:"TestGoProject.Supplier"'

SupplierId            string      'json:"SupplierId" validate:"string,mandatory" id:"true'
RawMaterialAvailable  int         'json:"RawMaterialAvailable" validate:"int,min=0"'
License               string      'json:"License" validate:"string,min=10"'
ExpiryDate            date.Date   'json:"ExpiryDate" validate:"date,before=2020-06-26"'
Active                bool	  'json:"Active" validate:"bool" default:"true"'
Metadata              interface{} 'json:"Metadata,omitempty"'
}

ORM

Go chaincodes implement Transparent Persistence Capability (ORM) with the model package.

The following ORM methods are exposed via the model package:

model.Get
Queries the ledger for the stored asset based on the given ID.
func Get(Id string, result ...interface{}) (interface{}, error)
Parameters:
  • Id - The ID of the asset which is required from the ledger.
  • result (interface{}) - This is an empty asset object of a particular type, which is passed by reference. This object will contain the result from this method. To be used only if type-specific result is required.
  • asset (interface) - Empty asset object, which is passed by reference. This object will contain the result from this method. To be used only if type-specific result is required.
Returns:
  • interface {} - Interface contains the asset in the form of map[string]interface{}. Before operating on this map, it is required to assert the obtained interface with type map[string]interface{}. To convert this map into an asset object, you can use the utility API util.ConvertMaptoStruct (see: Utility Package).
  • error - Contains an error if returned, or is nil.
model.Update
Updates the provided asset in the ledger with the new values.
func Update(args ...interface{}) (interface{}, error)
Parameters:
  • obj (interface) - The object that is required to be updated in the ledger is passed by reference into this API with the new values. The input asset is validated and verified according to the struct tags mentioned in the model specification and then stored into the ledger.
Returns:
  • interface{} - The saved asset is returned as an interface.
  • error - Contains an error if returned, or is nil.
model.Save
Saves the asset to the ledger after validating on all the struct tags.
func Save(args ...interface{}) (interface{}, error)
Parameters:
  • obj/args[0] (interface{}) - The object that needs to be stored in the ledger is passed by reference in this utility method.
  • metadata/args[1] (interface{}) - This parameter is optional. It has been given in order to facilitate you if you're required to store any metadata into the ledger along with the asset at the runtime. This parameter can be skipped if no such requirement exists.
Returns:
  • interface {} - The asset is returned as an interface.
  • error - Contains an error if returned, or is nil.
model.Delete
Deletes the asset from the ledger.
func Delete(Id string) (interface{}, error)
Parameters:
  • id (string) - The ID of the asset which is required to be deleted from the ledger.
Returns:
  • interface {} - Contains the asset being deleted in the form of map[string]interface{}.
model.GetByRange
Returns the list of assets by range of IDs.
func GetByRange(startKey string, endKey string, asset ...interface{})
([]map[string]interface{}, error)
Parameters:
  • startkey (string) - Starting ID for the range of objects which are required.
  • endkey (string) - End of the range of objects which are required.
  • asset interface - (optional) Empty array of assets, which is passed by reference. This array will contain the result from this method. To be used if type-specific result is required.
Returns:
  • []map[string]interface{} - This array contains the list of assets obtained from the ledger. You can access the objects iterating over this array and asserting the objects as map[string]interface{} and using utility to convert to asset object.
  • error - Contains an error if returned, or is nil.
model.GetHistoryById
Returns the history of the asset with the given ID.
func GetHistoryByID(Id string) ([]interface{}, error)
Parameters:
  • Id (string) - ID of the asset for which the history is needed.
Returns:
  • []interface{} - This slice contains the history of the asset obtained from the ledger in form of slice of map[string]interface{}. You can access each history element by iterating over this slice and asserting the objects as map[string]interface{} and using utility to convert to asset object.
  • error - Contains the error if observed.
model.Query
The query method will run a SQL/Couch DB query over the ledger. This method is only supported for remote deployment on Oracle Blockchain Platform. This is a generic method for executing SQL queries on the ledger.
func Query(queryString string) ([]interface{}, error)
Parameters:
  • queryString (string) - Input the query string.
Returns:
  • []interface{} - This will contain the output of the query. The result is in form of slice of interfaces. You need to iterate over the slice and use the elements by converting them to proper types.
  • error - Contains the error if observed.

Composite Key Methods

model.GenerateCompositeKey
This method generates and returns the composite key based on the indexName and the attributes given in the arguments.
func GenerateCompositeKey(indexName string, attributes []string)
(string, error)
Parameters:
  • indexName (string) - Object type of the composite key.
  • attrbutes ([]string) - Attributes of the asset based on which the composite key has to be formed.
Returns:
  • string - This contains the composite key result.
  • error - Contains the error if observed.
model.GetByCompositeKey
This method returns the asset that matches the key and the column given in the parameters. The index parameter indicates the index of the key returned in the array of stub method SplitCompositeKey.
Internally this method calls Hyperledger Fabric's getStateByPartialCompositeKey, splitCompositeKey and getState.
func GetByCompositeKey(key string, columns []string, index int)
(interface{}, error)
Parameters:
  • key (string) - Object type provided while creating composite key.
  • column ([]string) - This is the slice of attributes on which the ledger has to be queried using the composite key.
  • index(int) - Index of the attribute.
Returns:
  • Interface{} - Contains the list of assets which are result of this method.
  • error - Contains any errors if present.

Stub Method

model.GetNetworkStub
This method will return the Hyperledger Fabric chaincodeStub.
You can get access to the shim stub by calling the GetNetworkStub method. This will help you write your own implementation working directly with the assets.
func GetNetworkStub() shim.ChaincodeStubInterface
Parameters:
  • none
Returns:
  • shim.ChaincodeStubInterface - This is the Hyperledger Fabric chaincode stub.

Other Methods

  • model.GetTransactionId()
  • model.GetTransactionTimestamp()
  • model.GetChannelID()
  • model.GetCreator()
  • model.GetSignedProposal()
  • model.GetArgs()
  • model.GetStringArgs()
  • model.getId
model.GetTransactionId
Returns the transaction ID for the current chaincode invocation request. The transaction ID uniquely identifies the transaction within the scope of the channel.
func GetTransactionId() string
Parameters:
  • none
Returns:
  • string - This contains the required transaction ID.
model.GetTransactionTimestamp
Returns the timestamp when the transaction was created. This is taken from the transaction ChannelHeader, therefore it will indicate the client's timestamp, and will have the same value across all endorsers.
func GetTransactionTimestamp() (*timestamp.Timestamp, error)
Parameters:
  • none
Returns:
  • timestamp.Timestamp - Contains the timestamp required.
  • error - Contains any errors if present.
model.GetChannelID
Returns the channel ID for the proposal for the chaincode to process.
func GetChannelID() string
Parameters:
  • none
Returns:
  • string - Contains the required channel ID as a string.
model.GetCreator
Returns the identity object of the chaincode invocation's submitter
func GetCreator() ([]byte, error)
Parameters:
  • none
Returns:
  • [ ]byte - Contains the required identity object serialized.
  • error - Contains any errors if present.
model.GetSignedProposal
Returns a fully decoded object of the signed transaction proposal.
func GetSignedProposal() (*peer.SignedProposal, error)
Parameters:
  • none
Returns:
  • *peer.SignedProposal - Contains the required signed proposal object.
  • error - Contains any errors if present.
model.GetArgs
Returns the arguments as array of strings from the chaincode invocation request.
func GetArgs() [][]byte
Parameters:
  • none
Returns:
  • [ ][ ]byte - Contains the arguments passed.
model.GetStringArgs
Returns the arguments intended for the chaincode Init and Invoke as a string array.
func GetStringArgs() []string
Parameters:
  • none
Returns:
  • []string - Contains the required arguments as a string array.
model.getId
When the asset has a derived key as Id, you can use this method to get a derived ID. This method will return an error if the derived key contains %t (timestamp).
Parameters:
  • object - Object should contain all the properties on which the derived key is dependent.
Returns:
  • Returns the derived key as a string.
Example:
func (t *Controller) CustomGetterForSupplier(License string, Name string)(interface{}, error){
   var asset Supplier
   asset.License = License
   asset.Name = Name
   id,err := model.GetId(&asset)
   if err !=nil {
      return nil, fmt.Errorf("error in getting ID %s", err.Error())
   }
   return t.GetSupplierById(id)
}

Utility Package

The following methods in the utility package may be useful:

Util.CreateModel
Parses the provided JSON string and creates an asset object of the provided type.
func CreateModel(obj interface{}, inputString string) error
Parameters:
  • inputString (string) - The input JSON string from which the object is to be created.
  • obj (interface{}) - The reference of the object that is to be created from the JSON string. This object will store the created model which is also validated as per validator tags.
Returns:
  • error - Contains any errors found while creating or validating the asset.
util.ConvertMapToStruct
Convert the provided map into object of provided type.
func ConvertMapToStruct(inputMap map[string](interface{}), resultStruct
interface{}) error
Parameters:
  • inputMap (map[string](interface{})) - Map which needs to be converted into the asset object.
  • resultStruct (interface{}) - The reference of the required asset object which needs to be generated from the map. Contains the result asset object required.
Returns:
  • error - Contains any errors found while creating or validating the asset.

Controller

The Controller.go file implements the CRUD and custom methods for the assets.

You can create any number of classes, functions, or files, but only those methods that are defined on chaincode struct are invokable from outside, the rest of them are hidden.

CRUD Methods

As described in Input Specification File, you can specify which CRUD methods you want generated in the specification file. For example, if you selected to generate all methods, the result would be similar to:

//	
//Supplier
//	
func (t *ChainCode) CreateSupplier(inputString string) (interface{}, error) {
    var obj Supplier
    err := util.CreateModel(&obj, inputString)
    if err != nil {
        return nil, err   
    }
    return model.Save(&obj)
}

func (t *ChainCode) GetSupplierById(id string) (interface{}, error) {
    asset, err := model.Get(id) 
    return asset, err
}

func (t *ChainCode) UpdateSupplier(inputString string) (interface{}, error) {
    var obj Supplier
    err := util.CreateModel(&obj, inputstring)
    if err != nil {   
        return nil, err
    }
return model.Update(&obj)
}

func (t *ChainCode) DeleteSupplier(id string) (interface{}, error) { 
    return model.Delete(id)
}

func (t *ChainCode) GetSupplierHistoryById(id string) (interface{}, error) { 
    historyArray, err := model.GetHistoryByld(id) 
    return historyArray, err
}

func (t *ChainCode) GetSupplierByRange(startkey string, endKey string) (interface{}, error) { 
    assetArray, err := model.GetByRange(startkey, endKey) 
    return assetArray, err
}

Custom Methods

The following custom methods were generated from our example specification file.

The executeQuery shows how SQL rich queries can be called. The validators against the arguments are added automatically by Blockchain App Builder based on the type of the argument specified in the specification file.

You can implement the functionality as per the business logic.

//	
//Custom Methods
//			
/*
*	BDB sql rich queries can be executed in OBP CS/EE.
*	This method can be invoked only when connected to remote OBP CS/EE network.
*/
func (t *ChainCode) ExecuteQuery(inputQuery string) (interface{}, error) { 
    resultArray, err := model.Query(inputQuery) 
    return resultArray, err
}

func (t *ChainCode) FetchRawMaterial(supplierId string, rawMaterialSupply int) (interface{}, error) {
    return nil, nil
}

func (t *ChainCode) GetRawMaterialFromSupplier(manufacturerId string, supplierId string, rawMaterialSupply int) (interface{} error) { 
    return nil, nil
}

Func (t *ChainCode) CreateProducts(manufacturerId string, rawMaterialConsumed int, productsCreated int) (interface{}, error) { 
    return nil, nil
}

func (t *ChainCode) SendProductsToDistribution() (interface{}, error) { 
    return nil, nil
}
For Go chaincodes, every custom method should return two values: empty interface, error. For example:
func (t *Controller) FetchRawMaterial(supplierId string, rawMaterialSupply int) (interface{}, error) { 
    return nil, nil
}

Init Method

We have provided one init method in the controller with an empty definition. This method will be called by the Hyperledger Fabric Init method during first time instantiation or upgrade of a chaincode.

type Controller struct {
}
func (t *Controller) Init(args string) (interface{}, error) 
    { return nil, nil
}

If you would like to initialize any application state at this point, you can use this method to do that.