1. 啟動第一個網(wǎng)絡(luò)
首先要下載fabric的示例代碼
$ git clone https://github.com/hyperledger/fabric-samples.git
然后找到目錄first-network,這里我們會用到的一個腳本就是byfn.sh
- 首先執(zhí)行如下命令,
$ ./byfn.sh -m generate
這個命令執(zhí)行如下的操作
- 生成密碼和證書,用于身份認(rèn)證;
- 創(chuàng)建orderer的創(chuàng)世紀(jì)區(qū)塊,genesis.block;
- 創(chuàng)建通道配置交易所需的工件,channel.tx;
- 創(chuàng)建錨節(jié)點更新工件,Org1MSPanchor.tx/Org2MSPanchor.tx;
- 然后執(zhí)行命令,
$ ./byfn.sh -m up
這個命令會啟動所有所需的容器,
- 一個ordere容器;
- 四個peer節(jié)點容器;
- 一個CLI容器;
之后再CLI容器中自動運行腳本,執(zhí)行如下操作 - 創(chuàng)建通道;
- 節(jié)點加入到通道;
- 更新錨節(jié)點;
- 安裝和實例化chaincode;
- 進行查詢和更新操作;
2. 密碼生成器
使用cryptogen工具來生成用于多個網(wǎng)絡(luò)實體的密碼材料,這些證書是識別身份的代表。
通過這些證書可以運行不同的網(wǎng)絡(luò)實體之間通信過程中身份認(rèn)證的登陸/驗證
2.1 這是如何工作的?
cyptogen工具需要有個配置文件:crypto-config.yaml,這個文件包含網(wǎng)絡(luò)拓?fù)?,基于這個配置文件我們可以生成一個集合的證書和密鑰;這些證書和密鑰既可以用于這些組織,也可以用于屬于這些組織的部件。
- 每個組織都準(zhǔn)備了一個獨有的根證書(ca-cert),這個根證書把一些特殊的組件(peers和orderers節(jié)點)綁定到了這個組織;通過給每個組織分配一個特有的CA證書,我們就模擬出了一個典型的網(wǎng)絡(luò),在這個網(wǎng)絡(luò)里所有的參與者都要使用他自己獨有的證書鑒權(quán)。
- Hyperledger Fabric里面所有的交易和通信都被一個實體(entity)的私鑰(keystore)簽名,然后通過公鑰(signcerts)來進行驗證;
- 你會發(fā)現(xiàn)這個文件里面有一個計數(shù)器變量。我們用這個來指定每個組織里面的peers節(jié)點的個數(shù);這個例子里面每個組織(Orgnization)都有兩個peers節(jié)點。
- 我們這里不會去鉆研x.509證書和公鑰這些基礎(chǔ)設(shè)施的細節(jié)。
首先來簡單看一下這個yaml文件,尤其注意“Name”,“Domain”和“Specs這些參數(shù)”
OrdererOrgs:
#---------------------------------------------------------
# Orderer
# --------------------------------------------------------
- Name: Orderer
Domain: example.com
CA:
Country: US
Province: California
Locality: San Francisco
# OrganizationalUnit: Hyperledger Fabric
# StreetAddress: address for org # default nil
# PostalCode: postalCode for org # default nil
# ------------------------------------------------------
# "Specs" - See PeerOrgs below for complete description
# -----------------------------------------------------
Specs:
- Hostname: orderer
# -------------------------------------------------------
# "PeerOrgs" - Definition of organizations managing peer nodes
# ------------------------------------------------------
PeerOrgs:
# -----------------------------------------------------
# Org1
# ----------------------------------------------------
- Name: Org1
Domain: org1.example.com
網(wǎng)絡(luò)實體(entity)的命名規(guī)則如下 - "{{.Hostname}}.{{.Domain}}";這里拿ordering節(jié)點為例,orderer.example.com這個名字就和Orderer的一個MSP ID關(guān)聯(lián)上了。這個文件還包含了一個擴展文件,描述定義和符號規(guī)則,可以參考成員服務(wù)提供者(MSP: Membership Service Provider)文檔來進一步理解MSP
運行完cyptogen工具后,生成的證書和密鑰會被保存到crypto-config文件夾中。
本文例子中的crypto-config文件夾的目錄結(jié)構(gòu)如下
.
├── ordererOrganizations
│ └── example.com
│ ├── ca
│ ├── msp
│ ├── orderers
│ ├── tlsca
│ └── users
└── peerOrganizations
├── org1.example.com
│ ├── ca
│ ├── msp
│ ├── peers
│ ├── tlsca
│ └── users
└── org2.example.com
├── ca
...
- 可見,生成了兩個組織:orderer以及peer
- ca目錄下保存了根證書,有兩個文件,一個是證書文件,一個密鑰文件
我們打開證書文件ca.example.com-cert.pem來看看到底生成了什么
-----BEGIN CERTIFICATE-----
MIICLjCCAdWgAwIBAgIQT+87OiMiSvnXF74e2QOp2jAKBggqhkjOPQQDAjBpMQsw
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
YW5jaXNjbzEUMBIGA1UEChMLZXhhbXBsZS5jb20xFzAVBgNVBAMTDmNhLmV4YW1w
bGUuY29tMB4XDTE4MDMyMjEyMTY1NFoXDTI4MDMxOTEyMTY1NFowaTELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lz
Y28xFDASBgNVBAoTC2V4YW1wbGUuY29tMRcwFQYDVQQDEw5jYS5leGFtcGxlLmNv
bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFwgglk7r8kqdR69C+SA0A9EBLhL
QALXpsAnqojj9btYfTCRdXbGu1SmkE88o8Qel8q42vjN8XTQO6dmco3E3XijXzBd
MA4GA1UdDwEB/wQEAwIBpjAPBgNVHSUECDAGBgRVHSUAMA8GA1UdEwEB/wQFMAMB
Af8wKQYDVR0OBCIEIIfdbeaMNg+kkU9Kp89R7jUulqNYoaRtSLkHM/s31MyuMAoG
CCqGSM49BAMCA0cAMEQCIGHPp0S8x/nOcrNHu6SP0muhX884sxAbZnFi+GMzLZes
AiBm/LZOkRZVgw6ud3SUIjnSqUJ4dr9tcw5IOzr4MPdRMQ==
-----END CERTIFICATE-----
再打開密鑰文件看看
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8DtexDbtkQMUiZ7W
v/IVUvwSfUa2rtYBRymyXyK1X8ihRANCAARcIIJZO6/JKnUevQvkgNAPRAS4S0AC
16bAJ6qI4/W7WH0wkXV2xrtUppBPPKPEHpfKuNr4zfF00DunZnKNxN14
-----END PRIVATE KEY-----
其實看起來就是一串雜亂無章的字符串。
3. 配置交易生成器
configtxgen 工具(Channel configuration)是用來創(chuàng)建四個配置構(gòu)建
- orderer的創(chuàng)世紀(jì)塊(genesis block)
- channel的配置交易(configuration transaction)
- 以及兩個錨節(jié)點交易(anchor peer transaction) - 一個Peer組織一個
orderer區(qū)塊是ordering服務(wù)的創(chuàng)世紀(jì)區(qū)塊,并且在channel創(chuàng)建的時候會把channel交易文件廣播到orderer。
顧名思義,每個錨節(jié)點交易(anchor peer transaction)就指定了每個組織在這個channel上的的錨節(jié)點(anchor peer)
3.1. 配置交易生成器如何工作
Configtxgen會讀取配置文件configtx.yaml,這個配置文件包含了示例網(wǎng)絡(luò)的的定義。這個網(wǎng)絡(luò)里面一共有三種成員:
- 一個Orderer組織(OrdererOrg);
- 兩個Peer組織(Org1和Org2),每個組織管理和維護兩個peer節(jié)點;
參看下面這段配置
...
################################################################################
#
# Section: Organizations
#
# - This section defines the different organizational identities which will
# be referenced later in the configuration.
#
################################################################################
Organizations:
# SampleOrg defines an MSP using the sampleconfig. It should never be used
# in production but may be used as a template for other definitions
- &OrdererOrg
# DefaultOrg defines the organization which is used in the sampleconfig
# of the fabric.git development environment
Name: OrdererOrg
# ID to load the MSP definition as
ID: OrdererMSP
# MSPDir is the filesystem path which contains the MSP configuration
MSPDir: crypto-config/ordererOrganizations/example.com/msp
- &Org1
# DefaultOrg defines the organization which is used in the sampleconfig
# of the fabric.git development environment
Name: Org1MSP
# ID to load the MSP definition as
ID: Org1MSP
MSPDir: crypto-config/peerOrganizations/org1.example.com/msp
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
- &Org2
# DefaultOrg defines the organization which is used in the sampleconfig
# of the fabric.git development environment
Name: Org2MSP
# ID to load the MSP definition as
ID: Org2MSP
MSPDir: crypto-config/peerOrganizations/org2.example.com/msp
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
...
這個文件同時還定義了一個共同體(consortium) - SampleConsortium,這個共同體包含了我們的兩個Peer組織;尤其注意這個文件最上面的"Profiles"部分。你會發(fā)現(xiàn)有兩個特殊的頭:
- 一個用于orderer的創(chuàng)世紀(jì)區(qū)塊 - TwoOrgsOrdererGeneisis;
- 另一個用于我們的channel - TwoOrgsChannel
參看如下這段配置
################################################################################
#
# Profile
...
################################################################################
Profiles:
TwoOrgsOrdererGenesis:
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Consortiums:
SampleConsortium:
Organizations:
- *Org1
- *Org2
TwoOrgsChannel:
Consortium: SampleConsortium
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
- *Org2
....
這些頭和重用,當(dāng)我們創(chuàng)建工件(artifacts)的時候會把他們作為參數(shù)傳遞進去;
NOTE: 注意我們的SampleConsortium定義在系統(tǒng)級別的配置文件里,但是被我們的channel級別的配置文件引用。channels只存在于一個共同體(consortium)的職權(quán)范圍內(nèi),在網(wǎng)絡(luò)范圍內(nèi)必須定義所有的聯(lián)盟(consortia)
這個文件還包含了兩個額外的沒有太多價值的定義
- 第一個是定義了每個Peer Org的錨節(jié)點(peer0.org1.example.com & peer0.org2.example.com );
- 第二個,我們指向了每個成員的MSP的文件夾路徑,這樣我們就可以把每個org的根證書保存到orderer的創(chuàng)世區(qū)塊中了;這是一個關(guān)鍵原理,現(xiàn)在任何一個和ordering服務(wù)進行通信網(wǎng)絡(luò)實體就都可以驗證自己的數(shù)字簽名了。
4. 運行工具
4.1. 手動生成工件(artifact)
你可以在命令行里手動運行這兩個程序來生成證書和密鑰,以及那些配置工件;或者你也可以試著修改一下byfn.sh腳本來實現(xiàn)你要的目的。
我們這里簡單介紹一下怎么使用,
首先運行一下cryptogen 工具,
../bin/cryptogen generate --config=./crypto-config.yaml
你有可能會看到如下告警,可以選擇忽略這些告警
[bccsp] GetDefault -> WARN 001 Before using BCCSP, please call InitFactories(). Falling back to bootBCCSP.
接著,我們需要告訴工具configtxgen怎么找到它需要的configx.yaml配置文件,
第一步,設(shè)置環(huán)境變量,指定配置文件的搜索路徑,比如說設(shè)置成了當(dāng)前目錄
export FABRIC_CFG_PATH=$PWD
第二步,運行configtxgen工具,創(chuàng)建orderer創(chuàng)世紀(jì)區(qū)塊
../bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
你可以忽略日志中關(guān)于中間證書,證書撤銷列表(crls)和MSP配置的那些告警,在這個示例網(wǎng)絡(luò)里面我們不會使用這些功能。
4.2. 創(chuàng)建通道配置交易
現(xiàn)在,我們需要創(chuàng)建一個通道交易工件。在這之前確保環(huán)境變量$CHANNEL_NAME已經(jīng)設(shè)置好了
export CHANNEL_NAME=mychannel
# this file contains the definitions for our sample channel
../bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME
接下來,我們會在我們構(gòu)造的通道上定義Org1的錨節(jié)點。同樣的我們要保證環(huán)境變量$CHANNEL_NAME已經(jīng)設(shè)置好了并且可以被如下的命令使用,
../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
現(xiàn)在,我就可以在同一個通道上間里Org2的錨節(jié)點
../bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
5. 啟動網(wǎng)絡(luò)
我們會使用一個docker-compose腳本來把我們的網(wǎng)絡(luò)帶起來,這個docker-compose文件使用了我們之前下載的那些docker鏡像文件,并且使用我們之前創(chuàng)建的genesis.block來引導(dǎo)啟動了orderer
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
# command: /bin/bash -c './scripts/script.sh ${CHANNEL_NAME}; sleep $TIMEOUT'
volumes
如果第二行不注釋掉,這個腳本會在網(wǎng)絡(luò)啟動后執(zhí)行所有的CLI命令,但是我們希望手動的一步一步運行一下這些命令來了解一下這完整的語法和每次調(diào)用的功能
傳進去一個適當(dāng)?shù)拇笠稽c的TIMEOUT變量值(單位是秒);不然的話,CLI容器默認(rèn)會在60秒后退出。
啟動你的網(wǎng)絡(luò):
CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=<pick_a_value> docker-compose -f docker-compose-cli.yaml up -d
如果你希望看到你網(wǎng)絡(luò)的實時log,那么就不要添加-d標(biāo)志
5.1. 環(huán)境變量
為了保證后面針對peer0.org1.example.com的CLI命令可以正常工作,我們首先需要設(shè)置如下的四個環(huán)境變量。這些用于peer0.org1.example.com的環(huán)境變量都已經(jīng)打包進了CLI容器中了,就不需要額外的去傳遞這些環(huán)境變量了;但是如果你想要往其他的節(jié)點或者是orderer發(fā)送調(diào)用,你就需要相應(yīng)的提供這些參數(shù)了。檢查一下docker-compose-base.yaml文件,
# Environment variables for PEER0
CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
CORE_PEER_ADDRESS=peer0.org1.example.com:7051
CORE_PEER_LOCALMSPID="Org1MSP"
CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
5.2. 創(chuàng)建并加入通道
回想一下之前我們創(chuàng)建了通道配置交易,你可以重復(fù)這個步驟來創(chuàng)建一個額外的通道配置交易
使用configtx.yaml中相同的或者不同的配置文件并傳遞給configtxgen工具,這樣你就可以在你的網(wǎng)絡(luò)中創(chuàng)建其他的通道。
首先進入CLI container
docker exec -it cli bash
如果成功了,你會看到如下信息
root@0d78bb69300d:/opt/gopath/src/github.com/hyperledger/fabric/peer#
接下來,我們要傳遞生成的通道配置交易工件(channel.tx)給orderer作為創(chuàng)建通道請求的一部分。
我們通過-c標(biāo)簽指定了我們的通道名字,使用-f標(biāo)簽指定我們的通道配置交易,在這個例子里面是channel.tx。當(dāng)然你也可以掛載你自己的配置交易
export CHANNEL_NAME=mychannel
# the channel.tx file is mounted in the channel-artifacts directory within your CLI container
# as a result, we pass the full path for the file
# we also pass the path for the orderer ca-cert in order to verify the TLS handshake
# be sure to replace the $CHANNEL_NAME variable appropriately
peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME
-f ./channel-artifacts/channel.tx --tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
Note:注意命令行參數(shù)里面--cafile這個選項,這是orderer的根證書的文件路徑,允許我們來驗證TLS握手。
這個命令返回了一個創(chuàng)世紀(jì)區(qū)塊-<channel-ID.block>,我們會使用這個區(qū)塊來加入通道中。
Note:手動運行完這些命令以后你還會留在容器中,記住,當(dāng)你要針對除了peer0.org1.example.com以外的其他節(jié)點創(chuàng)建通道,你要預(yù)先把所有的環(huán)境變量設(shè)置成相應(yīng)的值
現(xiàn)在我們開始把節(jié)點peer0.org1.example.com加入到通道中
# By default, this joins ``peer0.org1.example.com`` only
# the <channel-ID.block> was returned by the previous command
peer channel join -b <channel-ID.block>
5.3. 安裝和實例化(Instantiate)Chaincode
Note: 我們這里利用一個簡單的chaincode來學(xué)習(xí)怎么樣編寫自己的chaincode
應(yīng)用程序通過chaincode來作用于區(qū)塊鏈賬本。我們需要在每個可能執(zhí)行我們交易的節(jié)點上面安裝chaincode,然后在通道上實例化這些chaincode
首先在四個peer節(jié)點上的一個安裝示例程序,下面的命令把我們的源代碼部署到四個節(jié)點的文件系統(tǒng)中
peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02
Next, instantiate the chaincode on the channel. This will initialize the chaincode on the channel, set the endorsement
接下來,在通道上實例化chaincode,這一步驟會在通道上初始化chaincode;設(shè)置chaincode的背書(endorsement)策略;為目標(biāo)節(jié)點啟動chaincode容器。注意一下-P選項,這是我們的策略,定義當(dāng)前這個chaincode在驗證一個交易過程中的背書的級別(?)
在如下的命令中,你注意到我們是這么來指定我們的策略的:-P "OR('Org0MSP.member', 'Org1MSP.member')"。這就意味著我們需要屬于Org1或者Org2的節(jié)點的背書,如果我們把語法改為AND,這樣我們就需要兩個組織節(jié)點的背書
# 確保你替換了 $CHANNEL_NAME 環(huán)境變量值
# 如果你沒有把你安裝的chaincode的名字定義為mycc,那么就修改成你定義的那個
peer chaincode instantiate -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
-C $CHANNEL_NAME -n mycc -v 1.0
-c '{"Args":["init","a", "100", "b","200"]}'
-P "OR ('Org1MSP.member','Org2MSP.member')"
5.4. 查詢
接下來我們查詢一下a的值,來確保chaincode正常實例化了,并且state DB也部署了。查詢的語法如下,
# 確保-C 和 -n 標(biāo)志都設(shè)置準(zhǔn)確了
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'
5.5. 調(diào)用
Now let’s move 10 from a to b . This transaction will cut a new block and update the state DB. The syntax for invoke
現(xiàn)在我們從a轉(zhuǎn)移10個到b,這個交易會切割一個新的區(qū)塊,并且更新state DB。調(diào)用的語法如下,
# be sure to set the -C and -n flags appropriately
peer chaincode invoke -o orderer.example.com:7050 --tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
-C $CHANNEL_NAME -n mycc
-c '{"Args":["invoke","a","b","10"]}'
然后再查詢一下,我們要確認(rèn)之前的調(diào)用正常執(zhí)行了。
# be sure to set the -C and -n flags appropriately
peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'
正常來說我們會得到如下的查詢結(jié)果:
Query Result: 90
5.6. 這些現(xiàn)象背后發(fā)生了些什么?
NOTE: 上述的那些步驟是在script.sh腳本中運行的,cli容器啟動以后會自動調(diào)用這個腳本運行;在你自己一步步執(zhí)行的時候,你需要在docker-compose-cli.yaml文件中把這一行給注銷掉。
- 一個腳本 - script.sh
這個腳本被安裝到CLI容器內(nèi)了。這個腳本驅(qū)動了依據(jù)提供的channel名稱來運行createChannel的命令,同時使用channel.tx文件來進行通道配置;
createChannel的輸出是一個genesis區(qū)塊 - <your_channel_name>.block - 這個區(qū)塊會保存到peers節(jié)點的文件系統(tǒng)中并且包含一個channel.tx指定的通道配置;
對4個peer執(zhí)行joinChannel,之前一步生成的genesis區(qū)塊做為輸入。這個命令指示這些節(jié)點加入通道<your_channel_name>中,并且創(chuàng)建一個由<your_channel_name>.block起頭的區(qū)塊鏈;
現(xiàn)在我們就創(chuàng)建了一個通道,包含4個節(jié)點,以及兩個組織。這就是我們的TwoOrgsChannelprofile
節(jié)點peer0.org1.example.com 和peer1.org1.example.com 屬于Org1;
節(jié)點peer0.org2.example.com 和peer1.org2.example.com 屬于Org2;
這些關(guān)系通過crypto-config.yaml配置文件來定義,MSP的路徑定義在docker compose文件中;
這個時候會更新Org1MSP(peer0.org1.example.com )和Org2MSP(peer0.org2.example.com ) 的錨節(jié)點。我們是通過傳遞Org1MSPanchors.tx和Org2MSPanchors.tx工件傳遞給ordering服務(wù),同時還帶上了我們通道的名字來實現(xiàn)更新的;
- 一個chaincode - chaincode_example02安裝到了節(jié)點peer0.org1.example.com和peer0.org2.example.com中了
- 這個chaincode這個時候就在peer0.org2.example.com上實例化了(instantiated)。實例化會把chaincode添加到channel上,為每個目標(biāo)節(jié)點啟動容器,并且初始化和chaincode相關(guān)聯(lián)的密鑰對;
這個例子里面初始化值是 [”a”,”100” “b”,”200”]。這個實例化的結(jié)局是啟動了一個名叫dev-peer0.org2.example.com-mycc-1.0的容器; - 這個實例化還會為背書策略傳遞一個參數(shù)進去。這個策略定義為-P "OR
('Org1MSP.member','Org2MSP.member')",意味著任何交易都必須被一個綁定到Org1或者Org2的peer節(jié)點準(zhǔn)許(背書,endose) - 往節(jié)點peer0.org1.example.com發(fā)起一次針對變量"a"的查詢。之前有chaincode安裝到了節(jié)點peer0.org1.example.com,所以這個查詢會為Org1 peer0啟動一個名叫dev-peer0.org1.example.com-mycc-1.0的容器。同時也會返回查詢結(jié)果
- 發(fā)送一個調(diào)用給peer0.org1.example.com來從“a”轉(zhuǎn)移10到"b"上;
- 這個時候chaincode被安裝到了peer1.org2.example.com上;
- 往節(jié)點peer1.org2.example.com發(fā)起一次針對變量"a"的查詢。這個操作會啟動第三個chaincode容器,名字是dev-peer1.org2.example.com-mycc-1.0。 返回值是90,正確反映了之前的交易。
5.7. 這個證明了什么?
Chaincode必須安裝到一個節(jié)點上,以保證可以成功實現(xiàn)對賬本的讀寫。
進一步的,一個chaincode容器不會為這個節(jié)點啟動,直到發(fā)生了初始化,或者針對這個chaincode執(zhí)行了傳統(tǒng)的交易 - 讀寫(比如說,查詢"a"的值),這個交易會導(dǎo)致容器的啟動。
當(dāng)然,通道上的所有peers節(jié)點維護一個額外的賬本拷貝,這個賬本拷貝由區(qū)塊鏈組成;區(qū)塊鏈上保存了不可變更的,有序的記錄,以及一個狀態(tài)數(shù)據(jù)庫來維護當(dāng)前狀態(tài)的snapshot。
這也包含那些上面沒有安裝chaincode的節(jié)點(比如說上述例子中的peer1.org1.example.com)。最終,chaincode在安裝完成后就可以對他進行訪問了(比如說上述例子中的peer1.org2.example.com ),這是因為它已經(jīng)實例化了。
5.8. 怎么看到這些交易?
可以通過查看CLI容器的logs
docker logs -f cli
你會看到如下的輸出
2017-05-16 17:08:01.366 UTC [msp] GetLocalMSP -> DEBU 004 Returning existing local MSP
2017-05-16 17:08:01.366 UTC [msp] GetDefaultSigningIdentity -> DEBU 005 Obtaining
?→default signing identity
2017-05-16 17:08:01.366 UTC [msp/identity] Sign -> DEBU 006 Sign: plaintext:
?→0AB1070A6708031A0C08F1E3ECC80510...6D7963631A0A0A0571756572790A0161
2017-05-16 17:08:01.367 UTC [msp/identity] Sign -> DEBU 007 Sign: digest:
?→E61DB37F4E8B0D32C9FE10E3936BA9B8CD278FAA1F3320B08712164248285C54
Query Result: 90
2017-05-16 17:08:15.158 UTC [main] main -> INFO 008 Exiting.....
===================== Query on PEER3 on channel 'mychannel' is successful
?→=====================
===================== All GOOD, BYFN execution completed =====================
_____ _ _ ____
| ____| | \ | | | _ \
| _| | \| | | | | |
| |___ | |\ | | |_| |
|_____| |_| \_| |____/
5.9. 怎么查看chaincode的日志
查看每個單獨的chaincode容器來看看針對每個容器分別執(zhí)行的交易,這里是每個容器的輸出的集合
$ docker logs dev-peer0.org2.example.com-mycc-1.0
04:30:45.947 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW]
ex02 Init
Aval = 100, Bval = 200
$ docker logs dev-peer0.org1.example.com-mycc-1.0
04:31:10.569 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW]
ex02 Invoke
Query Response:{"Name":"a","Amount":"100"}
ex02 Invoke
Aval = 90, Bval = 210
$ docker logs dev-peer1.org2.example.com-mycc-1.0
04:31:30.420 [BCCSP_FACTORY] DEBU : Initialize BCCSP [SW]
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}
6. 理解Docker Compose拓?fù)?/h2>
BYFN這個例子給我提供了兩個Docker compose文件的風(fēng)味,這兩個例子都是從docker-compose-base.yaml文件擴展而來。我們的第一種風(fēng)味,docker-compose-cli.yaml,提供給我們CLI容器,以及一個orderer,四個peers。
NOTE: 這一部分的描述涉及SDK中docker-compose文件的設(shè)計,參考Node SDK部分的描述來理解這些步驟的細節(jié)
第二道風(fēng)味,docker-compose-e2e.yaml,可以用于通過Node.js SDK運行一個端到端的測試
拋開SDK的功能不說,它的主要區(qū)別就是,有一個用于fabric-ca服務(wù)的容器
結(jié)果就是我們可以通過發(fā)送一個REST調(diào)用給CAs組織來進行用戶注冊和登記。
如果你想不通過運行byfn.sh腳本來使用docker-compose-e2e.yaml文件,那你可能需要做一些小的改動,
我們需要在yaml文件里面指定我們組織的CA's(證書)的密鑰,這些密鑰保存在crypto-config文件夾中。
比如說,Org1的私鑰就保存在這樣的目錄中 - crypto-config/peerOrganizations/org1.example.com/ca/。私鑰是一個以_sk為后綴的很長的哈希值。Org2的路徑是 - crypto-config/peerOrganizations/org2.example.com/ca/;
在docker-compose-e2e.yaml文件中,需要為ca0和ca1更新一下FABRIC_CA_SERVER_TLS_KEYFILE變量的值。在啟動ca服務(wù)的時候你還需要修改命令行參數(shù)中的路徑,你要為每個CA容器提供兩次私鑰。
7 使用CouchDB
狀態(tài)數(shù)據(jù)庫可以從默認(rèn)的goleveldb切換成CouchDB。同樣的chaincode也可以和CoubhDB一起工作,當(dāng)然,用了CouchDB之后,我們就可能執(zhí)行更加豐富和復(fù)雜的查詢DB操作了;這些查詢可以通過JSON的格式來進行
使用CouchDB需要在之前描述的生成工件的步驟基礎(chǔ)上做一些修改,在啟動網(wǎng)絡(luò)的時候把yaml文件替換成docker-compose-couch.yaml
CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=<pick_a_value> docker-compose -f docker-compose-cli.yaml -f docker-compose-couch.yaml up -d
下面chaincode_example02就可以和CouchDB一起工作了。
NOTE:如果你這里選擇把fabric-couchdb容器的端口映射到主機端口上,請確保你意識到了這里可能有安全隱患。在開發(fā)環(huán)境中做這樣的映射使得CouchDB的REST接口可用了,但是同時CouchDB的數(shù)據(jù)口內(nèi)容也通過web接口Fauxton變得可視了。生產(chǎn)環(huán)境中要注意限制外部環(huán)境的CouchDB容器的訪問。
如果你想要使用CouchDB的查詢能力,這個時候就需要使用數(shù)據(jù)建模成JSON格式的chaincode了,比如說marbles02.
首先還是創(chuàng)建通道,并把節(jié)點加進通道中去。
- 在節(jié)點peer0.org1.example.com上安裝和實例化chaincode
# be sure to modify the $CHANNEL_NAME variable accordingly for the instantiate command
peer chaincode install -n marbles -v 1.0
-p github.com/hyperledger/fabric/examples/chaincode/go/marbles02
peer chaincode instantiate
-o orderer.example.com:7050
--tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C $CHANNEL_NAME
-n marbles -v 1.0 -c '{"Args":["init"]}' -P "OR ('Org0MSP.member','Org1MSP.member')"
- 制造一些marbles,然后使用
# be sure to modify the $CHANNEL_NAME variable accordingly
peer chaincode invoke -o orderer.example.com:7050
--tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
-C $CHANNEL_NAME -n marbles
-c '{"Args":["initMarble","marble1","blue","35","tom"]}'
peer chaincode invoke -o orderer.example.com:7050
--tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
-C $CHANNEL_NAME -n marbles
-c '{"Args":["initMarble","marble2","red","50","tom"]}'
peer chaincode invoke -o orderer.example.com:7050
--tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
-C $CHANNEL_NAME -n marbles
-c '{"Args":["initMarble","marble3","blue","70","tom"]}'
peer chaincode invoke -o orderer.example.com:7050
--tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
-C $CHANNEL_NAME -n marbles
-c '{"Args":["transferMarble","marble2","jerry"]}'
peer chaincode invoke -o orderer.example.com:7050
--tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
-C $CHANNEL_NAME -n marbles
-c '{"Args":["transferMarblesBasedOnColor","blue","jerry"]}'
peer chaincode invoke -o orderer.example.com:7050
--tls $CORE_PEER_TLS_ENABLED
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
-C $CHANNEL_NAME -n marbles
-c '{"Args":["delete","marble1"]}'
? If you chose to map the CouchDB ports in docker-compose, you can now view the state database through the
- 如果你選擇在docker-compose中映射CouchDB端口,你現(xiàn)在就可以通過web接口查看CouchDB的數(shù)據(jù)庫內(nèi)容了。用瀏覽器打開http://localhost:5984/_utils。
你可以看到一個名叫mychannel的數(shù)據(jù)庫,里面有一些文件,
Note: For the below commands, be sure to update the $CHANNEL_NAME variable appropriately.
You can run regular queries from the CLI (e.g. reading marble2 ):
NOTE: 對于下面的這些命令,請確保你以及相應(yīng)的更新了$CHANNEL_NAME變量。
你可以通過CLI來進行一些常規(guī)查詢,比如說讀取marble2:
peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["readMarble","marble2"]}'
輸出結(jié)果會顯示marble2的一些細節(jié),
Query Result: {"color":"red","docType":"marble","name":"marble2","owner":"jerry","size":50}
You can retrieve the history of a specific marble - e.g. marble1 :
你可以獲取一個特定marble的歷史 - 比如說marble1:
peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["getHistoryForMarble","marble1"]}'
結(jié)果會顯示marble1的歷史交易記錄,
Query Result: [
{ "TxId":"1c3d3caf124c89f91a4c0f353723ac736c58155325f02890adebaa15e16e6464",
"Value":{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"tom"}},
{ "TxId":"755d55c281889eaeebf405586f9e25d71d36eb3d35420af833a20a2f53a3eefd",
"Value":{"docType":"marble","name":"marble1","color":"blue","size":35,"owner":"jerry"}},
{ "TxId":"819451032d813dde6247f85e56a89262555e04f14788ee33e28b232eef36d98f",
"Value":}
]
You can also perform rich queries on the data content, such as querying marble fields by owner jerry :
你也可以對數(shù)據(jù)內(nèi)容進行富查詢,比如說查詢jerry這個擁有者的marble,
peer chaincode query -C $CHANNEL_NAME -n marbles -c '{"Args":["queryMarblesByOwner","jerry"]}'
輸出顯示jerry擁有的兩個marbles,
Query Result: [
{ "Key":"marble2", "Record":{"color":"red","docType":"marble","name":"marble2","owner":"jerry","size":50}},
{ "Key":"marble3", "Record":{"color":"blue","docType":"marble","name":"marble3","owner":"jerry","size":70}}
]
7.1. 為什么用CouchDB
CouchDB采用的是NoSQL的解決方案,是一種面向文檔的數(shù)據(jù)庫,文檔的內(nèi)容都以鍵值對的方式保存在數(shù)據(jù)庫中;可以實簡單的鍵值對,列表,或者映射表。
除了LevelDB支持的這些鍵值查詢之外,CouchDB還支持完整數(shù)據(jù)的富查詢能力,比如說針對整個區(qū)塊鏈數(shù)據(jù)的非鍵值查詢,這些數(shù)據(jù)內(nèi)容以JSON格式存儲。
CouchDB還可以增強區(qū)塊鏈上數(shù)據(jù)保護的安全性。