搭建流程
環(huán)境搭建
一、自行下載虛擬機(jī)軟件(這里使用VirtualBox)
二、下載iso光盤CentOS Linux release 7.8.2003 (Core)
http://mirrors.aliyun.com/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-2003.iso
三、創(chuàng)建虛擬機(jī),橋接網(wǎng)絡(luò),內(nèi)存4G,硬盤80G,掛載光盤,啟動(dòng)虛擬機(jī)。
四、配置linux
# 關(guān)閉防火墻
systemctl stop firewalld && systemctl disable firewalld
#CentOS關(guān)閉selinux
setenforce 0
sed -i "s/^SELINUX=.*/SELINUX=disabled/g" /etc/sysconfig/selinux
sed -i "s/^SELINUX=.*/SELINUX=disabled/g" /etc/selinux/config
sed -i "s/^SELINUX=.*/SELINUX=disabled/g" /etc/sysconfig/selinux
sed -i "s/^SELINUX=.*/SELINUX=disabled/g" /etc/selinux/config
# 修改時(shí)區(qū)
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 修改系統(tǒng)語言環(huán)境
echo 'LANG="en_US.UTF-8"' >> /etc/profile;source /etc/profile
# 設(shè)置dns
echo "nameserver 8.8.8.8" > /etc/resolv.conf
# 設(shè)置yum源
sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak
echo '
[base]
name=CentOS-$releasever - Base - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
#released updates
[updates]
name=CentOS-$releasever - Updates - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
#contrib - packages by Centos Users
[contrib]
name=CentOS-$releasever - Contrib - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
' > /etc/yum.repos.d/CentOS-Base.repo
# 更新內(nèi)核與安裝包
sudo yum update -y
# 重新緩存
yum clean all && yum makecache
五、安裝docker-ce
# 安裝docker-ce依賴包
sudo yum install -y yum-utils device-mapper-persistent-data lvm2 bash-completion
# 添加docker-ce yum源地址
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 安裝docker-ce
yum -y install docker-ce
# 修改docker配置文件,添加docker鏡像加速倉庫,存儲(chǔ)格式等
mkdir -p /etc/docker
echo '
{
"max-concurrent-downloads": 3,
"max-concurrent-uploads": 5,
"registry-mirrors": ["https://7bezldxe.mirror.aliyuncs.com/"],
"storage-driver": "overlay2",
"storage-opts": ["overlay2.override_kernel_check=true"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
' > /etc/docker/daemon.json
# 啟動(dòng)docker
systemctl start docker
# 檢查狀態(tài)及版本
systemctl status docker
docker version
# 設(shè)置開機(jī)啟動(dòng)
sudo systemctl enable docker
六、安裝git
yum -y install git
七、安裝docker-compose
# 安裝epel源
yum -y install epel-release
如何不可用vim 一下/etc/yum.repos.d/epel.repo改為如下
[epel]
name=Extra Packages for Enterprise Linux 7 - $basearch
baseurl=http://download.fedoraproject.org/pub/epel/7/$basearch
#metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-7&arch=$basearch
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
[epel-debuginfo]
name=Extra Packages for Enterprise Linux 7 - $basearch - Debug
#baseurl=http://download.fedoraproject.org/pub/epel/7/$basearch/debug
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-7&arch=$basearch
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck=1
[epel-source]
name=Extra Packages for Enterprise Linux 7 - $basearch - Source
#baseurl=http://download.fedoraproject.org/pub/epel/7/SRPMS
metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-source-7&arch=$basearch
failovermethod=priority
enabled=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
gpgcheck=1
#安裝python-pip包
yum -y install python-pip
# 對安裝好的pip進(jìn)行升級(jí)
pip install --upgrade pip
# 查看
pip -V
# 安裝docker-compose
pip --default-timeout=200 install -U docker-compose
# 查看
docker-compose -version
八、git下載fabric源碼,并切換master分支
mkdir -p /opt/gopath/src/github.com/hyperledger
cd /opt/gopath/src/github.com/hyperledger
git clone https://github.com/hyperledger/fabric.git
cd /opt/gopath/src/github.com/hyperledger/fabric
git branch
如git clone出現(xiàn)錯(cuò)誤:Peer reports incompatible or unsupported protocol version.
請更新yum update -y nss curl libcurl
九、根據(jù)源碼腳本拉取鏡像、二進(jìn)制文件等
cd /opt/gopath/src/github.com/hyperledger/fabric/scripts
./bootstrap.sh
# 查看
docker images
------------------------------------------------------------------------------------------------
REPOSITORY TAG IMAGE ID CREATED SIZE
hyperledger/fabric-ca 1.4 dbbc768aec79 5 weeks ago 158MB
hyperledger/fabric-ca 1.4.9 dbbc768aec79 5 weeks ago 158MB
hyperledger/fabric-ca latest dbbc768aec79 5 weeks ago 158MB
hyperledger/fabric-tools 2.2 e9b802fadb41 5 weeks ago 519MB
hyperledger/fabric-tools 2.2.1 e9b802fadb41 5 weeks ago 519MB
hyperledger/fabric-tools latest e9b802fadb41 5 weeks ago 519MB
hyperledger/fabric-peer 2.2 ece149884124 5 weeks ago 55MB
hyperledger/fabric-peer 2.2.1 ece149884124 5 weeks ago 55MB
hyperledger/fabric-peer latest ece149884124 5 weeks ago 55MB
hyperledger/fabric-orderer 2.2 78a16ddd2cf4 5 weeks ago 38.4MB
hyperledger/fabric-orderer 2.2.1 78a16ddd2cf4 5 weeks ago 38.4MB
hyperledger/fabric-orderer latest 78a16ddd2cf4 5 weeks ago 38.4MB
hyperledger/fabric-ccenv 2.2 8e554c280cac 5 weeks ago 586MB
hyperledger/fabric-ccenv 2.2.1 8e554c280cac 5 weeks ago 586MB
hyperledger/fabric-ccenv latest 8e554c280cac 5 weeks ago 586MB
hyperledger/fabric-baseos 2.2 0b99d26b26ad 5 weeks ago 6.85MB
hyperledger/fabric-baseos 2.2.1 0b99d26b26ad 5 weeks ago 6.85MB
hyperledger/fabric-baseos latest 0b99d26b26ad 5 weeks ago 6.85MB
------------------------------------------------------------------------------------------------
十、安裝Golang
yum -y install golang
# 自定義環(huán)境Go環(huán)境變量到linux文件
echo '
export PATH=$PATH:/usr/local/go/bin
export GOPATH=/opt/gopath
# 加載
source /etc/profile
' >> /etc/profile
十一、啟動(dòng)區(qū)塊鏈網(wǎng)絡(luò)
cd fabric-samples/test-network
./network.sh up
# 查看 (可以看到兩個(gè)組織的peer節(jié)點(diǎn)與1個(gè)order節(jié)點(diǎn))
docker ps

成功夠可以看到啟動(dòng)了3個(gè)容器:2個(gè)peer、1個(gè)order節(jié)點(diǎn)。
十二、創(chuàng)建channel
./network.sh createChannel -c ch1
十三、在channel上創(chuàng)建chaincode,指定七牛云go依賴包倉庫進(jìn)行下載,這里chaincode默認(rèn)加載的智能合約程序?yàn)?opt/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples下的basic資產(chǎn)程序
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
./network.sh deployCC -c ch1
十四、交互
cd /opt/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/test-network
export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=$PWD/../config/
# 認(rèn)證Org1
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
# 對賬本初始化資產(chǎn)
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C ch1 -n basic --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"InitLedger","Args":[]}'
# 查看我們添加的資產(chǎn)
peer chaincode query -C ch1 -n basic -c '{"Args":["GetAllAssets"]}'
# 顯示的資產(chǎn)列表 [{"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}]
# 轉(zhuǎn)移資產(chǎn)
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C ch1 -n basic --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"TransferAsset","Args":["asset6","Christopher"]}'
# 使用Org2認(rèn)證,進(jìn)行驗(yàn)證
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp
export CORE_PEER_ADDRESS=localhost:9051
# 查看剛才轉(zhuǎn)移的第六個(gè)資產(chǎn)
peer chaincode query -C ch1 -n basic -c '{"Args":["ReadAsset","asset6"]}'
# 關(guān)閉區(qū)塊鏈網(wǎng)絡(luò)
./network.sh down
十五、添加新節(jié)點(diǎn)
cd addOrg3
./addOrg3.sh generate
./addOrg3.sh up -c ch1
# 使用org3認(rèn)證
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org3MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp
export CORE_PEER_ADDRESS=localhost:11051
# 安裝chaincode到org3Chaincode is installed on peer0.org3
peer lifecycle chaincode install basic.tar.gz
# 查詢安裝是否成功
peer lifecycle chaincode queryinstalled
# 讓org3上的鏈碼定義在ch1通道上認(rèn)可 Chaincode definition approved on peer0.org3 on channel 'ch1'
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/test-network/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem --channelID ch1 --name basic --version 1.0 --package-id basic_1.0:4ec191e793b27e953ff2ede5a8bcc63152cecb1e4c3f301a26e22692c61967ad --sequence 1
十六、編寫智能合約
# 創(chuàng)建智能合約程序目錄并進(jìn)入
mkdir atcc && cd atcc
# goland開啟 go module模式并配置goproxy

