Hyperledger Fabric:V2.5.4
write first
Reference for using Fabric to build a custom network:https://blog.csdn.net/yeshang_lady/article/details/134113296
Using Fabric Reference for creating an application channel:https://blog.csdn.net/yeshang_lady/article/details/134668458
Next, we will introduce how to create an application channel in a customized network Deploy and execute chaincode on the channel.
1 Chaincode deployment
The deployment of chain code in Fabric generally includes the following steps: writing chain code->packaging chain code->installing chain code->instantiating chain code->deploying chain code, etc. The following steps will be introduced one by one.
1.1 Write chain code
After creating the network and application channels, go back to the finance_network
directory to create the chaincode directoryusersChaincode
. Then create a chaincode file under the chaincode directoryusersChaincode
. The code is as follows (the code in this file is written using Constructed using the chaincode example in ):asset-transfer.go
test-network
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type SmartContract struct {
contractapi.Contract
}
type Asset struct {
AppraisedValue int `json:"AppraisedValue"`
Color string `json:"Color"`
ID string `json:"ID"`
Owner string `json:"Owner"`
Size int `json:"Size"`
}
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
assets := []Asset{
{
ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300},
{
ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400},
{
ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500},
{
ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600},
{
ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700},
{
ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800},
}
for _, asset := range assets {
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
err = ctx.GetStub().PutState(asset.ID, assetJSON)
if err != nil {
return fmt.Errorf("failed to put to world state. %v", err)
}
}
return nil
}
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the asset %s already exists", id)
}
asset := Asset{
ID: id,
Color: color,
Size: size,
Owner: owner,
AppraisedValue: appraisedValue,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if assetJSON == nil {
return nil, fmt.Errorf("the asset %s does not exist", id)
}
var asset Asset
err = json.Unmarshal(assetJSON, &asset)
if err != nil {
return nil, err
}
return &asset, nil
}
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}
asset := Asset{
ID: id,
Color: color,
Size: size,
Owner: owner,
AppraisedValue: appraisedValue,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}
return ctx.GetStub().DelState(id)
}
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return assetJSON != nil, nil
}
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) (string, error) {
asset, err := s.ReadAsset(ctx, id)
if err != nil {
return "", err
}
oldOwner := asset.Owner
asset.Owner = newOwner
assetJSON, err := json.Marshal(asset)
if err != nil {
return "", err
}
err = ctx.GetStub().PutState(id, assetJSON)
if err != nil {
return "", err
}
return oldOwner, nil
}
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) {
resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
if err != nil {
return nil, err
}
defer resultsIterator.Close()
var assets []*Asset
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var asset Asset
err = json.Unmarshal(queryResponse.Value, &asset)
if err != nil {
return nil, err
}
assets = append(assets, &asset)
}
return assets, nil
}
func main() {
assetChaincode, err := contractapi.NewChaincode(&SmartContract{
})
if err != nil {
log.Panicf("Error creating asset-transfer-basic chaincode: %v", err)
}
if err := assetChaincode.Start(); err != nil {
log.Panicf("Error starting asset-transfer-basic chaincode: %v", err)
}
}
Then use commands such asgo mod
in the chaincode directory to initialize the module. Specifically include the following:
#先进入usersChaincode目录下
go mod init
sudo chmod -R 777 go.mod
#下载链码中的需要的模块信息
go get github.com/hyperledger/fabric-contract-api-go/contractapi
sudo chmod -R 777 go.sum
#将项目的依赖库复制都vendor目录中去
GO111MODULE=on go mod vendor
Tips: The following points need to be noted
- First of all, when generating the
go.mod
file, the go language version is automatically specified as the go language version number installed in the current environment. For examplego 1.21.3
. But the go language version number ingo.mod
should consist of two numbers separated by dots, such as 1.13, 1.14, etc. - Secondly,
go.mod
The go language version in the file must also consider the compatible version of the current third-party package here. - Again,
go.mod
the file must be modified and thego mod tidy
command must be run to regeneratego.sum
the file. - Finally, if you encounter the following problems when installing the chain code, you need to go back and modify it< /span>), and then regenerate (refer to file to file. Here you need to change the go language version in the also needs to repackage the chain code. For convenience, it is best to modify thefile in advance.
invalid go version 1.21.3: must match format 1.23
go.mod
go.mod
go 1.17
fabric-samples
go.sum
go.mod
- In addition, if you do not execute the
GO111MODULE=on go mod vendor
command, you will encounter a timeout problem during the subsequent installation of the chain code :Error: chaincode install failed with status: 500 ... error sending: timeout expired while executing transaction
.
1.2 Packaging chain code
Packaging chaincode refers to packaging the chaincode file into a file in tar format. You can use the peer lifecycle chaincode package
command. The specific execution command is as follows:
#先使用cd命令跳转到finance_network目录下
export FABRIC_CFG_PATH=$PWD/config
#basic即为链码的名字
peer lifecycle chaincode package basic.tar.gz --path usersChaincode --lang "golang" --label basic_1.0.1
wherebasic
is the chain code name. After the code is executed, you will see the file in the finance_network
directory. Tips: The peer lifecycle chaincode package command only packages the chaincode into a tar format file. This process does not require interaction with specific peer nodes, so the execution of this command does not require binding the node in advance. . basic.tar.gz
1.3 Install chaincode
Installing the chain code is mainly responsible for deploying the chain code to each Peer node that needs to execute the chain code. Install the chain code to the local file system of the Peer node by calling the peer lifecycle chaincode install
command of the Peer node. The following only describes the chain code installation process using the peer0.org1.finance.com
node. The details are as follows:
#先设置环境变量将peer CLI绑定到peer0.org1.finance.com节点上
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=$PWD/organizations/peerOrganizations/org1.finance.com/peers/peer0.org1.finance.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=$PWD/organizations/peerOrganizations/org1.finance.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=localhost:7051
export FABRIC_CFG_PATH=$PWD/config
# 安装链码
peer lifecycle chaincode install basic.tar.gz
The results are as follows:
You can use peer lifecycle chaincode queryinstalled
to view the chain code that has been installed on the peer node. The details are as follows:
In addition, the chain code is also installed on peer0.org2.finance.com
, and the installation steps are omitted here. Tips: Although 3 peer nodes are created in the fabric_test network, the chain code does not necessarily need to be installed on all peer nodes.
1.4 Instantiate chaincode
- Use the
peer lifecycle chaincode approveformyorg
command to complete the organization's approval of the chaincode deployment. Assume that both organizations need to agree on the deployment of chaincode. Here we takeOrg1
as an example. The specific code is as follows:
export ORDERER_CA=$PWD/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/msp/tlscacerts/tlsca.finance.com-cert.pem
export PACKAGE_ID=$(peer lifecycle chaincode calculatepackageid basic.tar.gz)
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.finance.com --tls --cafile "$ORDERER_CA" --channelID channel1 --name basic --version 1.0 --sequence 1 --package-id ${PACKAGE_ID}
The result is as follows:
- Use the
peer lifecycle chaincode checkcommitreadiness
command to check the commit readiness of the chaincode definition. The specific commands are as follows:
peer lifecycle chaincode checkcommitreadiness --channelID channel1 --name basic --version 1.0 --sequence 1
The execution results are as follows:
- Use the
peer lifecycle chaincode commit
command to submit the transaction defined by the chaincode. The command is as follows:
peer lifecycle chaincode commit -o localhost:7050 --orderdeTLSHostnameOverride orderer.finance.com --tls --cafile "$ORDERER_CA" --channelID channel1 --name basic --version 1.0 --sequence 1 --peerAddresses localhost:7051 --tlsRootCertFiles "${
PWD}/organizations/peerOrganizations/org1.finance.com/peers/peer0.org1.finance.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${
PWD}/organizations/peerOrganizations/org2.finance.com/peers/peer0.org2.finance.com/tls/ca.crt"
The code execution result is as follows:
You can use the following code to determine whether the submission is successful. The details are as follows:
peer lifecycly chaincode querycommitted -C channel1 -n basic
The execution result is as follows:
At this point, the instantiation of the chain code has been completed. You can usedocker ps -a
to see the container information of the chaincode. The details are as follows:
2 Chaincode execution
Regarding the execution of chain code, only two commands are introduced here.
peer chaincode invoke
: The chain code can be made to execute customized business logic and change the status in the blockchain ledger. Examples are as follows:
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.finance.com --tls --cafile "${
PWD}/organizations/ordererOrganizations/finance.com/orderers/orderer.finance.com/msp/tlscacerts/tlsca.finance.com-cert.pem" -C channel1 -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${
PWD}/organizations/peerOrganizations/org1.finance.com/peers/peer0.org1.finance.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${
PWD}/organizations/peerOrganizations/org2.finance.com/peers/peer0.org2.finance.com/tls/ca.crt" -c '{"Args":["InitLedger"]}'
The execution results are as follows:
peer chaincode query
: Data can be obtained from the blockchain ledger without any status updates to the ledger.
peer chaincode query -C channel -n basic -c '{
"Args":["GetAllAssets
The execution results are as follows:
In order to illustrate thatpeer chaincode query
has not modified the ledger, execute the following two commands, as follows:
#删除id为asset6的记录
peer chaincode query -C channel1 -n basic -c '{"Args":["DeleteAsset","asset6"]}'
#读取id为asset6的记录
peer chaincode query -C channel1 -n basic -c '{"Args":["ReadAsset","asset6"]}'
The result is as follows: