Chaincode For Developers - Hyperledger-Fabricdocs Master Documentation
Chaincode For Developers - Hyperledger-Fabricdocs Master Documentation
In the following sections, we will explore chaincode through the eyes of an application
developer. We’ll present a simple chaincode sample application and walk through the
purpose of each method in the Chaincode Shim API.
Chaincode API
Note
There is another set of chaincode APIs that allow the client (submitter) identity to be
used for access control decisions, whether that is based on client identity itself, or the
org identity, or on a client identity attribute. For example an asset that is represented
as a key/value may include the client’s identity, and only this client may be authorized
to make updates to the key/value. The client identity library has APIs that chaincode
can use to retrieve this submitter information to make such access control decisions.
Go
node.js
Java
whose methods are called in response to received transactions. In particular the Init
Go
node.js
Java
which is used to access and modify the ledger, and to make invocations between
chaincodes.
In this tutorial using Go chaincode, we will demonstrate the use of these APIs by
implementing a simple chaincode application that manages simple “assets”.
Simple Asset Chaincode
Our application is a basic sample chaincode to create assets (key-value pairs) on the
ledger.
If you haven’t been doing programming in Go, you may want to make sure that you have
Go Programming Language installed and your system properly configured.
Now, you will want to create a directory for your chaincode application as a child
directory of $GOPATH/src/ .
Now, let’s create the source file that we’ll fill in with code:
touch sacc.go
Housekeeping
First, let’s start with some housekeeping. As with every chaincode, it implements the
Chaincode interface in particular, Init and Invoke functions. So, let’s add the Go import
statements for the necessary dependencies for our chaincode. We’ll import the chaincode
shim package and the peer protobuf package. Next, let’s add a struct SimpleAsset as a
receiver for Chaincode shim functions.
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
Note
Note that chaincode upgrade also calls this function. When writing a chaincode that
will upgrade an existing one, make sure to modify the Init function appropriately. In
particular, provide an empty “Init” method if there’s no “migration” or nothing to be
initialized as part of the upgrade.
Next, we’ll retrieve the arguments to the Init call using the
ChaincodeStubInterface.GetStringArgs function and check for validity. In our case, we are
expecting a key-value pair.
Next, now that we have established that the call is valid, we’ll store the initial state in the
ledger. To do this, we will call ChaincodeStubInterface.PutState with the key and value
passed in as the arguments. Assuming all went well, return a peer.Response object that
indicates the initialization was a success.
// Init is called during chaincode instantiation to initialize any
// data. Note that chaincode upgrade also calls this function to reset
// or to migrate data, so be careful to avoid a scenario where you
// inadvertently clobber your ledger's data!
func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {
// Get the args from the transaction proposal
args := stub.GetStringArgs()
if len(args) != 2 {
return shim.Error("Incorrect arguments. Expecting a key and a value")
}
As with the Init function above, we need to extract the arguments from the
ChaincodeStubInterface . The Invoke function’s arguments will be the name of the
chaincode application function to invoke. In our case, our application will simply have two
functions: set and get , that allow the value of an asset to be set or its current state to
be retrieved. We first call ChaincodeStubInterface.GetFunctionAndParameters to extract
the function name and the parameters to that chaincode application function.
Next, we’ll validate the function name as being either set or get , and invoke those
chaincode application functions, returning an appropriate response via the shim.Success
or shim.Error functions that will serialize the response into a gRPC protobuf message.
// Invoke is called per transaction on the chaincode. Each transaction is
// either a 'get' or a 'set' on the asset created by Init function. The Set
// method may create a new asset by specifying a new key-value pair.
func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Extract the function and args from the transaction proposal
fn, args := stub.GetFunctionAndParameters()
As noted, our chaincode application implements two functions that can be invoked via
the Invoke function. Let’s implement those functions now. Note that as we mentioned
above, to access the ledger’s state, we will leverage the ChaincodeStubInterface.PutState
and ChaincodeStubInterface.GetState functions of the chaincode shim API.
// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 2 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
}
Finally, we need to add the main function, which will call the shim.Start function. Here’s
the whole chaincode program source.
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
// Set stores the asset (both key and value) on the ledger. If the key exists,
// it will override the value with the new one
func set(stub shim.ChaincodeStubInterface, args []string) (string, error) {
if len(args) != 2 {
return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value")
}
Building Chaincode
go get -u github.com/hyperledger/fabric/core/chaincode/shim
go build
Assuming there are no errors, now we can proceed to the next step, testing your
chaincode.
Normally chaincodes are started and maintained by peer. However in “dev mode”,
chaincode is built and started by the user. This mode is useful during chaincode
development phase for rapid code/build/run/debug cycle turnaround.
We start “dev mode” by leveraging pre-generated orderer and channel artifacts for a
sample dev network. As such, the user can immediately jump into the process of
compiling chaincode and driving calls.
Install Hyperledger Fabric Samples
If you haven’t already done so, please Install Samples, Binaries and Docker Images.
cd chaincode-docker-devmode
docker-compose -f docker-compose-simple.yaml up
The above starts the network with the SingleSampleMSPSolo orderer profile and launches
the peer in “dev mode”. It also launches two additional containers - one for the chaincode
environment and a CLI to interact with the chaincode. The commands for create and join
channel are embedded in the CLI container, so we can jump immediately to the chaincode
calls.
Terminal 2 - Build & start the chaincode
root@d2629980e76b:/opt/gopath/src/chaincode#
cd sacc
go build
The chaincode is started with peer and chaincode logs indicating successful registration
with the peer. Note that at this stage the chaincode is not associated with any channel.
This is done in subsequent steps using the instantiate command.
Chaincode encryption
In certain scenarios, it may be useful to encrypt values associated with a key in their
entirety or simply in part. For example, if a person’s social security number or address was
being written to the ledger, then you likely would not want this data to appear in
plaintext. Chaincode encryption is achieved by leveraging the entities extension which is
a BCCSP wrapper with commodity factories and functions to perform cryptographic
operations such as encryption and elliptic curve digital signatures. For example, to
encrypt, the invoker of a chaincode passes in a cryptographic key via the transient field.
The same key may then be used for subsequent query operations, allowing for proper
decryption of the encrypted state values.
For more information and samples, see the Encc Example within the fabric/examples
directory. Pay specific attention to the utils.go helper program. This utility loads the
chaincode shim APIs and Entities extension and builds a new class of functions (e.g.
encryptAndPutState & getStateAndDecrypt ) that the sample encryption chaincode then
leverages. As such, the chaincode can now marry the basic shim APIs of Get and Put
govendor init
govendor add +external // Add all external package, or
govendor add github.com/external/pkg // Add specific external package
This imports the external dependencies into a local vendor directory.
peer chaincode package and peer chaincode install operations will then include code
associated with the dependencies into the chaincode package.