# 新建go文件
touch atcc.go
# 編寫代碼
package main
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
"log"
)
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)
}
}
// SmartContract provides functions for managing an Asset
type SmartContract struct {
contractapi.Contract
}
// Asset describes basic details of what makes up a simple asset
type Student struct {
ID string `json:"ID"`
Name string `json:"name"`
Age int `json:"age"`
}
func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
stus := []Student{
{ID: "a1", Name: "小明", Age: 16},
{ID: "a2", Name: "小花", Age: 15},
{ID: "a3", Name: "小強(qiáng)", Age: 17},
{ID: "a4", Name: "小剛", Age: 16},
{ID: "a5", Name: "小草", Age: 14},
{ID: "a6", Name: "小新", Age: 14},
}
for _, stu := range stus {
assetJSON, err := json.Marshal(stu)
if err != nil {
return err
}
err = ctx.GetStub().PutState(stu.ID, assetJSON)
if err != nil {
return fmt.Errorf("failed to put to world state. %v", err)
}
}
return nil
}
// CreateAsset issues a new asset to the world state with given details.
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, name string, age int) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the asset %s already exists", id)
}
stu := Student{
ID: id,
Name: name,
Age: age,
}
assetJSON, err := json.Marshal(stu)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
// AssetExists returns true when asset with given ID exists in world state
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
}
// ReadAsset returns the asset stored in the world state with given id.
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Student, 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 stu Student
err = json.Unmarshal(assetJSON, &stu)
if err != nil {
return nil, err
}
return &stu, nil
}
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, name string, age 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)
}
// overwriting original asset with new asset
stu := Student{
ID: id,
Name: name,
Age: age,
}
assetJSON, err := json.Marshal(stu)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
// DeleteAsset deletes an given asset from the world state.
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)
}
上面程序主要調(diào)用"github.com/hyperledger/fabric-contract-api-go/contractapi"接口的增刪改查方法來操作自己的資產(chǎn)。
十七、部署智能合約
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
# 整理go依賴包
go mod tidy
# 使用vendor管理,將依賴包下載到module目錄
go mod vendor
登錄fabric network機(jī)器上,將寫好的合約程序上傳到/opt/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/personal-demo/位置,mkdir -p /opt/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/personal-demo
# 初始集群
cd /opt/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/test-network
./network down
./network up
# 創(chuàng)建通道ch3
./network createChannel -c ch3
# 部署智能合約
./network.sh deployCC -ccp ../personal-demo/atcc -c ch3
# 調(diào)用智能合約api
cd /opt/gopath/src/github.com/hyperledger/fabric/scripts/fabric-samples/test-network
export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=$PWD/../config/
# 認(rèn)證Org1
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051
# 使用智能合約初始化方法
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C ch3 -n basic --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"function":"InitLedger","Args":[]}'
# 查看我們添加的資產(chǎn)
peer chaincode query -C ch3 -n atcc -c '{"function":"ReadAsset","Args":["a1"]}'
#顯示結(jié)果{"ID":"a1","name":"小明","age":16}
十八、編寫運(yùn)行App調(diào)用智能合約
這里繼續(xù)使用golang編寫,調(diào)用智能合約相關(guān)sdk包
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
"github.com/hyperledger/fabric-sdk-go/pkg/gateway"
大體流程:
1.系統(tǒng)環(huán)境變量設(shè)置是否為本地app運(yùn)行:true
2.創(chuàng)建錢包,錢包中為身份認(rèn)證等信息,這里使用User1@org1.example.com的身份
3.通過gateway連接智能合約,傳入wallet認(rèn)證信息、通道名稱、合約名稱
4.獲取合約對象后,調(diào)用合約方法
5.啟動(dòng)app
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
"github.com/hyperledger/fabric-sdk-go/pkg/gateway"
)
func main() {
log.Println("============ application-golang starts ============")
log.Println("系統(tǒng)環(huán)境變量設(shè)置是否為本地app運(yùn)行:true ")
err := os.Setenv("DISCOVERY_AS_LOCALHOST", "true")
if err != nil {
log.Fatalf("Error setting DISCOVERY_AS_LOCALHOST environemnt variable: %v", err)
}
log.Println("創(chuàng)建錢包,錢包中為身份認(rèn)證等信息,這里使用User1@org1.example.com的身份.")
wallet, err := gateway.NewFileSystemWallet("wallet")
if err != nil {
log.Fatalf("Failed to create wallet: %v", err)
}
if !wallet.Exists("appUser") {
err = populateWallet(wallet)
if err != nil {
log.Fatalf("Failed to populate wallet contents: %v", err)
}
}
log.Println("通過gateway連接智能合約,傳入通道名稱、合約名稱.")
ccpPath := filepath.Join(
"..",
"..",
"test-network",
"organizations",
"peerOrganizations",
"org1.example.com",
"connection-org1.yaml",
)
gw, err := gateway.Connect(
gateway.WithConfig(config.FromFile(filepath.Clean(ccpPath))),
gateway.WithIdentity(wallet, "appUser"),
)
if err != nil {
log.Fatalf("Failed to connect to gateway: %v", err)
}
defer gw.Close()
network, err := gw.GetNetwork("ch3")
if err != nil {
log.Fatalf("Failed to get network: %v", err)
}
contract := network.GetContract("basic")
log.Println("--> Submit Transaction: 調(diào)用智能合約InitLedger方法初始化所有同學(xué)的數(shù)據(jù).")
result, err := contract.SubmitTransaction("InitLedger")
if err != nil {
log.Fatalf("Failed to Submit transaction: %v", err)
}
log.Println(string(result))
log.Println("--> Submit Transaction: 調(diào)用智能合約CreateAsset方法創(chuàng)建一個(gè)同學(xué)的信息, id=a8,name=小朵,age=13.")
result, err = contract.SubmitTransaction("CreateAsset", "a8", "小朵", "13")
if err != nil {
log.Fatalf("Failed to Submit transaction: %v", err)
}
log.Println(string(result))
log.Println("--> Evaluate Transaction: 調(diào)用智能合約ReadAsset方法讀取剛剛創(chuàng)建的同學(xué)的信息.")
result, err = contract.EvaluateTransaction("ReadAsset", "a8")
if err != nil {
log.Fatalf("Failed to evaluate transaction: %v\n", err)
}
log.Println(string(result))
log.Println("--> Evaluate Transaction: 調(diào)用智能合約AssetExists方法判斷剛剛創(chuàng)建的同學(xué)信息是否存在.")
result, err = contract.EvaluateTransaction("AssetExists", "a8")
if err != nil {
log.Fatalf("Failed to evaluate transaction: %v\n", err)
}
log.Println(string(result))
log.Println("============ application-golang ends ============")
}
func populateWallet(wallet *gateway.Wallet) error {
log.Println("============ Populating wallet ============")
credPath := filepath.Join(
"..",
"..",
"test-network",
"organizations",
"peerOrganizations",
"org1.example.com",
"users",
"User1@org1.example.com",
"msp",
)
certPath := filepath.Join(credPath, "signcerts", "User1@org1.example.com-cert.pem")
// read the certificate pem
cert, err := ioutil.ReadFile(filepath.Clean(certPath))
if err != nil {
return err
}
keyDir := filepath.Join(credPath, "keystore")
// there's a single file in this dir containing the private key
files, err := ioutil.ReadDir(keyDir)
if err != nil {
return err
}
if len(files) != 1 {
return fmt.Errorf("keystore folder should have contain one file")
}
keyPath := filepath.Join(keyDir, files[0].Name())
key, err := ioutil.ReadFile(filepath.Clean(keyPath))
if err != nil {
return err
}
identity := gateway.NewX509Identity("Org1MSP", string(cert), string(key))
return wallet.Put("appUser", identity)
}
go run xxx.go 運(yùn)行該app
十九、test-network目錄說明
├── addOrg3 添加新組織相關(guān)文件及腳本文件夾
│ ├── addOrg3.sh
│ ├── ccp-generate.sh
│ ├── ccp-template.json
│ ├── ccp-template.yaml
│ ├── configtx.yaml
│ ├── docker
│ ├── fabric-ca
│ ├── org3-crypto.yaml
│ └── README.md
├── channel-artifacts 通道文件
│ ├── ch5.block
│ ├── ch5.tx
│ ├── Org1MSPanchors.tx
│ └── Org2MSPanchors.tx
├── configtx
│ └── configtx.yaml 用于生成系統(tǒng)創(chuàng)世區(qū)塊的配置文件
├── docker 用于啟動(dòng)docker容器的docker-compose配置文件
│ ├── docker-compose-ca.yaml
│ ├── docker-compose-couch.yaml
│ └── docker-compose-test-net.yaml
├── log.txt
├── network.sh 入口腳本
├── organizations
│ ├── ccp-generate.sh
│ ├── ccp-template.json
│ ├── ccp-template.yaml
│ ├── cryptogen 使用cryptogen tool為組織生成證書、秘鑰
│ ├── fabric-ca 使用ca服務(wù)器來為每個(gè)身份 生成證書、秘鑰時(shí),其中包含相關(guān)的配置文件
│ │ ├── ordererOrg
│ │ ├── org1
│ │ ├── org2
│ │ └── registerEnroll.sh 生成證書、秘鑰的腳本
│ ├── ordererOrganizations order組織的證書文件
│ └── peerOrganizations peer組織的證書文件
├── README.md
├── scripts 子腳本文件夾
│ ├── createChannel.sh createChannel.sh基于configtx/configtx.yaml使用指定的通道名稱創(chuàng)建通道,向通道中加入peer0.org1.example.com和peer0.org2.example.com,并將它們更新為錨節(jié)點(diǎn)
│ ├── deployCC.sh deployCC.sh在peer0.org1.example.com和peer0.org2.example.com上安裝fabric的鏈碼,并在通道中定義chaincode,最后調(diào)用chaincode顯示結(jié)果
│ ├── envVar.sh 系統(tǒng)環(huán)境變量
│ └── org3-scripts 添加新組織相關(guān)腳本
├── scriptUtils.sh 通用腳本-help等
└── system-genesis-block 系統(tǒng)創(chuàng)世區(qū)塊文件夾
└── genesis.block 生成的系統(tǒng)創(chuàng)世區(qū)塊
附錄
安裝go環(huán)境
1.下載go sdk
非科學(xué)上網(wǎng)訪問:https://studygolang.com/dl

