The Invoke method

The Invoke method is invoked whenever the state of the blockchain is queried or modified.

All create, read, update, and delete (CRUD) operations on the assets held on the ledger are encapsulated by the Invoke method.

The invocation of this method happens when a transaction is created by the invoking client. When the ledger is queried for the state (that is, one or more assets are retrieved but the state of the ledger is not modified), the contextual transaction will be discarded by the client after receiving the response of Invoke. Once the ledger has been modified, the modifications will be recorded into the transaction. After receiving a response for the transaction to be recorded on the ledger, the client will submit that transaction to an ordering service. An empty Invoke method is shown in the following snippet:

func (t *TradeWorkflowChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
fmt.Println("TradeWorkflow Invoke")
}

Typically, the implementation of chaincode will contain multiple queries and modification functions. If these functions are very simple, they can be directly implemented in the body of the Invoke method. However, a more elegant solution is to implement each function independently and then invoke them from the Invoke method.

The SHIM API provides several functions for retrieving the invocation arguments of the Invoke method. These are listed in the following snippet. It is up to the developer to choose the meaning and order of the arguments; however, it is customary for the first argument of the Invoke method to be the name of the function, with the following arguments the arguments of that function.

// Returns the first argument as the function name and the rest of the arguments as parameters in a string array.
// The client must pass only arguments of the type string.
func GetFunctionAndParameters() (string, []string)

// Returns all arguments as a single string array.
// The client must pass only arguments of the type string.
func GetStringArgs() []string

// Returns the arguments as an array of byte arrays.
func GetArgs() [][]byte

// Returns the arguments as a single byte array.
func GetArgsSlice() ([]byte, error)

In the following snippet, the arguments of the invocation are retrieved in line 1 using the stub.GetFunctionAndParameters function. From line 3 onwards, a series of if conditions pass the execution, along with the arguments, into the requested function (requestTrade, acceptTrade, and so on). Each of these functions implement their functionality separately. If a non-existent function is requested, the method returns an error indicating that the requested function does not exist, as shown in line 18:

    function, args := stub.GetFunctionAndParameters()

if function == "requestTrade" {
// Importer requests a trade
return t.requestTrade(stub, creatorOrg, creatorCertIssuer, args)
} else if function == "acceptTrade" {
// Exporter accepts a trade
return t.acceptTrade(stub, creatorOrg, creatorCertIssuer, args)
} else if function == "requestLC" {
// Importer requests an L/C
return t.requestLC(stub, creatorOrg, creatorCertIssuer, args)
} else if function == "issueLC" {
// Importer's Bank issues an L/C
return t.issueLC(stub, creatorOrg, creatorCertIssuer, args)
} else if function == "acceptLC" {
...

return shim.Error("Invalid invoke function name")

As you can see, the Invoke method is a suitable place for any shared code that is needed for extracting and validating arguments that will be used by the requested functions. In the following section, we will look at the access control mechanism and place some of the shared access control code into the Invoke method.