The Invoke() function, as its name implies, is executed when an application wants to invoke a specific function in the Chaincode. The calling application will pass the particular function name to be executed, with the needed arguments. In our code we will define the following Invoke() function:
func (t *FoodContract) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
if function == "createRawFood" {
return t.createRawFood(stub, args)
} else if function == "manufactureProcessing" {
return t.manufactureProcessing(stub, args)
} else if function == "wholesalerDistribute" {
return t.wholesalerDistribute(stub, args)
} else if function == "initiateShipment" {
return t.initiateShipment(stub, args)
} else if function == "deliverToRetail" {
return t.deliverToRetail(stub, args)
} else if function == "completeOrder" {
return t.completeOrder(stub, args)
} else if function == "query" {
return t.query(stub, args)
}
return shim.Error("Invalid function name")
}
The Invoke function is called per transaction on the Chaincode, with ChaincodeStubInterface being passed as an argument, and the GetFunctionAndParameters returning the function name and arguments. Based on the received function name, it invokes the appropriate Chaincode application method.
To keep it simple, and to avoid any irrelevant Golang coding details, we will present a single example of an invoked Chaincode function—createRawFood. The other Chaincode functions associated with subsequent steps in the chain will be similar to this method:
func (f *FoodContract) createRawFood(stub shim.ChaincodeStubInterface, args []string) pb.Response {
orderId := args[0]
foodBytes, _ := stub.GetState(orderId)
fd := food{}
json.Unmarshal(foodBytes, &fd)
if fd.Status == "order initiated" {
fd.FoodId = "FISH_1"
currentts := time.Now()
fd.RawFoodProcessDate = currentts.Format("2006-01-02 15:04:05")
fd.Status = "raw food created"
} else {
fmt.Printf("Order not initiated yet")
}
foodBytes, _ = json.Marshal(fd)
stub.PutState(orderId, foodBytes)
return shim.Success(nil)
}
As you will notice, the createRawFood method accepts a ChaincodeStubInterface stub and command-line inputs as arguments. We set orderId to the value of the first command-line argument. Then, we query food data from the Fabric and convert encoded data to a readable JSON format by using jsonUnmarshal. We check using the returned data, if the current status is "order initiated" before updating the food asset process using a system date with the format YYYY-mm-dd hh:mm:dd. In Go, we use currentts.Format("2006-01-02 15:04:05") to return the date timestamp in the specified format. We also update the food status as raw food created. At the end, we commit food to the blockchain data by calling the stub.PutState method, and we return a successful response to the client using shim.Success.
As we discussed in the previous section, in the food industry supply chain example, we have six kinds of actor: the raw food producers, the manufacturing processors, wholesalers, logistics operators, retailers, and consumers. Consequently, for a complete Chaincode, we will define an associated function to each actor representing their roles and interactions with the blockchain as follows:
- createRawFood: function called to produce the food
- manufatureProcessing: function called to send the food to the manufacturing processor
- wholesaleDistribute: function called to transport the food product to the wholesaler for distribution
- initiateShipment: function called to initiate the shipment process from wholesalers
- deliverToRetail: function called to deliver the food product to the retailers from logistics
- completeOrder: function called to complete the order process after the consumers can pick up their products
The overall process is shown in the following sequence diagram:
Once the customer receives the product, the order is completed and, per consequence, we update the status as completed by calling completeOrder, which is defined as follows:
func (f *FoodContract) completeOrder(stub shim.ChaincodeStubInterface, args []string) pb.Response {
orderId := args[0]
foodBytes, err := stub.GetState(orderId)
fd := food{}
err = json.Unmarshal(foodBytes, &fd)
if err != nil {
return shim.Error(err.Error())
}
if fd.Status == "Retailer started" {
currentts := time.Now()
fd.DeliveryDate = currentts.Format("2006-01-02 15:04:05")
fd.Status = "Consumer received order"
} else {
fmt.Printf("Retailer not initiated yet")
}
foodBytes0, _ := json.Marshal(fd)
err = stub.PutState(orderId, foodBytes0)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}