安裝后請確認(rèn)環(huán)境變量可用
go versiongo env2.下載Goland
訪問:
https://www.jetbrains.com/zh-cn/go/
cryptogen介紹
1.MSP是什么
MSP一個(gè)提供虛擬成員操作的管理框架組件
每個(gè)節(jié)點(diǎn)都有一個(gè)MSP賬號(hào)
每個(gè)用戶也都有MSP賬號(hào)
MSP下面有一個(gè)管理員賬號(hào)和證書
驗(yàn)證身份的證書都在MSP下面。
2.cryptogen使用方法
cryptogen --help //查看使用方法
cryptogen generate --help //查看generate后的使用方法
//使用cryptogen generate需要指定模板文件,不然就是默認(rèn)模板
cryptogen generate --config == 某文件
會(huì)生成peer節(jié)點(diǎn)和orderer節(jié)點(diǎn)文件
根據(jù)yaml的模塊修改 cryptogen showtemplate //查看模板
使用重定向命令將模板放在別的地方通過修改成自己的
cryptogen showtemplate > a.yaml //一般起名叫crypto-config.yaml
3.模板說明
OrdererOrgs: //排序節(jié)點(diǎn)的組織信息,這里不能改
# ---------------------------------------------------------------------------
# Orderer
# ---------------------------------------------------------------------------
- Name: Orderer //排序節(jié)點(diǎn)組織的名字,可以修改可以不修改
Domain: example.com //根域名,排序節(jié)點(diǎn)組織的根域名,測試的時(shí)候隨便取
# ---------------------------------------------------------------------------
# "Specs" - See PeerOrgs below for complete description
# ---------------------------------------------------------------------------
Specs: //User去指定
- Hostname: orderer //生成一個(gè)子域名,去訪問當(dāng)前唯一排序節(jié)點(diǎn)。orderer對應(yīng)的訪問域名應(yīng)該是:orderer.example.com
Hostname有幾個(gè)就有幾個(gè)排序節(jié)點(diǎn),通過隊(duì)友的域名
排序節(jié)點(diǎn)名字.根域名去訪問
第二部分:
PeerOrgs: Org1和Org2是指定的默認(rèn)peer節(jié)點(diǎn)的組織可以無限添加
# ---------------------------------------------------------------------------
# Org1
# ---------------------------------------------------------------------------
- Name: Org1 //名字,自己指定
Domain: org1.example.com //域名同樣跟orderer的域名一樣
EnableNodeOUs: false //是否支持nodejs來編寫鏈碼
Template: //模板
Count: 1 //生成的peer節(jié)點(diǎn)數(shù),訪問的域名peer0.org1.example.com //0對應(yīng)1,1對2
Users:
Count: 1 //操作節(jié)點(diǎn)的普通用戶的個(gè)數(shù),還會(huì)默認(rèn)生成一個(gè)管理員用戶
user1@org1.example.com
使用template和specs的區(qū)別:
使用specs二級(jí)域名可以自己指定
使用template二級(jí)域名就是默認(rèn)的peer0這種類似
可以單獨(dú)也可以混合使用
新增組織
1.新增組織證書配置
echo '
PeerOrgs:
# ---------------------------------------------------------------------------
# Org4
# ---------------------------------------------------------------------------
- Name: Org4
Domain: org4.example.com
EnableNodeOUs: true
Template:
Count: 1
Users:
Count: 1
' > crypto-config.yaml
cryptogen extend --config=./crypto-config.yaml
- 新增組織定義到區(qū)塊鏈
echo '
Organizations:
- &Org4
Name: Org4MSP
ID: Org4MSP
MSPDir: crypto-config/peerOrganizations/org4.example.com/msp
Policies:
Readers:
Type: Signature
Rule: "OR('Org4MSP.admin', 'Org4MSP.peer', 'Org4MSP.client')"
Writers:
Type: Signature
Rule: "OR('Org4MSP.admin', 'Org4MSP.client')"
Admins:
Type: Signature
Rule: "OR('Org4MSP.admin')"
Endorsement:
Type: Signature
Rule: "OR('Org4MSP.peer')"
AnchorPeers:
- Host: peer0.org4.example.com
Port: 7051
' > configtx.yaml
configtxgen -printOrg Org4MSP > ./channel-artifacts/org.json
3.啟動(dòng)新組織容器
docker run -tid \
--name=peer0.org4.example.com \
--restart=always \
-p 7051:7051 \
-p 7052:7052 \
-p 7053:7053 \
-w /opt/gopath/src/github.com/hyperledger/fabric/peer \
-e CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock \
-e CORE_PEER_ID=peer0.org4.example.com \
-e CORE_PEER_ADDRESS=peer0.org4.example.com:7051 \
-e CORE_PEER_LISTENADDRESS=0.0.0.0:7051 \
-e CORE_PEER_CHAINCODEADDRESS=peer0.org4.example.com:7052 \
-e CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 \
-e CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org4.example.com:7051 \
-e CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org4.example.com:7051 \
-e CORE_PEER_LOCALMSPID=Org4MSP \
-e FABRIC_LOGGING_SPEC=INFO \
-e CORE_PEER_TLS_ENABLED=true \
-e CORE_PEER_GOSSIP_USELEADERELECTION=true \
-e CORE_PEER_GOSSIP_ORGLEADER=false \
-e CORE_PEER_PROFILE_ENABLED=true \
-e CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt \
-e CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key \
-e CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt \
-e CORE_CHAINCODE_EXECUTETIMEOUT=300s \
-v /var/run/:/host/var/run/ \
-v /opt/multiple-deployment/crypto-config/peerOrganizations/org4.example.com/peers/peer0.org4.example.com/msp:/etc/hyperledger/fabric/msp \
-v /opt/multiple-deployment/crypto-config/peerOrganizations/org4.example.com/peers/peer0.org4.example.com/tls:/etc/hyperledger/fabric/tls \
--add-host=orderer0.example.com:10.160.0.122 \
--add-host=orderer1.example.com:10.160.0.115 \
--add-host=orderer2.example.com:10.160.0.200 \
hyperledger/fabric-peer:latest sh -c 'peer node start'
docker run -tid \
--name=peer0.org4.example.com.cli \
--restart=always \
-w /opt/gopath/src/github.com/hyperledger/fabric/peer \
-e GOPATH=/opt/gopath \
-e CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock \
-e FABRIC_LOGGING_SPEC=INFO \
-e CORE_PEER_ID=peer0.org4.example.com \
-e CORE_PEER_ADDRESS=peer0.org4.example.com:7051 \
-e CORE_PEER_LOCALMSPID=Org4MSP \
-e CORE_PEER_TLS_ENABLED=true \
-e CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/peers/peer0.org4.example.com/tls/server.crt \
-e CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/peers/peer0.org4.example.com/tls/server.key \
-e CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/peers/peer0.org4.example.com/tls/ca.crt \
-e CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org4.example.com/users/Admin@org4.example.com/msp \
-v /var/run/:/host/var/run/ \
-v /opt/multiple-deployment/chaincode/go/:/opt/gopath/src/github.com/hyperledger/multiple-deployment/chaincode/go \
-v /opt/multiple-deployment/crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ \
-v /opt/multiple-deployment/channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts \
--add-host=orderer0.example.com:10.160.0.122 \
--add-host=orderer1.example.com:10.160.0.115 \
--add-host=orderer2.example.com:10.160.0.200 \
--add-host=peer0.org1.example.com:10.160.0.122 \
--add-host=peer1.org1.example.com:10.160.0.122 \
--add-host=peer0.org2.example.com:10.160.0.115 \
--add-host=peer1.org2.example.com:10.160.0.115 \
--add-host=peer0.org3.example.com:10.160.0.200 \
--add-host=peer1.org3.example.com:10.160.0.200 \
--add-host=peer0.org4.example.com:10.160.0.162 \
--add-host=peer1.org4.example.com:10.160.0.200 \
hyperledger/fabric-tools:latest /bin/bash
- 更新通道配置
channelName=mychannel
ordererAddr=orderer0.example.com:7050
ordererCa=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer0.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
4.1 獲取配置 獲取最新塊
在peer0.org1.cli容器中
docker exec -ti peer0.org1.example.com.cli bash
peer channel fetch config config_block.pb -o orderer0.example.com:7050 -c mychannel --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer0.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
4.2 修改配置
將pb文件轉(zhuǎn)json
configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > config.json
先把之前生成的org.json放進(jìn)去Org3cli容器
將org.json添加到config.json
jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org4MSP":.[1]}}}}}' config.json ./channel-artifacts/org.json > modified_config.json
將config.json 跟modified_config.json 轉(zhuǎn)pb編碼
configtxlator proto_encode --input config.json --type common.Config --output config.pb
configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb
計(jì)算兩個(gè)pb差異
configtxlator compute_update --channel_id mychannel --original config.pb --updated modified_config.pb --output org_update.pb
將更新的pb解析為json
configtxlator proto_decode --input org_update.pb --type common.ConfigUpdate | jq . > org_update.json
現(xiàn)在我們有一個(gè)解碼后的更新文件org_update.json,我們需要將其包裝在信封消息中。此步驟將使我們返回之前刪除的header字段。我們將這個(gè)文件命名為org_update_in_envelope.json:
echo '{"payload":{"header":{"channel_header":{"channel_id":"'mychannel'", "type":2}},"data":{"config_update":'$(cat org_update.json)'}}}' | jq . > org_update_in_envelope.json
轉(zhuǎn)pb編碼
configtxlator proto_encode --input org_update_in_envelope.json --type common.Envelope --output org_update_in_envelope.pb
4.3 簽名并提交更新配置
peer channel signconfigtx -f org_update_in_envelope.pb
切換環(huán)境為org2執(zhí)行更新配置,因?yàn)閡pdate也會(huì)為當(dāng)前組織簽名,所以不需要再org2簽名
docker cp peer0.org1.example.com.cli:/opt/gopath/src/github.com/hyperledger/fabric/peer/org_update_in_envelope.pb ./
scp org_update_in_envelope.pb root@10.160.0.115:/opt/multiple-deployment/channel-artifacts
docker exec -ti peer0.org2.example.com.cli bash
peer channel update -f org_update_in_envelope.pb -c mychannel -o orderer0.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer0.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
- 新組織加入通道
docker exec -ti peer0.org4.example.com.cli bash
peer channel fetch 0 mychannel.block -o orderer0.example.com:7050 -c mychannel --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer0.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
peer channel join -b mychannel.block
peer channel list