搭建Hyperledger Fabric

搭建流程

環(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
image.png

成功夠可以看到啟動(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
![image.png](https://upload-images.jianshu.io/upload_images/10280146-81a5a1ff88b8795e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
# 新建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

image.png

安裝后請確認(rèn)環(huán)境變量可用
go version
go env
2.下載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
  1. 新增組織定義到區(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
    
  1. 更新通道配置
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
  1. 新組織加入通道
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 
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容