本章零基礎地介紹瞭如何快速體驗超級帳本搭建的區塊鏈網絡,咱們先繞過了比較複雜的初始化配置,用官方提供的fabric-samples提供的配置和鏈碼示例,展現瞭如何調用和查詢鏈碼,對Hyperledger Fabric實現的功能有一個初步的認識。html
Hyperledger Fabric 1.0依賴Docker執行智能合約,須要先安裝Docker和Docker Compose的運行環境。java
一、Docker的安裝和使用python
Docker支持Linux、Mac、Windows等多個平臺,安裝文檔參考:https://docs.docker.com/engine/installation。git
在Linux環境下Docker的安裝github
Ubuntu、Debian、CentOS等Linux系統,能夠經過Docker官方提供的腳本進行安裝:golang
curl -sSL https://get.docker.com | sh算法
而後把用戶加入到docker組,非root用戶USER能夠執行docker命令(可能須要從新登陸生效):docker
sudo usermod -aG docker $USER編程
若是是Ubuntu或者Debian操做系統,修改Docker的配置文件/etc/default/docker,增長Docker的socket綁定,運行在Docker中的進程才能經過映射的socket調用Docker的API執行鏡像編譯和建立容器等操做。json
DOCKER_OPTS="-s=aufs -r=true --api-cors-header='*' -H tcp://0.0.0.0:2375 -H
接着,重啓Docker服務讓配置生效:
sudo service docker start
CentOS系統採用Systemd進行系統和服務管理,配置文件的修改方法是不同的。CentOS系統下Docker的配置文件是/etc/sysconfig/docker,一樣要修改DOCKER_OPTS選項。
還須要修改/usr/lib/systemd/system/docker.service文件,在[Service]的ExexStart=下面增長一行$DOCKER_OPTS,以下所示:
[Service]
Type=notify
NotifyAccess=all
EnvironmentFile=-/etc/sysconfig/docker
EnvironmentFile=-/etc/sysconfig/docker-storage
EnvironmentFile=-/etc/sysconfig/docker-network
Environment=GOTRACEBACK=crash
Environment=DOCKER_HTTP_HOST_COMPAT=1
Environment=PATH=/usr/libexec/docker:/usr/bin:/usr/sbin
ExecStart=/usr/bin/dockerd-current \
--add-runtime docker-runc=/usr/libexec/docker/docker-runc-current \
--default-runtime=docker-runc \
--exec-opt native.cgroupdriver=systemd \
--userland-proxy-path=/usr/libexec/docker/docker-proxy-current \
$DOCKER_OPTS \
$OPTIONS \
$DOCKER_STORAGE_OPTIONS \
$DOCKER_NETWORK_OPTIONS \
$ADD_REGISTRY \
$BLOCK_REGISTRY \
$INSECURE_REGISTRY
重啓服務讓配置生效:
systemctl daemon-reload
systemctl restart docker.service
其餘環境下Docker的安裝
Windows和Mac都提供了安裝包,直接下載便可安裝:
Docker for Mac: https://download.docker.com/mac/stable/Docker.dmg
Docker for Windows: https://download.docker.com/win/stable/InstallDocker.msi
Docker國內鏡像倉庫
國外的鏡像下載較慢,能夠設置國內的鏡像,阿里雲和DaoCloud都提供鏡像加速的服務,須要登陸註冊才能使用。
阿里雲:登陸容器Hub服務https://cr.console.aliyun.com的控制檯,左側的加速器幫助頁面會顯示爲你獨立分配的加速地址。
DaoCloud:在https://www.daocloud.io進行註冊登陸,而後點擊加速器,就能夠獲取加速器的相關配置。
修改Docker鏡像倉庫的辦法是在DOCKER_OPTS裏增長registry-mirror參數,好比:
DOCKER_OPTS="-s=aufs -r=true --api-cors-header='*' -H tcp://0.0.0.0:2375
-H unix:///var/run/docker.sock
--registry-mirror=http://069f616f.m.daocloud.io」
重啓Docker服務就可使用鏡像加速了。
Docker在Windows和Mac中的版本能夠在圖形界面添加鏡像倉庫。
Docker經常使用命令
Docker經常使用命令如圖所示。
更多的命令請查看幫助文檔和在線文檔:https://docs.docker.com/engine/reference/commandline/docker。
二、Docker Compose的安裝和使用
Docker Compose可以在一個主機上建立出相互隔離的網絡,經過命令行管理多個Docker容器,快速啓動、中止和更新容器。
Docker Compose的安裝
Docker在Windows和Mac中都已經集成了Docker Compose工具,不須要單獨安裝。在Linux系統下有多種安裝方法,以下所示:
sudo apt install python-pip
sudo pip install docker-compose
curl -L
https://github.com/docker/compose/releases/download/1.17.1/docker-compose-
uname -s
-uname -m
-o /usr/local/bin/docker-composechmod +x /usr/local/bin/docker-compose
Docker Compose的配置文件
Compose採用YAML文件定義Docker容器之間的依賴,設置環境變量和文件的持久化。咱們看一個配置文件examples/e2e_cli/base/docker-compose-base.yaml的節選:
version: '2'
services:
orderer.example.com:
container_name: orderer.example.com
image: hyperledger/fabric-orderer
environment:
- ORDERER_GENERAL_LOGLEVEL=debug
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_GENESISMETHOD=file
- ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
- ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp
# enabled TLS
- ORDERER_GENERAL_TLS_ENABLED=true
- ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key
- ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt
- ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
command: orderer
volumes:
../channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/
msp:/var/hyperledger/orderer/msp
tls/:/var/hyperledger/orderer/tls
ports:
- 7050:7050
peer0.org1.example.com:
container_name: peer0.org1.example.com
extends:
file: peer-base.yaml
service: peer-base
environment:
- CORE_PEER_ID=peer0.org1.example.com
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
- CORE_PEER_CHAINCODELISTENADDRESS=peer0.org1.example.com:7052
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051
- CORE_PEER_LOCALMSPID=Org1MSP
volumes:
- /var/run/:/host/var/run/
com/msp:/etc/hyperledger/fabric/msp
com/tls:/etc/hyperledger/fabric/tls
ports:
- 7051:7051
- 7052:7052
- 7053:7053
在這個節選的配置文件中,一共定義了1個排序服務節點orderer.example.com和1個Peer節點peer0.org1.example.com。Docker Compose目前有3個版本,這個配置文件採用的version 2的語法,配置文件的解釋如圖所示。
更多不一樣版本的配置文件說明請參考在線幫助文檔:https://docs.docker.com/compose/compose-file。
Docker Compose的經常使用命令
Docker Compose的經常使用命令如圖所示。
更多的命令查看幫助文檔和在線文檔:https://docs.docker.com/compose/reference。
三、下載超級帳本源代碼
git clone https://github.com/hyperledger/fabric.git
也能夠打包下載文件後解壓:https://github.com/hyperledger/fabric/archive/release.zip。
先最小化地體驗一下超級帳本的環境,更詳細的部署流程參考第11章。
一、下載Docker鏡像文件
超級帳本源碼scripts目錄下有多個下載鏡像的腳本,咱們能夠修改權限之後直接運行:
chmod +x bootstrap-1.0.0.sh
sed -i '' 's/curl/#curl/g' bootstrap-1.0.0.sh
#其餘系統執行以下命令(不下載二進制文件)
sed -i 's/curl/#curl/g' bootstrap-1.0.0.sh
./bootstrap-1.0.0.sh
根據網絡狀況,可能須要等待一段時間。下面是下載的Docker鏡像文件:
localhost:dive-into-fabric clarity$ docker images
REPOSITORY TAG IMAGE ID SIZE
hyperledger/fabric-tools latest 0403fd1c72c7 1.32GB
hyperledger/fabric-tools x86_64-1.0.0 0403fd1c72c7 1.32GB
hyperledger/fabric-couchdb latest 2fbdbf3ab945 1.48GB
hyperledger/fabric-couchdb x86_64-1.0.0 2fbdbf3ab945 1.48GB
hyperledger/fabric-kafka latest dbd3f94de4b5 1.3GB
hyperledger/fabric-kafka x86_64-1.0.0 dbd3f94de4b5 1.3GB
hyperledger/fabric-zookeeper latest e545dbf1c6af 1.31GB
hyperledger/fabric-zookeeper x86_64-1.0.0 e545dbf1c6af 1.31GB
hyperledger/fabric-orderer latest e317ca5638ba 179MB
hyperledger/fabric-orderer x86_64-1.0.0 e317ca5638ba 179MB
hyperledger/fabric-peer latest 6830dcd7b9b5 182MB
hyperledger/fabric-peer x86_64-1.0.0 6830dcd7b9b5 182MB
hyperledger/fabric-javaenv latest 8948126f0935 1.42GB
hyperledger/fabric-javaenv x86_64-1.0.0 8948126f0935 1.42GB
hyperledger/fabric-ccenv latest 7182c260a5ca 1.29GB
hyperledger/fabric-ccenv x86_64-1.0.0 7182c260a5ca 1.29GB
hyperledger/fabric-ca latest a15c59ecda5b 238MB
hyperledger/fabric-ca x86_64-1.0.0 a15c59ecda5b 238MB
REPOSITORY表明的是鏡像的倉庫名稱,每一個倉庫下面都有打了不一樣TAG的標籤名稱,表明不一樣的版本。一般最少有兩個標籤,一個是latest;另一個的命名規則是「主機CPU類型–超級帳本主版本號–snapshot–代碼庫版本號」,其中主機CPU類型爲x86_64,說明是Intel的64位CPU,超級帳本的主版本爲1.0.0,snapshot是固定名稱,代碼庫版本號爲58cde93,它是git代碼庫最近一次提交版本號的前7位。snapshot和代碼庫版本號只有經過本地編譯的時候纔會出現。每次make docker的時候都會檢查是否有文件改動,若是有變化的文件,則會從新構建,生成新的鏡像再標記成latest。鏡像文件詳細的解釋請參考第11章的相關內容。
二、部署超級帳本網絡
運行超級帳本須要設置較多的初始化配置,咱們先繞開初始化過程,用fabric-samples工程中已經生成的配置文件來體驗部署安裝的過程:
git clone https://github.com/hyperledger/fabric-samples.git
進入basic-network目錄,利用docker-compose啓動容器:
cd fabric-samples/basic-network
docker-compose -f docker-compose.yml up -d
查看已經啓動的容器(輸出進行了刪減):
localhost:basic-network clarity$ docker ps
CONTAINER ID IMAGE NAMES
efddfbf4fc0a hyperledger/fabric-peer:x86_64-1.0.0 peer0.org1.example.com
606d13c1e7a2 hyperledger/fabric-couchdb:x86_64-1.0.0 couchdb
d8c870db8634 hyperledger/fabric-ca:x86_64-1.0.0 ca.example.com
c6f25a5e6fd6 hyperledger/fabric-tools:x86_64-1.0.0 cli
a5f6331c5bc5 hyperledger/fabric-orderer:x86_64-1.0.0 orderer.example.com
切換到管理員用戶再建立通道和加入通道:
docker exec -it -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@
org1.example.com/msp" peer0.org1.example.com bash
peer channel create -o orderer.example.com:7050 -c mychannel -f /etc/hyperledger/
configtx/channel.tx
peer channel join -b mychannel.block
exit
#退出Peer節點容器peer0.org1.example.com,進入cli容器安裝鏈碼和實例化:
# 進入cli容器
docker exec -it cli /bin/bash
peer chaincode install -n mycc -v v0 -p github.com/chaincode_example02
peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n mycc -v
v0 -c '{"Args":["init","a","100","b","200"]}'
三、鏈碼調用和查詢
鏈碼實例化之後,能夠查詢初始值,一樣是在cli容器裏執行下面的操做:
peer chaincode query -C mychannel -n mycc -v v0 -c '{"Args":["query","a"]}'
查詢結果顯示爲Query Result: 100,詳細信息以下:
2017-08-09 14:47:05.853 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing
local MSP
2017-08-09 14:47:05.853 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining
default signing identity
2017-08-09 14:47:05.853 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003
Using default escc
2017-08-09 14:47:05.854 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004
Using default vscc
2017-08-09 14:47:05.854 UTC [msp/identity] Sign -> DEBU 005 Sign: plaintext: 0A9
1070A6708031A0C08A9A694D00510...6D7963631A0A0A0571756572790A0161
2017-08-09 14:47:05.854 UTC [msp/identity] Sign -> DEBU 006 Sign: digest: E18FC9
7C13D550C5E3349AAD49523A6D7C71B4E51C219CD9A8799DEF54FFFE66
Query Result: 100
2017-08-09 14:47:05.886 UTC [main] main -> INFO 007 Exiting.....
調用鏈碼,從「a」轉移10到「b」:
peer chaincode invoke -C mychannel -n mycc -v v0 -c '{"Args":["invoke","a",
"b","10"]}'
顯示調用成功的結果:
2017-08-09 14:49:46.018 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 0cb
Chaincode invoke successful. result: status:200
再次查詢「a」和「b」的值:
peer chaincode query -C mychannel -n mycc -v v0 -c '{"Args":["query","a"]}'
peer chaincode query -C mychannel -n mycc -v v0 -c '{"Args":["query","b"]}'
查詢結果顯示「a」的值爲Query Result: 90,「b」的值爲Query Result: 210。
四、常見錯誤
請求調用者權限不足
調用的時候設置了錯誤的MSP,好比須要管理員才能執行建立通道的操做,可是設置了普通的成員MSP,會出現Error: Got unexpected status: BAD_REQUEST的錯誤:
2017-08-09 14:49:04.652 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing
local MSP
2017-08-09 14:49:04.652 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining
default signing identity
2017-08-09 14:49:04.654 UTC [channelCmd] InitCmdFactory -> INFO 003 Endorser and
orderer connections initialized
2017-08-09 14:49:04.656 UTC [msp] GetLocalMSP -> DEBU 004 Returning existing
local MSP
2017-08-09 14:49:04.656 UTC [msp] GetDefaultSigningIdentity -> DEBU 005 Obtaining
default signing identity
2017-08-09 14:49:04.657 UTC [msp] GetLocalMSP -> DEBU 006 Returning existing
local MSP
2017-08-09 14:49:04.657 UTC [msp] GetDefaultSigningIdentity -> DEBU 007 Obtaining
default signing identity
2017-08-09 14:49:04.657 UTC [msp/identity] Sign -> DEBU 008 Sign: plaintext: 0A8
8060A074F7267314D535012FC052D...53616D706C65436F6E736F727469756D
2017-08-09 14:49:04.657 UTC [msp/identity] Sign -> DEBU 009 Sign: digest: F77320
AE89B131CE75A858A4A450CF0F35301DA62FE1DE465CAEF4439F6FC520
2017-08-09 14:49:04.657 UTC [msp] GetLocalMSP -> DEBU 00a Returning existing
local MSP
2017-08-09 14:49:04.657 UTC [msp] GetDefaultSigningIdentity -> DEBU 00b Obtaining
default signing identity
2017-08-09 14:49:04.657 UTC [msp] GetLocalMSP -> DEBU 00c Returning existing
local MSP
2017-08-09 14:49:04.657 UTC [msp] GetDefaultSigningIdentity -> DEBU 00d Obtaining
default signing identity
2017-08-09 14:49:04.657 UTC [msp/identity] Sign -> DEBU 00e Sign: plaintext: 0AB
F060A1508021A0608E0D591D00522...A38A58EED7B94AC4CB800B86F0A5EF03
2017-08-09 14:49:04.658 UTC [msp/identity] Sign -> DEBU 00f Sign: digest: 475A33
426FA36D50F090AAF7C3AAAB2BF34339191BA74D4A60BF13460B241329
Error: Got unexpected status: BAD_REQUEST
Usage:
peer channel create [flags]
Flags:
-c, --channelID string In case of a newChain command, the channel ID to
create.
-f, --file string Configuration transaction file generated by a tool
such as configtxgen for submitting to orderer
-t, --timeout int Channel creation timeout (default 5)
Global Flags:
--cafile string Path to file containing PEM-encoded trusted
certificate(s) for the ordering endpoint
--logging-level string Default logging level and overrides, see
core.yaml for full syntax
-o, --orderer string Ordering service endpoint
--test.coverprofile string Done (default "coverage.cov")
--tls Use TLS when communicating with the orderer endpoint
-v, --version Display current version of fabric peer server
傳遞錯誤的通道名稱
好比通道名稱是mychannel,傳遞參數的時候寫成了錯誤的myc:
peer chaincode query -C myc -n mycc -v v0 -c '{"Args":["query","a"]}'
會出現以下錯誤:
2017-08-09 14:40:36.703 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing
local MSP
2017-08-09 14:40:36.703 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining
default signing identity
2017-08-09 14:40:36.706 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003
Using default escc
2017-08-09 14:40:36.706 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004
Using default vscc
2017-08-09 14:40:36.707 UTC [msp/identity] Sign -> DEBU 005 Sign: plaintext: 0A9
1070A6708031A0C08A4A394D00510...30300A000A04657363630A0476736363
2017-08-09 14:40:36.707 UTC [msp/identity] Sign -> DEBU 006 Sign: digest: F085E1
89A0765713209DD8802DDF57B054EBCD294305A500F56BC34CB3D2E577
Error: Error endorsing chaincode: rpc error: code = Unknown desc = chaincode
error (status: 500, message: chaincode exists mycc)
Usage:
peer chaincode instantiate [flags]
Flags:
-C, --channelID string The channel on which this command should be executed
(default "testchainid")
-c, --ctor string Constructor message for the chaincode in JSON format
(default "{}")
-E, --escc string The name of the endorsement system chaincode to be used
for this chaincode
-l, --lang string Language the chaincode is written in (default "golang")
-n, --name string Name of the chaincode
-P, --policy string The endorsement policy associated to this chaincode
-v, --version string Version of the chaincode specified in install/
instantiate/upgrade commands
-V, --vscc string The name of the verification system chaincode to be
used for this chaincode
Global Flags:
--cafile string Path to file containing PEM-encoded trusted
certificate(s) for the ordering endpoint
--logging-level string Default logging level and overrides, see core.
yaml for full syntax
-o, --orderer string Ordering service endpoint
--test.coverprofile string Done (default "coverage.cov")
--tls Use TLS when communicating with the orderer endpoint
在docker-compose.yml文件中,咱們能夠看到有ORDERER_GENERAL_LEDGERTYPE= ram的設置,這是傳遞給節點的參數。給節點傳遞參數的方法有多種方式:環境變量、配置文件、動態環境變量、默認值。
程序在啓動的時候會讀取配置文件和環境變量的值,分別保存到不一樣變量緩存起來,在程序須要獲取某個變量值的時候,不一樣傳遞方法的參數讀取流程圖如圖所示。
從圖中能夠看到,若是配置了自動從環境變量獲取參數的值,那麼每次都實時地從環境變量中獲取,不然依次讀取程序啓動時從環境變量、配置文件中讀取後緩存到內存中的值,優先獲取到的值做爲返回值。若是都沒有獲取到,則返回空,交給程序進行處理,程序可能會以默認值運行,也可能會報錯中止運行,這跟業務邏輯有關係。因此,只有在設置了自動從環境變量中獲取參數的狀況下,才能在運行時經過修改環境變量改變參數的值。
每一個環境變量的名稱都有一個前綴,每一個模塊都是單獨設置的,好比ORDERER_GENERAL_
LEDGERTYPE的前綴是ORDERER,一般每一個模塊的前綴是不同的。好比這裏的ORDERER表明的是排序服務節點,Peer節點的變量名稱前綴是CORE。環境變量名稱是以「_」做爲分隔符的,表明一種層級,是和配置文件一一對應的,好比ORDERER_GENERAL_LEDGERTYPE對應orderer.yaml配置文件的General.LedgerType。環境變量和配置文件的變量名稱都是不區分大小寫的,內部會統一轉換成小寫的變量名稱進行處理。
配置文件路徑優先讀取環境變量設置的路徑,排序服務節點和Peer節點都是相同的環境變量。若是沒有設置環境變量,則默認是應用程序所在的目錄,而後環境變量GOPATH路徑對應到模塊代碼工程下的目錄,好比排序服務節點的目錄是$GOPATH/src/github.com/hyperledger/fabric/orderer,Peer節點的目錄是$GOPATH/src/github.com/hyperledger/fabric/peer。
節點配置參數的傳遞規則
通常狀況下,配置文件的名稱設置成模塊前綴的小寫,好比排序服務節點的配置文件名稱是orderer,Peer節點的配置文件名稱是core。
配置文件名稱的後綴支持:json、toml、yaml、properties、props、prop,它們分別對應JSON文件、TOML文件、YAML文件和Properties文件,程序設置配置文件的時候若是不指定後綴,則按支持的後綴順序在配置文件路徑下進行搜索,找到第一個匹配的文件做爲最終的配置文件。文件路徑和文件後綴按照廣度優先的搜索順序,即在同一路徑下匹配完全部文件後綴再進入下一個文件路徑。假設Peer節點沒有設置配置文件路徑,$GOPATH的路徑是/opt/gopath,則有兩個目錄下的文件以下所示:
vagrant@hyperledger-devenv:v0.2.2-58cde93: /opt/gopath/bin$ tree .
.
├── core.yaml
└── peer
vagrant@hyperledger-devenv:v0.2.2-58cde93: /opt/gopath/bin$ tree /opt/gopath/
src/github.com/hyperledger/fabric/peer
.
├── core.json
└── peer
搜索路徑順序是:
./core.json ./core.toml ./core.yaml ./core.properties ./core.props ./core.prop /opt/gopath/src/github.com/hyperledger/fabric/peer/core.json /opt/gopath/src/github.com/hyperledger/fabric/peer/core.toml /opt/gopath/src/github.com/hyperledger/fabric/peer/core.yaml /opt/gopath/src/github.com/hyperledger/fabric/peer/core.properties /opt/gopath/src/github.com/hyperledger/fabric/peer/core.props /opt/gopath/src/github.com/hyperledger/fabric/peer/core.prop
因此最終獲取到的配置文件是:./core.yaml。
而不是/opt/gopath/src/github.com/hyperledger/
fabric/peer/core.json。
深度探索區塊鏈
Hyperledger技術與應用
區塊鏈
張增駿,董寧,朱軒彤,陳劍雄 著
本書由超級帳本執行董事Brian Behlendorf領銜推薦,區塊鏈一線落地實踐團隊、Hyperleger會員智鏈骨幹團對撰寫。深刻講解Hyperledger Fabric 1.0的架構、執行邏輯、核心功能實現、從零部署,並以票據案例爲例,講解具體開發實踐,穿插開發所需的最佳實踐和遇到的問題解決。
機械工業
出版社
華章科技是機械出版社的旗下品牌,出版了「計算機科學叢書」等近30個經典套系,在各個細分領域均處於領導地位,其中《Java編程思想》、《算法導論》、《編譯原理》、《數據挖掘:概念與技術》、《深刻理解計算機系統》、《深刻理解Java虛擬機》等著做猶如計算機圖書領域的璀璨明珠,長銷不衰!
本文內容節選自《深度探索區塊鏈:Hyperledger技術與應用》一書的第2章《超級帳本初體驗》。
本書做者:張增駿,董寧,朱軒彤,陳劍雄
感謝機械工業出版社華章分社的支持和分享。
點擊「閱讀原文」便可購買