This is the Chaincode for developers chapter in the official document of Hyperledger Fabric V1.0.
The first translation, the wrong place also asked netizens to point out that we learn together progress.
Original address (http://hyperledger-fabric.readthedocs.io/en/latest/chaincode4ade.html) chain code is what
Chaincode is a program written with go, which eventually implements a defined interface in other programming languages, such as Java. The chain code runs in a secure Docker container that is isolated from the endorsement node. Chaincode initializes and manages the ledger status through transactions submitted by the application.
A chain code is usually used to handle the business logic that a network member agrees on, so it can be considered an "intelligent contract". The state created by the chain code is limited to that chain code and cannot be accessed directly by another chain code. However, in the same network, given the appropriate permissions, a chain code can call another chain code to visit its state.
In the following sections, we will explore chaincode through the eyes of an application developer. We will introduce a simple chain code sample application and introduce each of the methods in the Shim API. Chain Code API
Each chain code program must implement a chain Code interface (Chaincode interface), whose method is invoked when responding to a received transaction. In particular, when the chain code receives an instantiation or update transaction, the initialization Init method is invoked, allowing the chain code to perform any necessary initialization, including the initialization of the application state. The Invoke method is invoked when the response receives an invoke transaction that is used to process the transaction proposal.
Another interface in the chain code "shim" API is Chaincodestubinterface, which is used to access and modify the ledger and call between the chain codes.
In this tutorial, we will demonstrate the use of these APIs by implementing a chain code application to manage simple assets. Simple asset Chain Code
Our application is a basic example chain code used to create assets (key-value pairs) on the ledger. choose where to store the source code
If you do not have a go language environment, make sure that you install and configure it correctly.
Now you want to create a subdirectory for your chain code application under $*gopath*/src/.
To make things simpler, we execute the following command:
Mkdir-p $GOPATH/src/sacc && cd $GOPATH/SRC/SACC
Now let's create the source file and write the code:
Touch Sacc.go
Housekeeping Preparation Work
First of all, let's do some preparatory work first. As with each chain code, the chain code is the implementation of the chain Code interface Chaincode interface
Https://github.com/hyperledger/fabric/blob/master/core/chaincode/shim/interfaces.go#L28
Especially the init and invoke functions.
So we're going to import the necessary dependency packets for the chain code.
We will import the chain code of the shim package and the peer Protobuf package.
Package main
Import (
"FMT"
"Github.com/hyperledger/fabric/core/chaincode/shim" "
github.com/ Hyperledger/fabric/protos/peer "
)
Initializing chain Code
Next we're going to implement an initialization function.
The Init is called during Chaincode instantiation to initialize any data.
Func (t *simpleasset) Init (stub shim. Chaincodestubinterface) Peer. Response {
}
Note that the chain code upgrade will also call this function. When upgrading an existing chain code, be sure to modify the corresponding initialization init function. In particular, it is more important to provide an empty initialization method if there is no migration or if there is nothing to initialize as part of the upgrade.
Next, we will use the Chaincodestubinterface.getstringargs function to take out the parameters of the INIT call and check its validity. In our example, we expect to get a key-value pair.
The 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 is 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 ")
}
}
Next, we're going to build a valid function call, and we'll store the initial state in the ledger. To do this, we pass the keys and values as parameters to the Chaincodestubinterface.putstate method. Assuming all goes well, a peer that represents the initialization has succeeded is returned. Response object.
The 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 is 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 ")
}
//Set up any variables or assets this by calling stub. Putstate ()
//We store the key and the value on the ledger
err: = Stub. Putstate (Args[0], []byte (Args[1]))
If Err!= nil {return
shim. Error (FMT. Sprintf (' Failed to create asset:%s ', args[0])
} return
shim. Success (nil)
}
Call chain Code
First, we first create a function signature of the Invoke function
The Invoke is called/transaction on the Chaincode. Each transaction are
//either a ' get ' or a ' set ' on the asset created by Init function. The ' Set '
//may create a new asset by specifying a new Key-value pair.
Func (t *simpleasset) Invoke (stub shim. Chaincodestubinterface) Peer. Response {
}
As with the Init function above, we need to extract the parameters from the Chaincodestubinterface. The arguments to the Invoke function will be the name of the Chaincode application function to invoke. In our case, our application will have only two features: set and get, which allows you to set the value of an asset or retrieve its current state. We first call Chaincodestubinterface.getfunctionandparameters to extract the function name and parameters of the code application.
The Invoke is called/transaction on the Chaincode. Each transaction are
//either a ' get ' or a ' set ' on the asset created by Init function. The Set
//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 ()
}
Next, we'll set the function name to set or get, and call these chain code application functions and pass shim. The success or Shim.error function returns the appropriate response, which serializes the response to a GRPC protobuf message.
The Invoke is called/transaction on the Chaincode. Each transaction are
//either a ' get ' or a ' set ' on the asset created by Init function. The Set
//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 ()
var result string
var err error
If fn = ' Set ' {result
, err = set (stub, args) c9/>} else {result
, err = get (stub, args)
}
If err!= nil {return
shim. Error (Err. Error ())
}//Return of result as
Success payload return
Shim. Success ([]byte (Result)
}
Implementing a chain code application
As noted above, our Chaincode application implements two functions that can be invoked through the invoke function. Now we're going to implement these features. Note that, as mentioned, we will use the chaincodestubinterface.putstate and Chaincodestubinterface.getstate functions in the Chaincode Shim API to access the ledger.
Set stores the asset (both key and value) on the ledger. If the key exists,//It'll 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 "} Err: = Stub. Putstate (Args[0], []byte (Args[1])) If Err!= nil {return "", FMT. Errorf ("Failed to set Asset:%s", Args[0])} return args[1], nil}//Get returns the value of the specified Asse T key func get (stub shim. Chaincodestubinterface, args []string) (string, error) {if Len (args)!= 1 {return "", FMT. Errorf ("incorrect arguments.") Expecting a key ")} value, Err: = Stub. GetState (Args[0]) If Err!= nil {return "", FMT. Errorf ("Failed to get asset:%s with Error:%s", Args[0], err)} if value = = Nil {return "", FMT. Errorf (' Asset not found:%s ', args[0])} return string (value), nil}
writing the main function
Finally we need to write a call to shim. The main function of the start function.
The source code for the entire chain is shown below:
Package main import ("FMT" "Github.com/hyperledger/fabric/core/chaincode/shim" "Github.com/hyperledger/fabr Ic/protos/peer ")//Simpleasset implements a simple chaincode to manage an asset type simpleasset struct {}//Init is Called during Chaincode instantiation to initialize any//data.
Note that Chaincode upgrade also calls the This function to reset//to migrate 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 ")}//Set up any variables or assets this by calling stub. Putstate ()//We store the key and the value on the ledger err: = Stub. Putstate (Args[0], []byte (Args[1])) If Err!= nil {return shim. Error (FMT. Sprintf (' Failed to create asset:%s ', args[0])} return shim. Success (NIL)}//Invoke is Called/Transaction on the Chaincode. Each transaction are//either a ' get ' or a ' set ' on the asset created by Init function.
The Set//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 () var result string var err error if fn = ' Set ' {result, err = set (stu b, args)} else {//assume ' get ' even if FN be nil result, err = get (stub, args)} If Err!= nil {return shim. Error (Err. Error ())}//Return of result as success payload return shim. Success ([]byte)}//Set stores the asset (both key and value) on the ledger. If the key exists,//It'll 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 "} Err: = Stub. Putstate (Args[0], []byte (Args[1])) If Err!= nil {return "", FMT. Errorf ("Failed to set Asset:%s", Args[0])} return args[1], nil}//Get returns the value of the specified Asse T key func get (stub shim. Chaincodestubinterface, args []string) (string, error) {if Len (args)!= 1 {return "", FMT. Errorf ("incorrect arguments.") Expecting a key ")} value, Err: = Stub. GetState (Args[0]) If Err!= nil {return "", FMT. Errorf ("Failed to get asset:%s with Error:%s", Args[0], err)} if value = = Nil {return "", FMT. Errorf (' Asset not found:%s ', args[0])} return string (value), nil}//main function starts up the Chaincode in The container during instantiate func main () {if err: = shim. Start (New (Simpleasset)); Err!= Nil {fmt. Printf ("Error starting Simpleasset Chaincode:%s", err)}}
Compile chain Code
Now let's compile the chain code:
Go Build
If there is no compilation error, we can proceed to the next step and test the chain code. testing with development mode
Usually the chain code is started and maintained by the peer node. However, in "development mode," the chain code is built and started by the user. This pattern is useful during the fast coding/building/running/debugging of the chain code development phase.
We started "Development mode" by leveraging the example of pre-generated sorting and channel artifacts from the sample development network. Therefore, the user can immediately enter the process of compiling the chain code and driving the call. Installing the Sample network
If you do not have a sample network, please install Hyperledger Fabric samples first.
After the installation is complete, enter the Chaincode-docker-devmode folder in Fabric-samples:
CD Chaincode-docker-devmode
Download Docker mirrors
We need four Docker mirrors for "development mode" to run the provided Docker compose scripts. If you installed the Fabric-samples repo clone and installed the Operation Guide to download the binaries for a specific platform, you should have installed the necessary Docker images locally.
If you choose to download the mirrors manually, you must mark them back as latest.
Execute the Docker images command to display your local Docker registration information. You should see something similar to the following:
Docker images REPOSITORY TAG IMAGE ID CREATED SIZE Hyperledger/fabric-tools latest e09f38f8928d 4 hours ago 1.32 GB H Yperledger/fabric-tools x86_64-1.0.0-rc1-snapshot-f20846c6 e09f38f8928d 4 hours ago 1.32 GB Hyperle Dger/fabric-orderer latest 0df93ba35a25 4 hours ago 179 MB HYPERLEDGER/FA Bric-orderer x86_64-1.0.0-rc1-snapshot-f20846c6 0df93ba35a25 4 hours ago 179 MB Hyperledger/fabric-pe ER latest 533aec3f5a01 4 hours ago MB hyperledger/fabric-peer X86_64-1.0.0-rc1-snapshot-f20846c6 533aec3f5a01 4 hours ago MB hyperledger/fabric-ccenv late St 4b70698a71d3 4 hours ago 1.29 GB Hyperledger/fabric-ccenv x86_64-1.0 .0-rc1-snapshot-f20846c6 4b70698a71d3 4 hours ago 1.29 GB
If you obtain a mirror by downloading a platform-specific binary file, the other mirrored files will be listed. However, we only care about these four.
Now open 3 terminals, each of which goes into the Chaincode-docker-devmode folder. Terminal 1-Start network
Docker-compose-f Docker-compose-simple.yaml up
The above command starts the network using the Singlesamplemspsolo sort node profile and starts the peer node in development mode. It also starts with two additional containers-one for the chaincode environment and one for the CLI that interacts with the chain code. Because the commands used to create and connect the channels are embedded in the CLI container, we can immediately jump to the chain code call. Terminal 2-compile and start chain code
Docker exec-it Chaincode Bash
As shown below:
root@d2629980e76b:/opt/gopath/src/chaincode#
Now compile the chain code
CD SACC Go build
Run chain code
core_peer_address=peer:7051 core_chaincode_id_name=mycc:0. /sacc
The chain code is started with the peer node and is displayed in the chain code log after the peer node is successfully registered. Note that at this stage, the chain code is not related to any channel. Subsequent steps will be completed in the instantiation command. Terminal 3-use chain code
Even if you are in –peer-chaincodedev mode, you must still install the chain code so that the lifecycle system chain code can be checked properly. In-peer-chaincodedev mode, this requirement may be removed in the future. We will use the CLI container to drive these calls.
Docker exec-it CLI Bash
Cd.. /
Peer Chaincode install-p chaincodedev/chaincode/sacc-n mycc-v 0 Peer Chaincode instantiate-n mycc-v 0-c
' { "Args": ["a", "Ten"]} '-C MYC
Now execute one call and change the value of a to 20.
Peer Chaincode invoke-n mycc-c ' {Args ': [' Set ', ' a ', ' ']} '-C MYC
Finally, query A's value and we'll get 20
Peer Chaincode query-n mycc-c ' {Args ': [' query ', ' A ']} '-C MYC
test the new chain code
By default, we only install SACC. However, you can easily test different chain codes by adding new chain codes to the Chaincode subdirectory and restarting the network. At this point they will be accessible in your Chaincode container.