git branch -a // 查看分支,不出意外的话,此时分支应该处于 release-1.4
git checkout release-1.4 // 切换到 release-1.3 版本
git branch -v
git checkout release-1.4
报错过程:
进入fabric-samples 目录中的first-networkd子目录后,在使用“./byfn.sh —help”时报错:
error getting endorser client for channel: endorser client failed to connect to peer0.org1.example.com:7051: failed to create new connection: context deadline exceeded
报错过程:
进入fabric-samples 目录中的first-networkd子目录后,在使用“./byfn.sh —help”时报错:
error getting endorser client for channel: endorser client failed to connect to peer0.org1.example.com:7051: failed to create new connection: context deadline exceeded
解决方案:
/etc/resolv.conf 注释掉 options timeout:2 attempts:3 rotate single-request-reopen,再次运行可成功。
cat /etc/resolv.conf
sudo chmod 777 /etc/resolv.conf
gedit /etc/resolv.conf
首先创建crypto-config.yaml文件:
../bin/cryptogen generate --config=./crypto-config.yaml
查看目录生成crypto-config
文件夹,证书文件存放在此文件夹,用这些文件进行系统初始化
创建configtx.yaml
文件,文件内容如下:
创建系统初始化文件夹:
mkdir channel-artifacts
生成创世快文件:
export FABRIC_CFG_PATH=$PWD
../bin/configtxgen -profile SampleMultiNodeEtcdRaft -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block byfn-sys-channel ==代表通道名称
运行成功后可看到 channel-artifacts
文件夹下生成了genesis.block
文件。
../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel
运行成功后可看到channel-artifacts文件夹下生成了channel.tx文件
通道名 mychannel
../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP
其中 锚节点 为:
AnchorPeers:
# AnchorPeers defines the location of peers which can be used
# for cross org gossip communication. Note, this value is only
# encoded in the genesis block in the Application section context
- Host: peer0.org1.example.com
Port: 7051
AnchorPeers:
# AnchorPeers defines the location of peers which can be used
# for cross org gossip communication. Note, this value is only
# encoded in the genesis block in the Application section context
- Host: peer0.org2.example.com
Port: 9051
运行成功后可看到channel-artifacts
文件夹下生成了Org1MSPanchors.tx
和Org2MSPanchors.tx
两个文件。
创建base.yaml
文件:
创建docker-compose-local.yaml
文件:
创建.env
文件:
FABRIC_IMG_TAG=latest
SYS_CHANNEL=byfn-sys-channel
NETWORK=local_test
创建网络:
docker network create local_test
docker-compose -f docker-compose-local.yaml up -d
如果运行成功,我们可以用docker ps命令查看运行起来的容器:
创建chaincode文件夹:
cd ..
mkdir chaincode
cd chaincode
创建文件scoreStruct.go:
package main
// 根据实际需求,字段自行增删,以下只供参考
type Score struct {
ObjectType string `json:"docType"`
Name string `json:"Name"` // 姓名
Gender string `json:"Gender"` // 性别
StuID string `json:"StuID"` // 学生学号
Grade string `json:"Grade"` // 年级
Result string `json:"Result"` // 成绩
Time string `json:"Time"` // 插入该数据的时间
Historys []HistoryItem // 当前Score的历史记录
}
type HistoryItem struct { // 交易hash,也是标志ID
TxId string
Score Score
}
创建文件main.go
:
package main
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"fmt"
"github.com/hyperledger/fabric/protos/peer"
)
type ScoreChaincode struct {
}
func (t *ScoreChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response{
return shim.Success(nil)
}
func (t *ScoreChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response{
// 获取用户意图
fun, args := stub.GetFunctionAndParameters()
if fun == "addScore"{
stub.SetEvent("addScore", []byte{})
return t.addScore(stub, args) // 添加成绩
}else if fun == "queryScoreByNameAndGrade" {
stub.SetEvent("queryScoreByNameAndGrade", []byte{})
return t.queryScoreByNameAndGrade(stub, args) // 根据姓名及年级查询成绩,假设不同年级存在同名
}else if fun == "queryScoreDetailByStuID" {
stub.SetEvent("queryScoreDetailByStuID", []byte{})
return t.queryScoreDetailByStuID(stub, args) // 根据学号查询成绩详细信息(包括历史信息)
}else if fun == "updateScore" {
stub.SetEvent("updateScore", []byte{})
return t.updateScore(stub, args) // 根据证书编号更新信息
}else if fun == "delScore"{
stub.SetEvent("delScore", []byte{})
return t.delScore(stub, args) // 根据证书编号删除信息
}
return shim.Error("指定的函数名称错误")
}
func main(){
err := shim.Start(new(ScoreChaincode))
if err != nil{
fmt.Printf("启动ScoreChaincode时发生错误: %s", err)
}
}
创建文件scoreCC.go
:
package main
import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
"encoding/json"
"fmt"
"bytes"
)
const DOC_TYPE = "ScoreObj"
// 保存sco
// args: Score
func PutScore(stub shim.ChaincodeStubInterface, sco Score) ([]byte, bool) {
sco.ObjectType = DOC_TYPE
b, err := json.Marshal(sco) //序列化
if err != nil {
return nil, false
}
// 保存sco状态
err = stub.PutState(sco.StuID, b)
if err != nil {
return nil, false
}
return b, true
}
// 根据学号查询信息
// args: stuID
func GetScoreInfo(stub shim.ChaincodeStubInterface, stuID string) (Score, bool) {
var sco Score
// 根据学号查询信息状态
b, err := stub.GetState(stuID)
if err != nil {
return sco, false
}
if b == nil {
return sco, false
}
// 对查询到的状态进行反序列化
err = json.Unmarshal(b, &sco)
if err != nil {
return sco, false
}
// 返回结果
return sco, true
}
// 根据指定的查询字符串实现富查询
func getEduByQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {
resultsIterator, err := stub.GetQueryResult(queryString)
if err != nil {
return nil, err
}
defer resultsIterator.Close() //记得结束富查询调用
// buffer is a JSON array containing QueryRecords
var buffer bytes.Buffer
bArrayMemberAlreadyWritten := false
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
// Add a comma before array members, suppress it for the first array member
if bArrayMemberAlreadyWritten == true {
buffer.WriteString(",")
}
// Record is a JSON object, so we write as-is
buffer.WriteString(string(queryResponse.Value))
bArrayMemberAlreadyWritten = true
}
fmt.Printf("- getQueryResultForQueryString queryResult:\n%s\n", buffer.String())
return buffer.Bytes(), nil
}
// 添加信息
// args: scoreObject
// 学生号为 key, Score 为 value
func (t *ScoreChaincode) addScore(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 1{
return shim.Error("给定的参数个数不符合要求")
}
var sco Score
err := json.Unmarshal([]byte(args[0]), &sco)
if err != nil {
return shim.Error("反序列化信息时发生错误")
}
// 查重: 身份证号码必须唯一
_, exist := GetScoreInfo(stub, sco.StuID)
if exist {
return shim.Error("要添加的学生号已存在")
}
_, bl := PutScore(stub, sco)
if !bl {
return shim.Error("保存信息时发生错误")
}
return shim.Success([]byte("信息添加成功"))
}
// 根据姓名及年级查询信息
// args: Name, Grade
func (t *ScoreChaincode) queryScoreByNameAndGrade(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 2 {
return shim.Error("给定的参数个数不符合要求")
}
name := args[0]
grade := args[1]
// 拼装CouchDB所需要的查询字符串(是标准的一个JSON串)
// queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"eduObj\", \"CertNo\":\"%s\"}}", CertNo)
queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"%s\", \"Name\":\"%s\", \"Grade\":\"%s\"}}", DOC_TYPE, name, grade)
// 查询数据
result, err := getEduByQueryString(stub, queryString)//getEduByQueryString
if err != nil {
return shim.Error("根据证书姓名及年级查询信息时发生错误")
}
if result == nil {
return shim.Error("根据指定的姓名及年级没有查询到相关的信息")
}
return shim.Success(result)
}
// 根据学号查询详情(溯源)
// args: StuID
func (t *ScoreChaincode) queryScoreDetailByStuID(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 1 {
return shim.Error("给定的参数个数不符合要求")
}
// 根据学号查询sco状态
b, err := stub.GetState(args[0])
if err != nil {
return shim.Error("根据学号查询信息失败")
}
if b == nil {
return shim.Error("根据学号没有查询到相关的信息")
}
// 对查询到的状态进行反序列化
var sco Score
err = json.Unmarshal(b, &sco)
if err != nil {
return shim.Error("反序列化sco信息失败")
}
// 获取历史变更数据
iterator, err := stub.GetHistoryForKey(sco.StuID)
if err != nil {
return shim.Error("根据指定的学号查询对应的历史变更数据失败")
}
defer iterator.Close()
// 迭代处理
var historys []HistoryItem
var hisSco Score
for iterator.HasNext() {
hisData, err := iterator.Next()
if err != nil {
return shim.Error("获取sco的历史变更数据失败")
}
var historyItem HistoryItem
historyItem.TxId = hisData.TxId
json.Unmarshal(hisData.Value, &hisSco)
if hisData.Value == nil {
var empty Score
historyItem.Score = empty
}else {
historyItem.Score = hisSco
}
historys = append(historys, historyItem)
}
sco.Historys = historys
// 返回
result, err := json.Marshal(sco)
if err != nil {
return shim.Error("序列化sco信息时发生错误")
}
return shim.Success(result)
}
// 根据学号更新信息
// args: Score Object
func (t *ScoreChaincode) updateScore(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 1{
return shim.Error("给定的参数个数不符合要求")
}
var info Score
err := json.Unmarshal([]byte(args[0]), &info)
if err != nil {
return shim.Error("反序列化edu信息失败")
}
// 根据学号查询信息
result, bl := GetScoreInfo(stub, info.StuID)
if !bl{
return shim.Error("根据学号查询信息时发生错误")
}
result.Name = info.Name
result.Gender = info.Gender
result.Grade = info.Grade
result.Result = info.Result
result.Time = info.Time
_, bl = PutScore(stub, result)
if !bl {
return shim.Error("保存信息信息时发生错误")
}
return shim.Success([]byte("信息更新成功"))
}
// 根据学号删除信息(暂不对外提供)
// args: StuID
func (t *ScoreChaincode) delScore(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 1{
return shim.Error("给定的参数个数不符合要求")
}
/*var edu Education
result, bl := GetEduInfo(stub, info.EntityID)
err := json.Unmarshal(result, &edu)
if err != nil {
return shim.Error("反序列化信息时发生错误")
}*/
err := stub.DelState(args[0])
if err != nil {
return shim.Error("删除信息时发生错误")
}
return shim.Success([]byte("信息删除成功"))
}
1.可能大家会疑惑区块链为什么还存在删除操作?
答:Fabric区块中存的是动作:“谁在什么时间对某个数据执行了什么操作”,例如:“张三在2020年8月10日插入了key为xxx,value为xx的数据”。而LevelDB或CouchDB存的是状态数据,及当前时间点某个key的状态(值)是什么。
- 为什么要用CouchDB?
**答:简单点讲就是只有用CouchDB才支持富查询等操作,LevelDB是不支持的。 **
mkdir META-INF/statedb/couchdb/indexes
cd META-INF/statedb/couchdb/indexes
创建indexNameGrade.json文件:
{"index":{"fields":["Name","Grade"]},"ddoc":"indexNameGrade", "name":"indexNameGrade","type":"json"}
因为链码中有通过Name和Grade两个字段来进行查询的函数。
创建配置目录:
cd .. # 回到主目录
mkdir sdkConfig
12
创建org1的配置文件org1_config.yaml
:
特别注意其中的目录配置,在organizations下的cryptoPath里一定要是相对目录,从crypto-config的下一级目录开始,因为在文件的开始就已经配置了crypto-config目录的,不然在创建resmgmt Client的时候会提示user not found。
name: "org1-config"
#
# Copyright SecureKey Technologies Inc. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
#
# The network connection profile provides client applications the information about the target
# blockchain network that are necessary for the applications to interact with it. These are all
# knowledge that must be acquired from out-of-band sources. This file provides such a source.
#
# copied from fabric-sdk-go/test/fixtures/config/config_e2e_pkcs11.yaml
#
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
#
version: 1.0.0
#
# The client section used by GO SDK.
#
client:
# Which organization does this application instance belong to? The value must be the name of an org
# defined under "organizations"
organization: Org1
logging:
# Develope can using debug to get more information
# level: debug
level: info
cryptoconfig:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config
# Some SDKs support pluggable KV stores, the properties under "credentialStore"
# are implementation specific
credentialStore:
# [Optional]. Used by user store. Not needed if all credentials are embedded in configuration
# and enrollments are performed elswhere.
path: "/tmp/examplestore"
# [Optional] BCCSP config for the client. Used by GO SDK.
BCCSP:
security:
enabled: true
default:
provider: "SW"
hashAlgorithm: "SHA2"
softVerify: true
level: 256
tlsCerts:
# [Optional]. Use system certificate pool when connecting to peers, orderers (for negotiating TLS) Default: false
systemCertPool: true
# [Optional]. Client key and cert for TLS handshake with peers and orderers
client:
# 使用User1@org1的证书
keyfile: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/tls/client.key
certfile: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/users/User1@org1.example.com/tls/client.cert
################################## General part ##################################
#
# [Optional]. But most apps would have this section so that channel objects can be constructed
# based on the content below. If an app is creating channels, then it likely will not need this
# section.
#
channels:
# name of the channel
mychannel:
# Required. list of orderers designated by the application to use for transactions on this
# channel. This list can be a result of access control ("org1" can only access "ordererA"), or
# operational decisions to share loads from applications among the orderers. The values must
# be "names" of orgs defined under "organizations/peers"
# deprecated: not recommended, to override any orderer configuration items, entity matchers should be used.
# orderers:
# - orderer.example.com
# 不要缺少当前channel的orderer节点
orderers:
- orderer0.example.com
- orderer1.example.com
- orderer2.example.com
# Required. list of peers from participating orgs
peers:
peer0.org1.example.com:
# [Optional]. will this peer be sent transaction proposals for endorsement? The peer must
# have the chaincode installed. The app can also use this property to decide which peers
# to send the chaincode install request. Default: true
endorsingPeer: true
# [Optional]. will this peer be sent query proposals? The peer must have the chaincode
# installed. The app can also use this property to decide which peers to send the
# chaincode install request. Default: true
chaincodeQuery: true
# [Optional]. will this peer be sent query proposals that do not require chaincodes, like
# queryBlock(), queryTransaction(), etc. Default: true
ledgerQuery: true
# [Optional]. will this peer be the target of the SDK's listener registration? All peers can
# produce events but the app typically only needs to connect to one to listen to events.
# Default: true
eventSource: true
# Add other peers in mychannel for byfn
peer1.org1.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
peer0.org2.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
peer1.org2.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
# [Optional]. The application can use these options to perform channel operations like retrieving channel
# config etc.
policies:
#[Optional] options for retrieving channel configuration blocks
queryChannelConfig:
#[Optional] min number of success responses (from targets/peers)
minResponses: 1
#[Optional] channel config will be retrieved for these number of random targets
maxTargets: 1
#[Optional] retry options for query config block
retryOpts:
#[Optional] number of retry attempts
attempts: 5
#[Optional] the back off interval for the first retry attempt
initialBackoff: 500ms
#[Optional] the maximum back off interval for any retry attempt
maxBackoff: 5s
#[Optional] he factor by which the initial back off period is exponentially incremented
backoffFactor: 2.0
#
# list of participating organizations in this network
#
organizations:
Org1:
mspid: Org1MSP
# set msp files path
cryptoPath: peerOrganizations/org1.example.com/users/{username}@org1.example.com/msp
# Add peers for org1
peers:
- peer0.org1.example.com
- peer1.org1.example.com
# [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based
# network. Typically certificates provisioning is done in a separate process outside of the
# runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
# Fabric-CA servers.
certificateAuthorities:
- ca.org1.example.com
#users:
# Admin:
# cert:
# pem: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/tlsca/tlsca.org1.example.com-cert.pem
# the profile will contain public information about organizations other than the one it belongs to.
# These are necessary information to make transaction lifecycles work, including MSP IDs and
# peers with a public URL to send transaction proposals. The file will not contain private
# information reserved for members of the organization, such as admin key and certificate,
# fabric-ca registrar enroll ID and secret, etc.
Org2:
mspid: Org2MSP
cryptoPath: peerOrganizations/org2.example.com/users/{username}@org2.example.com/msp
# Add peers for org2
peers:
- peer0.org2.example.com
- peer1.org2.example.com
certificateAuthorities:
- ca.org2.example.com
# Orderer Org name
ordererorg:
# Membership Service Provider ID for this organization
mspID: OrdererMSP
cryptoPath: ordererOrganizations/example.com/users/{username}@example.com/msp
orderers:
- orderer0.example.com
- orderer1.example.com
- orderer2.example.com
#
# List of orderers to send transaction and channel create/update requests to. For the time
# being only one orderer is needed. If more than one is defined, which one get used by the
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
#
orderers:
orderer0.example.com:
# [Optional] Default: Infer from hostname
url: grpcs://localhost:7050
# these are standard properties defined by the gRPC library
# they will be passed in as-is to gRPC client constructor
grpcOptions:
ssl-target-name-override: orderer0.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# Certificate location absolute path
# Replace to orderer cert path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/ordererOrganizations/example.com/orderers/orderer0.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
orderer1.example.com:
url: grpcs://localhost:8050
grpcOptions:
ssl-target-name-override: orderer1.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
allow-insecure: false
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
orderer2.example.com:
url: grpcs://localhost:9050
grpcOptions:
ssl-target-name-override: orderer2.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
allow-insecure: false
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
#
# List of peers to send various requests to, including endorsement, query
# and event listener registration.
#
peers:
peer0.org1.example.com:
# this URL is used to send endorsement and query requests
# [Optional] Default: Infer from hostname
# 表明使用grpcs协议,设置IP和端口号,使用域名会无法连接
# url: grpcs://peer0.org1.example.com:7051
url: grpcs://localhost:7051
grpcOptions:
ssl-target-name-override: peer0.org1.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
peer1.org1.example.com:
# this URL is used to send endorsement and query requests
# [Optional] Default: Infer from hostname
# 表明使用grpcs协议,设置IP和端口号,使用域名会无法连接
# url: grpcs://peer0.org1.example.com:7051
url: grpcs://localhost:8051
grpcOptions:
ssl-target-name-override: peer1.org1.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
peer0.org2.example.com:
# Replace the port
url: grpcs://localhost:9051
grpcOptions:
ssl-target-name-override: peer0.org2.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
peer1.org2.example.com:
# Replace the port
url: grpcs://localhost:10051
grpcOptions:
ssl-target-name-override: peer1.org2.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
# Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows
# certificate management to be done via REST APIs. Application may choose to use a standard
# Certificate Authority instead of Fabric-CA, in which case this section would not be specified.
#
certificateAuthorities:
ca.org1.example.com:
url: http://localhost:7054
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem
# Client key and cert for SSL handshake with Fabric CA
#client:
# key:
# path: /home/alextan/blockchain/fabric/fabric-samples-1.4/raft-local-test/crypto-config/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
# cert:
# path: /home/alextan/blockchain/fabric/fabric-samples-1.4/raft-local-test/crypto-config/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
# needed to enroll and invoke new users.
registrar:
enrollId: admin
enrollSecret: adminpw
# [Optional] The optional name of the CA.
caName: ca.org1.example.com
ca.org2.example.com:
url: http://localhost:8054
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem
# Client key and cert for SSL handshake with Fabric CA
#client:
# key:
# path: /home/alextan/blockchain/fabric/fabric-samples-1.4/raft-local-test/crypto-config/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
# cert:
# path: /home/alextan/blockchain/fabric/fabric-samples-1.4/raft-local-test/crypto-config/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
# needed to enroll and invoke new users.
registrar:
enrollId: admin
enrollSecret: adminpw
# [Optional] The optional name of the CA.
caName: ca.org2.example.com
entitymatchers:
peer:
- pattern: (\w*)peer0.org1.example.com(\w*)
urlsubstitutionexp: grpcs://localhost:7051
ssltargetoverrideurlsubstitutionexp: peer0.org1.example.com
mappedhost: peer0.org1.example.com
- pattern: (\w*)peer1.org1.example.com(\w*)
urlsubstitutionexp: grpcs://localhost:8051
ssltargetoverrideurlsubstitutionexp: peer1.org1.example.com
mappedhost: peer1.org1.example.com
- pattern: (\w*)peer0.org2.example.com(\w*)
urlsubstitutionexp: grpcs://localhost:9051
ssltargetoverrideurlsubstitutionexp: peer0.org2.example.com
mappedhost: peer0.org2.example.com
- pattern: (\w*)peer1.org2.example.com(\w*)
urlsubstitutionexp: grpcs://localhost:10051
ssltargetoverrideurlsubstitutionexp: peer1.org2.example.com
mappedhost: peer1.org2.example.com
orderer:
- pattern: (\w*)orderer0.example.com(\w*)
urlsubstitutionexp: localhost:7050
ssltargetoverrideurlsubstitutionexp: orderer0.example.com
mappedhost: orderer0.example.com
- pattern: (\w*)orderer1.example.com(\w*)
urlsubstitutionexp: localhost:8050
ssltargetoverrideurlsubstitutionexp: orderer1.example.com
mappedhost: orderer1.example.com
- pattern: (\w*)orderer2.example.com(\w*)
urlsubstitutionexp: localhost:9050
ssltargetoverrideurlsubstitutionexp: orderer2.example.com
mappedhost: orderer2.example.com
certificateAuthorities:
- pattern: (\w*)ca.org1.example.com(\w*)
urlSubstitutionExp: http://localhost:7054
mappedHost: ca.org1.example.com
- pattern: (\w*)ca.org2.example.com(\w*)
urlSubstitutionExp: http://localhost:8054
mappedHost: ca.org2.example.com
创建org2_config.yaml
的文件:
name: "org1-config"
#
# Copyright SecureKey Technologies Inc. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#
#
# The network connection profile provides client applications the information about the target
# blockchain network that are necessary for the applications to interact with it. These are all
# knowledge that must be acquired from out-of-band sources. This file provides such a source.
#
# copied from fabric-sdk-go/test/fixtures/config/config_e2e_pkcs11.yaml
#
# Schema version of the content. Used by the SDK to apply the corresponding parsing rules.
#
version: 1.0.0
#
# The client section used by GO SDK.
#
client:
# Which organization does this application instance belong to? The value must be the name of an org
# defined under "organizations"
organization: Org2
logging:
# Develope can using debug to get more information
# level: debug
level: info
cryptoconfig:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config
# Some SDKs support pluggable KV stores, the properties under "credentialStore"
# are implementation specific
credentialStore:
# [Optional]. Used by user store. Not needed if all credentials are embedded in configuration
# and enrollments are performed elswhere.
path: "/tmp/examplestore"
# [Optional] BCCSP config for the client. Used by GO SDK.
BCCSP:
security:
enabled: true
default:
provider: "SW"
hashAlgorithm: "SHA2"
softVerify: true
level: 256
tlsCerts:
# [Optional]. Use system certificate pool when connecting to peers, orderers (for negotiating TLS) Default: false
systemCertPool: true
# [Optional]. Client key and cert for TLS handshake with peers and orderers
client:
# 使用User1@org2的证书
keyfile: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/tls/client.key
certfile: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org2.example.com/users/User1@org2.example.com/tls/client.cert
################################## General part ##################################
#
# [Optional]. But most apps would have this section so that channel objects can be constructed
# based on the content below. If an app is creating channels, then it likely will not need this
# section.
#
channels:
# name of the channel
mychannel:
# Required. list of orderers designated by the application to use for transactions on this
# channel. This list can be a result of access control ("org1" can only access "ordererA"), or
# operational decisions to share loads from applications among the orderers. The values must
# be "names" of orgs defined under "organizations/peers"
# deprecated: not recommended, to override any orderer configuration items, entity matchers should be used.
# orderers:
# - orderer.example.com
# 不要缺少当前channel的orderer节点
orderers:
- orderer0.example.com
- orderer1.example.com
- orderer2.example.com
# Required. list of peers from participating orgs
peers:
peer0.org1.example.com:
# [Optional]. will this peer be sent transaction proposals for endorsement? The peer must
# have the chaincode installed. The app can also use this property to decide which peers
# to send the chaincode install request. Default: true
endorsingPeer: true
# [Optional]. will this peer be sent query proposals? The peer must have the chaincode
# installed. The app can also use this property to decide which peers to send the
# chaincode install request. Default: true
chaincodeQuery: true
# [Optional]. will this peer be sent query proposals that do not require chaincodes, like
# queryBlock(), queryTransaction(), etc. Default: true
ledgerQuery: true
# [Optional]. will this peer be the target of the SDK's listener registration? All peers can
# produce events but the app typically only needs to connect to one to listen to events.
# Default: true
eventSource: true
# Add other peers in mychannel for byfn
peer1.org1.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
peer0.org2.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
peer1.org2.example.com:
endorsingPeer: true
chaincodeQuery: true
ledgerQuery: true
eventSource: true
# [Optional]. The application can use these options to perform channel operations like retrieving channel
# config etc.
policies:
#[Optional] options for retrieving channel configuration blocks
queryChannelConfig:
#[Optional] min number of success responses (from targets/peers)
minResponses: 1
#[Optional] channel config will be retrieved for these number of random targets
maxTargets: 1
#[Optional] retry options for query config block
retryOpts:
#[Optional] number of retry attempts
attempts: 5
#[Optional] the back off interval for the first retry attempt
initialBackoff: 500ms
#[Optional] the maximum back off interval for any retry attempt
maxBackoff: 5s
#[Optional] he factor by which the initial back off period is exponentially incremented
backoffFactor: 2.0
#
# list of participating organizations in this network
#
organizations:
Org1:
mspid: Org1MSP
# set msp files path
cryptoPath: peerOrganizations/org1.example.com/users/{username}@org1.example.com/msp
# Add peers for org1
peers:
- peer0.org1.example.com
- peer1.org1.example.com
# [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based
# network. Typically certificates provisioning is done in a separate process outside of the
# runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for
# dynamic certificate management (enroll, revoke, re-enroll). The following section is only for
# Fabric-CA servers.
certificateAuthorities:
- ca.org1.example.com
#users:
# Admin:
# cert:
# pem: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/tlsca/tlsca.org1.example.com-cert.pem
# the profile will contain public information about organizations other than the one it belongs to.
# These are necessary information to make transaction lifecycles work, including MSP IDs and
# peers with a public URL to send transaction proposals. The file will not contain private
# information reserved for members of the organization, such as admin key and certificate,
# fabric-ca registrar enroll ID and secret, etc.
Org2:
mspid: Org2MSP
cryptoPath: peerOrganizations/org2.example.com/users/{username}@org2.example.com/msp
# Add peers for org2
peers:
- peer0.org2.example.com
- peer1.org2.example.com
certificateAuthorities:
- ca.org2.example.com
# Orderer Org name
ordererorg:
# Membership Service Provider ID for this organization
mspID: OrdererMSP
cryptoPath: ordererOrganizations/example.com/users/{username}@example.com/msp
orderers:
- orderer0.example.com
- orderer1.example.com
- orderer2.example.com
#
# List of orderers to send transaction and channel create/update requests to. For the time
# being only one orderer is needed. If more than one is defined, which one get used by the
# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers.
#
orderers:
orderer0.example.com:
# [Optional] Default: Infer from hostname
url: grpcs://localhost:7050
# these are standard properties defined by the gRPC library
# they will be passed in as-is to gRPC client constructor
grpcOptions:
ssl-target-name-override: orderer0.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# Certificate location absolute path
# Replace to orderer cert path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/ordererOrganizations/example.com/orderers/orderer0.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
orderer1.example.com:
url: grpcs://localhost:8050
grpcOptions:
ssl-target-name-override: orderer1.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
allow-insecure: false
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
orderer2.example.com:
url: grpcs://localhost:9050
grpcOptions:
ssl-target-name-override: orderer2.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
allow-insecure: false
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
#
# List of peers to send various requests to, including endorsement, query
# and event listener registration.
#
peers:
peer0.org1.example.com:
# this URL is used to send endorsement and query requests
# [Optional] Default: Infer from hostname
# 表明使用grpcs协议,设置IP和端口号,使用域名会无法连接
# url: grpcs://peer0.org1.example.com:7051
url: grpcs://localhost:7051
grpcOptions:
ssl-target-name-override: peer0.org1.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
peer1.org1.example.com:
# this URL is used to send endorsement and query requests
# [Optional] Default: Infer from hostname
# 表明使用grpcs协议,设置IP和端口号,使用域名会无法连接
# url: grpcs://peer0.org1.example.com:7051
url: grpcs://localhost:8051
grpcOptions:
ssl-target-name-override: peer1.org1.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
peer0.org2.example.com:
# Replace the port
url: grpcs://localhost:9051
grpcOptions:
ssl-target-name-override: peer0.org2.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
peer1.org2.example.com:
# Replace the port
url: grpcs://localhost:10051
grpcOptions:
ssl-target-name-override: peer1.org2.example.com
keep-alive-time: 0s
keep-alive-timeout: 20s
keep-alive-permit: false
fail-fast: false
#will be taken into consideration if address has no protocol defined, if true then grpc or else grpcs
allow-insecure: false
tlsCACerts:
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
# Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows
# certificate management to be done via REST APIs. Application may choose to use a standard
# Certificate Authority instead of Fabric-CA, in which case this section would not be specified.
#
certificateAuthorities:
ca.org1.example.com:
url: http://localhost:7054
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem
# Client key and cert for SSL handshake with Fabric CA
#client:
# key:
# path: /home/alextan/blockchain/fabric/fabric-samples-1.4/raft-local-test/crypto-config/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
# cert:
# path: /home/alextan/blockchain/fabric/fabric-samples-1.4/raft-local-test/crypto-config/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
# needed to enroll and invoke new users.
registrar:
enrollId: admin
enrollSecret: adminpw
# [Optional] The optional name of the CA.
caName: ca.org1.example.com
ca.org2.example.com:
url: http://localhost:8054
tlsCACerts:
# Certificate location absolute path
path: ${FABRIC_SDK_GO_PROJECT_PATH}/fixtures/crypto-config/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem
# Client key and cert for SSL handshake with Fabric CA
#client:
# key:
# path: /home/alextan/blockchain/fabric/fabric-samples-1.4/raft-local-test/crypto-config/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.key
# cert:
# path: /home/alextan/blockchain/fabric/fabric-samples-1.4/raft-local-test/crypto-config/peerOrganizations/tls.example.com/users/User1@tls.example.com/tls/client.crt
# Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is
# needed to enroll and invoke new users.
registrar:
enrollId: admin
enrollSecret: adminpw
# [Optional] The optional name of the CA.
caName: ca.org2.example.com
entitymatchers:
peer:
- pattern: (\w*)peer0.org1.example.com(\w*)
urlsubstitutionexp: grpcs://localhost:7051
ssltargetoverrideurlsubstitutionexp: peer0.org1.example.com
mappedhost: peer0.org1.example.com
- pattern: (\w*)peer1.org1.example.com(\w*)
urlsubstitutionexp: grpcs://localhost:8051
ssltargetoverrideurlsubstitutionexp: peer1.org1.example.com
mappedhost: peer1.org1.example.com
- pattern: (\w*)peer0.org2.example.com(\w*)
urlsubstitutionexp: grpcs://localhost:9051
ssltargetoverrideurlsubstitutionexp: peer0.org2.example.com
mappedhost: peer0.org2.example.com
- pattern: (\w*)peer1.org2.example.com(\w*)
urlsubstitutionexp: grpcs://localhost:10051
ssltargetoverrideurlsubstitutionexp: peer1.org2.example.com
mappedhost: peer1.org2.example.com
orderer:
- pattern: (\w*)orderer0.example.com(\w*)
urlsubstitutionexp: localhost:7050
ssltargetoverrideurlsubstitutionexp: orderer0.example.com
mappedhost: orderer0.example.com
- pattern: (\w*)orderer1.example.com(\w*)
urlsubstitutionexp: localhost:8050
ssltargetoverrideurlsubstitutionexp: orderer1.example.com
mappedhost: orderer1.example.com
- pattern: (\w*)orderer2.example.com(\w*)
urlsubstitutionexp: localhost:9050
ssltargetoverrideurlsubstitutionexp: orderer2.example.com
mappedhost: orderer2.example.com
certificateAuthorities:
- pattern: (\w*)ca.org1.example.com(\w*)
urlSubstitutionExp: http://localhost:7054
mappedHost: ca.org1.example.com
- pattern: (\w*)ca.org2.example.com(\w*)
urlSubstitutionExp: http://localhost:8054
mappedHost: ca.org2.example.com
链码写好后,我们使用Fabric-SDK-Go提供的API来实现对链码的安装及实例化操作。
创建目录cli
:
mkdir cli
cd cli
12
用于创建channel,加入channel:
/*
* @Author: AlexTan
* @GIthub: https://github.com/AlexTan-b-z
* @Date: 2020-08-12 21:13:19
* @LastEditors: AlexTan
* @LastEditTime: 2020-08-12 22:07:26
*/
package cli
import (
"log"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp"
mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
)
const (
org1CfgPath = "../../sdkConfig/org1_config.yaml"
org2CfgPath = "../../sdkConfig/org2_config.yaml"
channelConfig = "../../fixtures/channel-artifacts/channel.tx"
ordererID = "orderer0.example.com"
ordererOrgName = "ordererorg"
org1Name = "Org1"
org2Name = "Org2"
orgAdmin = "Admin"
channelID = "mychannel"
)
func CreateChannel(){
sdk1, err := fabsdk.New(config.FromFile(org1CfgPath))
if err != nil {
log.Panicf("failed to create fabric sdk1: %s", err)
}
sdk2, err := fabsdk.New(config.FromFile(org2CfgPath))
if err != nil {
log.Panicf("failed to create fabric sdk2: %s", err)
}
clientContext := sdk1.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(ordererOrgName))
resMgmtClient, err := resmgmt.New(clientContext)
if err != nil {
log.Panicf("failed to create resMgmtClient in createChannel: %s", err)
}
mspClient, err := mspclient.New(sdk1.Context(), mspclient.WithOrg(org1Name))
if err != nil {
log.Panicf("failed to create msp client: %s", err)
}
adminIdentity, err := mspClient.GetSigningIdentity(orgAdmin)
if err != nil {
log.Panicf("failed to GetSigningIdentity: %s", err)
}
req := resmgmt.SaveChannelRequest{ChannelID: channelID,
ChannelConfigPath: channelConfig,
SigningIdentities: []msp.SigningIdentity{adminIdentity}}
_, err = resMgmtClient.SaveChannel(req, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(ordererID))
if err != nil {
log.Panicf("failed to GetSigningIdentity: %s", err)
}
log.Println("created fabric channel")
// join Channel
org1Context := sdk1.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(org1Name))
org2Context := sdk2.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(org2Name))
org1ResMgmt, err := resmgmt.New(org1Context)
if err != nil {
log.Panicf("failed to create org1ResMgmt: %s", err)
}
org2ResMgmt, err := resmgmt.New(org2Context)
if err != nil {
log.Panicf("failed to create org2ResMgmt: %s", err)
}
if err = org1ResMgmt.JoinChannel(channelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(ordererID)); err != nil {
log.Panicf("Org1 peers failed to JoinChannel: %s", err)
}
log.Println("org1 joined channel")
if err = org2ResMgmt.JoinChannel(channelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(ordererID)); err != nil {
log.Panicf("Org2 peers failed to JoinChannel: %s", err)
}
log.Println("org2 joined channel")
}
创建client.go
,用于创建客户端:
/*
* @Author: AlexTan
* @GIthub: https://github.com/AlexTan-b-z
* @Date: 2020-08-08 17:00:23
* @LastEditors: AlexTan
* @LastEditTime: 2020-08-12 21:57:18
*/
package cli
import (
"log"
"os"
"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
)
type Client struct {
// Fabric network information
ConfigPath string
OrgName string
OrgAdmin string
OrgUser string
// sdk clients
SDK *fabsdk.FabricSDK
rc *resmgmt.Client
cc *channel.Client
// for create channel
// ChannelConfig string
// OrdererID string
// Same for each peer
ChannelID string
CCID string // chaincode ID, eq name
CCPath string // chaincode source path, 是GOPATH下的某个目录
CCGoPath string // GOPATH used for chaincode
}
func New(cfg, org, admin, user string) *Client {
c := &Client{
ConfigPath: cfg,
OrgName: org,
OrgAdmin: admin,
OrgUser: user,
// ChannelConfig: "../fixtures/channel-artifacts/channel.tx",
// OrdererID: "orderer0.example.com",
CCID: "mycc",
CCPath: "chaincode/", // 相对路径是从GOPAHT/src开始的
CCGoPath: os.Getenv("GOPATH"),
ChannelID: "mychannel",
}
// create sdk
sdk, err := fabsdk.New(config.FromFile(c.ConfigPath))
if err != nil {
log.Panicf("failed to create fabric sdk: %s", err)
}
c.SDK = sdk
log.Println("Initialized fabric sdk")
c.rc, c.cc = NewSdkClient(sdk, c.ChannelID, c.OrgName, c.OrgAdmin, c.OrgUser)
return c
}
// create channel
// func CreateChannel(sdk *fabsdk.FabricSDK, orgName, orgAdmin, channelID, ChannelConfig)
// NewSdkClient create resource client and channel client
func NewSdkClient(sdk *fabsdk.FabricSDK, channelID, orgName, orgAdmin, OrgUser string) (rc *resmgmt.Client, cc *channel.Client) {
var err error
// create rc
rcp := sdk.Context(fabsdk.WithUser(orgAdmin), fabsdk.WithOrg(orgName))
rc, err = resmgmt.New(rcp)
if err != nil {
log.Panicf("failed to create resource client: %s", err)
}
log.Println("Initialized resource client")
// create cc
ccp := sdk.ChannelContext(channelID, fabsdk.WithUser(OrgUser))
cc, err = channel.New(ccp)
if err != nil {
log.Panicf("failed to create channel client: %s", err)
}
log.Println("Initialized channel client")
return rc, cc
}
// RegisterChaincodeEvent more easy than event client to registering chaincode event.
func (c *Client) RegisterChaincodeEvent(ccid, eventName string) (fab.Registration, <-chan *fab.CCEvent, error) {
return c.cc.RegisterChaincodeEvent(ccid, eventName)
}
用于链码安装,实例化链码操作:
回到项目根目录
cd ..
mkdir samples
cd samples
mkdir chaincode
1234
在samples/chaincode
目录下创建文件main.go
,用于运行链码相关操作:
/*
* @Author: test1Tan
* @GIthub: https://github.com/test1Tan-b-z
* @Date: 2020-08-11 21:03:54
* @LastEditors: AlexTan
* @LastEditTime: 2020-08-19 20:20:44
*/
package main
import (
"log"
"time"
"github.com/AlexTan-b-z/fabric-sdk-go-demo/cli"
"github.com/AlexTan-b-z/fabric-sdk-go-demo/model"
)
const (
org1CfgPath = "../../sdkConfig/org1_config.yaml"
org2CfgPath = "../../sdkConfig/org2_config.yaml"
)
var (
peer0Org1 = "peer0.org1.example.com"
peer0Org2 = "peer0.org2.example.com"
)
func main() {
// init
// 第一次运行后记得注释掉
cli.CreateChannel()
org1Client := cli.New(org1CfgPath, "Org1", "Admin", "User1")
org2Client := cli.New(org2CfgPath, "Org2", "Admin", "User1")
defer org1Client.Close()
defer org2Client.Close()
// Install, instantiate, invoke, query
Phase1(org1Client, org2Client)
// Install, upgrade, invoke, query
Phase2(org1Client, org2Client)
}
func Phase1(cli1, cli2 *cli.Client) {
log.Println("=================== Phase 1 begin ===================")
defer log.Println("=================== Phase 1 end ===================")
if err := cli1.InstallCC("v1", peer0Org1); err != nil {
log.Panicf("Intall chaincode error: %v", err)
}
log.Println("Chaincode has been installed on org1's peers")
if err := cli2.InstallCC("v1", peer0Org2); err != nil {
log.Panicf("Intall chaincode error: %v", err)
}
log.Println("Chaincode has been installed on org2's peers")
// InstantiateCC chaincode only need once for each channel
if _, err := cli1.InstantiateCC("v1", peer0Org1); err != nil {
log.Panicf("Instantiated chaincode error: %v", err)
}
log.Println("Chaincode has been instantiated")
sco1 := model.Score{
Name: "test1",
Gender: "男",
StuID: "123",
Grade: "2015",
Result: "100",
Time: time.Now().Format("2006-01-02 15:04:05"),
}
sco2 := model.Score{
Name: "test2",
Gender: "女",
StuID: "1234",
Grade: "2017",
Result: "100",
Time: time.Now().Format("2006-01-02 15:04:05"),
}
if _, err := cli1.InvokeCCadd([]string{peer0Org1}, sco1); err != nil {
log.Panicf("InvokeCCadd test1 chaincode error: %v", err)
}
log.Println("InvokeCCadd test1 chaincode success 1")
if _, err := cli1.InvokeCCadd([]string{peer0Org1}, sco2); err != nil {
log.Panicf("InvokeCCadd test2 chaincode error: %v", err)
}
log.Println("InvokeCCadd test2 chaincode success 2")
if err := cli1.QueryCCByNameAndGrade("peer0.org1.example.com", "test1", "2015"); err != nil {
log.Panicf("QueryCCByNameAndGrade chaincode error: %v", err)
}
log.Println("QueryCCByNameAndGrade chaincode success on peer0.org1")
if err := cli1.QueryCCByID("peer0.org1.example.com", "1234"); err != nil {
log.Panicf("QueryCCByID chaincode error: %v", err)
}
log.Println("QueryCCByID chaincode success on peer0.org1")
new_sco := model.Score{
Name: "test1",
Gender: "男",
StuID: "123",
Grade: "2015",
Result: "99",
Time: time.Now().Format("2006-01-02 15:04:05"),
}
if _, err := cli1.UpdateCCScore([]string{peer0Org1}, new_sco); err != nil {
log.Panicf("updateCCScore chaincode error: %v", err)
}
log.Println("updateCCScore chaincode success 1")
if err := cli1.QueryCCByID("peer0.org1.example.com", "123"); err != nil {
log.Panicf("QueryCCByID chaincode error: %v", err)
}
log.Println("QueryCCByID chaincode success on peer0.org1")
if _, err := cli1.InvokeCCDelete([]string{"peer0.org1.example.com"}, "123"); err != nil {
log.Panicf("InvokeCCDelete chaincode error: %v", err)
}
log.Println("InvokeCCDelete chaincode success on peer0.org1")
}
func Phase2(cli1, cli2 *cli.Client) {
log.Println("=================== Phase 2 begin ===================")
defer log.Println("=================== Phase 2 end ===================")
v := "v2"
// Install new version chaincode
if err := cli1.InstallCC(v, peer0Org1); err != nil {
log.Panicf("Intall chaincode error: %v", err)
}
log.Println("Chaincode has been installed on org1's peers")
if err := cli2.InstallCC(v, peer0Org2); err != nil {
log.Panicf("Intall chaincode error: %v", err)
}
log.Println("Chaincode has been installed on org2's peers")
// Upgrade chaincode only need once for each channel
if err := cli1.UpgradeCC(v, peer0Org1); err != nil {
log.Panicf("Upgrade chaincode error: %v", err)
}
log.Println("Upgrade chaincode success for channel")
sco1 := model.Score{
Name: "test3",
Gender: "男",
StuID: "12345",
Grade: "2015",
Result: "100",
Time: time.Now().Format("2006-01-02 15:04:05"),
}
sco2 := model.Score{
Name: "test4",
Gender: "女",
StuID: "123456",
Grade: "2017",
Result: "100",
Time: time.Now().Format("2006-01-02 15:04:05"),
}
if _, err := cli1.InvokeCCadd([]string{"peer0.org1.example.com", "peer0.org2.example.com"}, sco1); err != nil {
log.Panicf("InvokeCCadd test3 chaincode error: %v", err)
}
log.Println("InvokeCCadd test3 chaincode success 1")
if _, err := cli1.InvokeCCadd([]string{peer0Org1, "peer0.org2.example.com"}, sco2); err != nil {
log.Panicf("InvokeCCadd test4 chaincode error: %v", err)
}
log.Println("InvokeCCadd test4 chaincode success 2")
if err := cli1.QueryCCByNameAndGrade("peer0.org2.example.com", "test3", "2015"); err != nil {
log.Panicf("QueryCCByNameAndGrade chaincode error: %v", err)
}
log.Println("QueryCCByNameAndGrade chaincode success on peer0.org2")
if err := cli1.QueryCCByID("peer0.org2.example.com", "12345"); err != nil {
log.Panicf("QueryCCByID chaincode error: %v", err)
}
log.Println("QueryCCByID chaincode success on peer0.org2")
new_sco := model.Score{
Name: "test3",
Gender: "男",
StuID: "12345",
Grade: "2015",
Result: "99",
Time: time.Now().Format("2006-01-02 15:04:05"),
}
if _, err := cli1.UpdateCCScore([]string{peer0Org1, "peer0.org2.example.com"}, new_sco); err != nil {
log.Panicf("updateCCScore chaincode error: %v", err)
}
log.Println("updateCCScore chaincode success 1")
if err := cli1.QueryCCByID("peer0.org2.example.com", "12345"); err != nil {
log.Panicf("QueryCCByID chaincode error: %v", err)
}
log.Println("QueryCCByID chaincode success on peer0.org2")
if _, err := cli1.InvokeCCDelete([]string{"peer0.org2.example.com", peer0Org1}, "12345"); err != nil {
log.Panicf("InvokeCCDelete chaincode error: %v", err)
}
log.Println("InvokeCCDelete chaincode success on peer0.org2")
}
回到前一个目录,创建event
文件夹
cd ..
mkdir event
cd event
123
创建main.go
,用于测试监听链码事件:
package main
import (
"encoding/hex"
"log"
"time"
"github.com/hyperledger/fabric-sdk-go/pkg/fab/events/deliverclient/seek"
"github.com/hyperledger/fabric-sdk-go/pkg/client/event"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
"github.com/AlexTan-b-z/fabric-sdk-go-demo/cli"
"github.com/AlexTan-b-z/fabric-sdk-go-demo/model"
)
const (
org1CfgPath = "../../sdkConfig/org1_config.yaml"
org2CfgPath = "../../sdkConfig/org2_config.yaml"
)
var (
peer0Org1 = "peer0.org1.example.com"
peer0Org2 = "peer0.org2.example.com"
)
func main() {
org1Client := cli.New(org1CfgPath, "Org1", "Admin", "User1")
org2Client := cli.New(org2CfgPath, "Org2", "Admin", "User1")
defer org1Client.Close()
defer org2Client.Close()
// New event client
cp := org1Client.SDK.ChannelContext(org1Client.ChannelID, fabsdk.WithUser(org1Client.OrgUser))
ec, err := event.New(
cp,
event.WithBlockEvents(), // 如果没有,会是filtered
// event.WithBlockNum(1), // 从指定区块获取,需要此参数
event.WithSeekType(seek.Newest))
if err != nil {
log.Printf("Create event client error: %v", err)
}
// block event listen
defer ec.Unregister(blockListener(ec))
defer ec.Unregister(filteredBlockListener(ec))
// tx listen
txIDCh := make(chan string, 100)
go txListener(ec, txIDCh)
// chaincode event listen
defer ec.Unregister(chainCodeEventListener(nil, ec))
DoChainCode(org1Client, txIDCh)
close(txIDCh)
time.Sleep(time.Second * 10)
}
func blockListener(ec *event.Client) fab.Registration {
// Register monitor block event
beReg, beCh, err := ec.RegisterBlockEvent()
if err != nil {
log.Printf("Register block event error: %v", err)
}
log.Println("Registered block event")
// Receive block event
go func() {
for e := range beCh {
log.Printf("Receive block event:\nSourceURL: %v\nNumber: %v\nHash"+
": %v\nPreviousHash: %v\n\n",
e.SourceURL,
e.Block.Header.Number,
hex.EncodeToString(e.Block.Header.DataHash),
hex.EncodeToString(e.Block.Header.PreviousHash))
}
}()
return beReg
}
func filteredBlockListener(ec *event.Client) fab.Registration {
// Register monitor filtered block event
fbeReg, fbeCh, err := ec.RegisterFilteredBlockEvent()
if err != nil {
log.Printf("Register filtered block event error: %v", err)
}
log.Println("Registered filtered block event")
// Receive filtered block event
go func() {
for e := range fbeCh {
log.Printf("Receive filterd block event:\nNumber: %v\nlen("+
"transactions): %v\nSourceURL: %v",
e.FilteredBlock.Number, len(e.FilteredBlock.
FilteredTransactions), e.SourceURL)
for i, tx := range e.FilteredBlock.FilteredTransactions {
log.Printf("tx index %d: type: %v, txid: %v, "+
"validation code: %v", i,
tx.Type, tx.Txid,
tx.TxValidationCode)
}
log.Println() // Just go print empty log for easy to read
}
}()
return fbeReg
}
func txListener(ec *event.Client, txIDCh chan string) {
log.Println("Transaction listener start")
defer log.Println("Transaction listener exit")
for id := range txIDCh {
// Register monitor transaction event
log.Printf("Register transaction event for: %v", id)
txReg, txCh, err := ec.RegisterTxStatusEvent(id)
if err != nil {
log.Printf("Register transaction event error: %v", err)
continue
}
defer ec.Unregister(txReg)
// Receive transaction event
go func() {
for e := range txCh {
log.Printf("Receive transaction event: txid: %v, "+
"validation code: %v, block number: %v",
e.TxID,
e.TxValidationCode,
e.BlockNumber)
}
}()
}
}
func chainCodeEventListener(c *cli.Client, ec *event.Client) fab.Registration {
eventName := ".*"
log.Printf("Listen chaincode event: %v", eventName)
var (
ccReg fab.Registration
eventCh <-chan *fab.CCEvent
err error
)
if c != nil {
log.Println("Using client to register chaincode event")
ccReg, eventCh, err = c.RegisterChaincodeEvent("mycc", eventName)
} else {
log.Println("Using event client to register chaincode event")
ccReg, eventCh, err = ec.RegisterChaincodeEvent("mycc", eventName)
}
if err != nil {
log.Printf("Register chaincode event error: %v", err.Error())
return nil
}
// consume event
go func() {
for e := range eventCh {
log.Printf("Receive cc event, ccid: %v \neventName: %v\n"+
"payload: %v \ntxid: %v \nblock: %v \nsourceURL: %v\n",
e.ChaincodeID, e.EventName, string(e.Payload), e.TxID, e.BlockNumber, e.SourceURL)
}
}()
return ccReg
}
// Install、Deploy、Invoke、Query、Upgrade
func DoChainCode(cli1 *cli.Client, txCh chan<- string) {
var (
txid fab.TransactionID
err error
)
// ccVersion := "v1"
// if err := cli1.InstallCC(ccVersion, peer0Org1); err != nil {
// log.Panicf("Intall chaincode error: %v", err)
// }
// log.Println("Chaincode has been installed on org1's peers")
//
// // InstantiateCC chaincode only need once for each channel
// if txid, err = cli1.InstantiateCC(ccVersion, peer0Org1); err != nil {
// log.Panicf("Instantiated chaincode error: %v", err)
// }
// if txid != "" {
// txCh <- string(txid)
// }
// log.Println("Chaincode has been instantiated")
sco3 := model.Score{
Name: "eventTest",
Gender: "男",
StuID: "888888",
Grade: "2018",
Result: "100",
Time: time.Now().Format("2006-01-02 15:04:05"),
}
if txid, err = cli1.InvokeCCadd([]string{peer0Org1, "peer0.org2.example.com"}, sco3); err != nil {
log.Panicf("InvokeCCadd chaincode error: %v", err)
}
log.Println("InvokeCCadd chaincode success 1")
if txid != "" {
txCh <- string(txid)
}
log.Println("Invoke chaincode success")
if err = cli1.QueryCCByID("peer0.org1.example.com", "888888"); err != nil {
log.Panicf("Query chaincode error: %v", err)
}
log.Println("Query chaincode success on peer0.org1")
if txid, err = cli1.InvokeCCDelete([]string{"peer0.org1.example.com", "peer0.org2.example.com"}, "888888"); err != nil {
log.Panicf("InvokeCCDelete chaincode error: %v", err)
}
}
确保已经启动好容器环境后,分别运行samples/chaincode/main.go和samples/event/main.go即可完成测试。
其中包括以下操作:
要清理运行环境,使用docker-compose将容器停掉并移除即可:
docker-compose -f docker-compose-local.yaml down -v