區塊鏈導航-超級賬本

區塊鏈導航系列-超級賬本

磨鏈(mochain)區塊鏈技術社區整理(投河自盡的魚、南瓜地、清源、John)區塊鏈導航也是一個入門區塊鏈的入門學習路線。下圖是一個區塊鏈相關內容的整理分享,內容還是比較繁瑣,當然在學習區塊鏈的過程中並不需要那麼複雜。鑽研琢磨某些方面可能對這個學習過程更為深刻。

所有內容不需要科學上網,當然通過科學上網能獲取更多信息,這裡暫時只收錄了不通過科學上網能獲取的內容

磨鏈社區-區塊鏈導航目錄:

區塊鏈導航-超級賬本

超級賬本相關導航

超級賬本(hyperledger)是Linux基金會於2015年發起的推進區塊鏈數字技術和交易驗證的開源項目,加入成員包括:荷蘭銀行(ABN AMRO)、埃森哲(Accenture)等十幾個不同利益體,目標是讓成員共同合作,共建開放平臺,滿足來自多個不同行業各種用戶案例,並簡化業務流程。由於點對點網絡的特性,分佈式賬本技術是完全共享、透明和去中心化的,故非常適合於在金融行業的應用,以及其他的例如製造、銀行、保險、物聯網等無數個其他行業。通過創建分佈式賬本的公開標準,實現虛擬和數字形式的價值交換,例如資產合約、能源交易、結婚證書、能夠安全和高效低成本的進行追蹤和交易。

超級賬本導航如圖:

區塊鏈導航-超級賬本

相關官網及網絡資料

Hyperledger Fabric 環境搭建及使⽤ (1)搭建開發環境 (2)搭建⾃己第一個網絡 (3)編寫⾃己第一個應⽤Hyperledger Fabric 架構與設計 (1)整體架構的概覽 (2)Hyperledger Fabric 核⼼組件 (3)Hyperledger Fabric 權限管理 (4)Hyperledger Fabric gRPC消息協議 (5)Hyperledger Fabric 排序服務Hyperledger Fabric 鏈碼開發 (1)鏈碼的簡介、結構 (2)鏈碼開發 API (3)鏈碼開發案例例解析Hyperledger Fabric SDK的使⽤ (1)Fabric-sdk-node 介紹及api (2)Fabric-sdk-node 調用鏈碼 (3)Fabric-sdk-node 開發區塊鏈應用 (4)Fabric-sdk-java 介紹及調用鏈碼Hyperledger Fabric 部署 (1)創建Fabric多節點集群 (2)啟動Fabric多節點集群 (3)Fabric多節點集群⽣生產部署及啟動相關基礎內容(來自趣鏈科技清源知乎)

清源知乎

Fabric學習筆記-架構初探

本文介紹了Fabric的架構,以及通過一個簡單的Demo來熟悉整個交易流程。

Hyperledger fabric V1.0的架構

如下圖所示:

區塊鏈導航-超級賬本

application提供各種語言的SDK接口。

peer負責模擬交易和記賬

Endorser(背書)用來當peer執行一個交易以後返回yes/no。

Committer將驗證過的區塊追加到通道上各個賬本的副本。

Ledger就是賬本啦。

Chaincode用來編寫業務邏輯,交易指令用來修改資產,可以理解為 fabric 網絡對外提供的一個交互接口(智能合約)。

Event是fabric提供的一個事件框架,比如鏈代碼事件,拒絕事件,註冊事件等,在編寫代碼的時候可以訂閱這些事件來實現自己的業務邏輯。

o-service用來實現共識。

交易流程

交易過程如下圖所示:

區塊鏈導航-超級賬本

Application向一個或多個peer節點發送對交易的背書請求。

Peer的endorse執行背書,但並不將結果提交到本地賬本,只是將結果返回給應用。

應用收集所有背書節點的結果後,將結果廣播給orderers,orderers執行共識,生成block,通過消息通道批量的將block發佈給peer節點,更新lerdger。

可以看一下下面這張圖,一樣的過程,更加突出了多個peer背書的過程。

區塊鏈導航-超級賬本

在介紹一下在交易過程中扮演重要角色的channel

channel是構建在Fabric網絡上的私有區塊鏈,實現了數據的隔離和保密。channel是由特定的peer所共享的,並且交易方必須通過該通道的正確驗證才能與賬本進行交互。

如下圖所示:

區塊鏈導航-超級賬本

可以看到不同顏色的channel隔離了不同的peer之間的通信。

構建Fabric網絡

Fabric提供了一個first-network的demo來學習整個流程。

first-network有兩個組織,每個組織各有兩個個peer節點,以及一個排序服務。兩個peer節點無法達成共識,三個peer節點無法容錯,四個peer節點剛好完成演示。

Demo啟動以後會自動完成一筆轉賬,並且打印出整個操作過程。

啟動網絡以後可以看到非常多的日誌,這裡我們只追蹤關鍵步驟。

################################################################### Generating Orderer Genesis block ########################################################################2018-02-05 15:13:08.760 CST [common/configtx/tool] main -> INFO 001 Loading configuration2018-02-05 15:13:08.816 CST [common/configtx/tool] doOutputBlock -> INFO 002 Generating genesis block2018-02-05 15:13:08.819 CST [common/configtx/tool] doOutputBlock -> INFO 003 Writing genesis block#################################################################### Generating channel configuration transaction 'channel.tx' ####################################################################2018-02-05 15:13:08.845 CST [common/configtx/tool] main -> INFO 001 Loading configuration2018-02-05 15:13:08.849 CST [common/configtx/tool] doOutputChannelCreateTx -> INFO 002 Generating new channel configtx2018-02-05 15:13:08.850 CST [common/configtx/tool] doOutputChannelCreateTx -> INFO 003 Writing new channel tx######################################################################## Generating anchor peer update for Org1MSP ###########################################################################2018-02-05 15:13:08.876 CST [common/configtx/tool] main -> INFO 001 Loading configuration2018-02-05 15:13:08.880 CST [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 002 Generating anchor peer update2018-02-05 15:13:08.881 CST [common/configtx/tool] doOutputAnchorPeersUpdate -> INFO 003 Writing anchor peer update可以看到,在轉賬之前首先進行了fabric網絡的初始化過程,創建了創世區塊,配置了channel和生成了membership(MSP)身份服務。

Channel name : mychannelCreating channel...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.crtCORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.keyCORE_PEER_LOCALMSPID=Org1MSPCORE_VM_ENDPOINT=unix:///host/var/run/docker.sockCORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crtCORE_PEER_TLS_ENABLED=trueCORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/[email protected]/mspCORE_PEER_ID=cliCORE_LOGGING_LEVEL=DEBUGCORE_PEER_ADDRESS=peer0.org1.example.com:70512018-02-05 07:13:13.382 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP2018-02-05 07:13:13.382 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity2018-02-05 07:13:13.406 UTC [channelCmd] InitCmdFactory -> INFO 003 Endorser and orderer connections initialized2018-02-05 07:13:13.409 UTC [msp] GetLocalMSP -> DEBU 004 Returning existing local MSPdd接著可以看到在轉賬前首先創建了channel,其中包括了身份認證的ca文件,key以及通信的socket信息和MSP的信息。

Attempting to Query PEER0 ...3 secs2018-02-05 07:13:55.998 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP2018-02-05 07:13:55.998 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity2018-02-05 07:13:55.998 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc2018-02-05 07:13:55.998 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc2018-02-05 07:13:55.999 UTC [msp/identity] Sign -> DEBU 005 Sign: plaintext: 0A95070A6708031A0C08B388E0D30510...6D7963631A0A0A0571756572790A01612018-02-05 07:13:55.999 UTC [msp/identity] Sign -> DEBU 006 Sign: digest: D6BB27BAC40E5A58ED3CF0AFB147280B60E305053D9B97A38461A398736ED7C7Query Result: 100接著在很不起眼的地方看到了上面這些日誌,差點看瞎我~重點來了~

在轉賬之前使用默認的ESCC(背書鏈碼),VSCC(驗證鏈碼),以及自身的簽名查詢了Peer0得到餘額是100。

2018-02-05 07:14:14.950 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> DEBU 009 ESCC invoke result: version:1 response:<200 message:=""> payload:"\n \370\316\5...,k\363_(\n\212\027" >2018-02-05 07:14:14.952 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 00a Chaincode invoke successful. result: status:2002018-02-05 07:14:14.953 UTC [main] main -> INFO 00b Exiting.....===================== Invoke transaction on PEER0 on channel 'mychannel' is successful =====================調用鏈碼進行轉賬操作,發現兩邊的狀態碼都是200表示轉賬成功,其中payload是發送轉賬的一些具體信息。

Attempting to Query PEER3 ...3 secs2018-02-05 07:14:18.566 UTC [msp] GetLocalMSP -> DEBU 001 Returning existing local MSP2018-02-05 07:14:18.566 UTC [msp] GetDefaultSigningIdentity -> DEBU 002 Obtaining default signing identity2018-02-05 07:14:18.566 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 003 Using default escc2018-02-05 07:14:18.566 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 004 Using default vscc2018-02-05 07:14:18.567 UTC [msp/identity] Sign -> DEBU 005 Sign: plaintext: 0A95070A6708031A0C08CA88E0D30510...6D7963631A0A0A0571756572790A01612018-02-05 07:14:18.567 UTC [msp/identity] Sign -> DEBU 006 Sign: digest: 9E93A6D0549C627788C5074633DE28C9D08F36A4763EFF55479613B2DD32CADBQuery Result: 902018-02-05 07:14:37.014 UTC [main] main -> INFO 007 Exiting.....最後查詢Peer3發現餘額只有90了,這樣就完成了一個最小區塊鏈網絡的交易示例。

Fabric學習筆記-智能合約

本文介紹了什麼是智能合約,以及如何在Fabric下編寫一個簡單的智能合約Demo。

什麼是智能合約?

智能合約是一種旨在以信息化方式傳播、驗證或執行合同的計算機協議。智能合約允許在沒有第三方的情況下進行可信交易。這些交易可追蹤且不可逆轉,Fabric為智能合約提供了一個可信的執行環境。

Fabric下的智能合約

Chaincode是Fabric下智能合約的代碼實現,是一段在區塊鏈上驗證,存儲和執行的代碼,只有通過Chaincode才能與Fabric網絡進行交互。

在Fabric下又分為系統Chaincode和普通Chaincode。

系統Chaincode運行於peer節點內而非一個隔離的容器中,沒有生命週期的概念,用於實現一些系統行為。普通Chaincode運行在一個單獨的容器中,對外提供操作Fabric網絡的接口,有打包、安裝、實例化和升級四個生命週期並且有相應的函數提供調用,操作更加的靈活也是我們通常所指Chaincode。

Chaincode的運行過程Channel負責連接不同的Peer,同步連接到的Peer對Chaincode的執行結果,相當於將一些節點劃分為一個子集同時同步Chaincode的執行結果。Endorser負責執行Chaincode。Orderer負責對Chaincode的執行結果進行共識,目前支持solo/kafka/sBFT三種共識方式。Committer負責將Chaincode經過共識後的結果寫入Ledger。編寫ChaincodeChaincode必須實現兩個接口。

type Chaincode interface { // 初始化⼯作,⼀般情況下僅被調⽤⼀次,升級時也會調用該函數 Init(stub ChaincodeStubInterface) pb.Response // 查詢或更新world state,可多次被調⽤ Invoke(stub ChaincodeStubInterface) pb.Response}下面是一個最小智能合約所需要的結構。

package main import ( "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" )type SimpleAsset struct {}func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response {}func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response {}func main() { if err := shim.Start(new(SimpleAsset)); err != nil { fmt.Printf("Error starting SimpleAsset chaincode: %s", err) }}具體的業務邏輯都在Invoke這個函數里面進行編寫。

首先,我們完善一下Init函數

func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { args := stub.GetStringArgs() if len(args) != 2 { return shim.Error("Incorrect arguments. Expecting a key and a value") } // 實例化的時候初始化一下狀態 err := stub.PutState(args[0], []byte(args[1])) if err != nil { return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0])) } return shim.Success(nil)}接著在Invoke裡編寫業務邏輯

func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { fn, args := stub.GetFunctionAndParameters() var result string var err error if fn == "set" { result, err = set(stub, args) } else { result, err = get(stub, args) } if err != nil { return shim.Error(err.Error()) } return shim.Success([]byte(result))}Invoke根據不同的參數決定調用查詢或者是設置狀態。

編寫set和get函數

func set(stub shim.ChaincodeStubInterface, args []string) (string, error) { if len(args) != 2 { return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value") } err := stub.PutState(args[0], []byte(args[1])) if err != nil { return "", fmt.Errorf("Failed to set asset: %s", args[0]) } return args[1], nil}func get(stub shim.ChaincodeStubInterface, args []string) (string, error) { if len(args) != 1 { return "", fmt.Errorf("Incorrect arguments. Expecting a key") } value, err := stub.GetState(args[0]) if err != nil { return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err) } if value == nil { return "", fmt.Errorf("Asset not found: %s", args[0]) } return string(value), nil}github上有完整代碼,代碼非常簡單就是根據命令查詢或者設置世界狀態。

執行效果如下圖:

區塊鏈導航-超級賬本

這樣就在Fabric上實現了一個簡單的智能合約。

Fabric學習筆記-PBFT算法

本文介紹了實用拜占庭容錯算法(PBFT)。

Fabric在v0.6中採用的是PBFT算法,在v1.0.0-preview中是SBFT算法,在v1.0.0-release中文檔上說PBFT還在開發中,項目中目前還沒有實現(⊙o⊙)…但是萬變不離其宗都是對BFT算法的一些優化改進。

解決的問題

拜占庭算法主要解決了缺少可信的中央節點和可信任的通道的情況下,分佈在網絡中的各個節點如何達成共識的問題,實用拜占庭算法是拜占庭算法的改進,主要改進了拜占庭算法效率不高的問題,將算法複雜度由指數級降低到多項式級,使得拜占庭容錯算法在實際系統應用中變得可行。

基本概念

客戶端(client)負責發送請求。副本(replica)所有參與提供服務的節點。備份節點(backup)主節點外的所有節點。主節點(primary)從副本中選出提供主要服務的節點。視圖(view)主節點和副本之間編號的一次快照。PBFT是一種狀態機副本複製算法,即服務作為確定有限狀態機進行建模,狀態機在分佈式系統的不同節點進行副本複製。每個狀態機的副本都保存了服務的狀態,同時也實現了服務的操作。

在確定有限自動機中,每個狀態對每個可能輸入只有精確的一個轉移,在輸入順序一致,起始狀態一致的情況下輸出也必然一致。這也就對PBFT算法中的副本提出了兩個限定 :

所有節點必須是確定性的。也就是說,在給定狀態和參數相同的情況下,操作執行的結果必須相同。所有節點必須從相同的狀態開始執行。在這兩個限定條件下,即使失效的副本節點存在,PBFT算法對所有非失效副本節點的請求執行總順序達成一致,從而保證安全性。

將所有的副本組成的集合使用大寫字母R表示,使用0到|R|-1的整數表示每一個副本。為了描述方便,假設|R|=3f+1,這裡f是有可能失效的副本的最大個數。儘管可以存在多於3f+1個副本,但是額外的副本除了降低性能之外不能提高可靠性。

算法流程

請求階段:客戶端向主節點發送請求。預準備階段:主節點分配一個序列號n給收到的請求,然後向所有備份節點廣播預準備消息,預準備消息的格式為,這裡v是視圖編號,m是客戶端發送的請求消息,d是請求消息m的摘要。準備階段:備份節點i接受了預準備消息,則進入準備階段。在準備階段的同時,該節點向所有副本節點發送準備消息,並且將預準備消息和準備消息寫入自己的消息日誌。確認階段:副本節點收到2f個從不同副本節點發來一致的預準備消息,一共2f+1個一致的預準備消息確認了消息的正確性,然後按照序號n依次執行請求。預準備階段和準備階段確保所有正常節點對同一個視圖中的請求序號達成一致。

準備階段和確認階段確保了節點執行了大多數都認可的操作。

PBFT算法流程:

區塊鏈導航-超級賬本

PBFT算法流程

主要介紹了PBFT算法的主要流程,其中還有一些問題例如:主節點是如何選擇,主節點失效,怎麼確定消息的正確性等都不在此贅述,可以參考:https://www.jianshu.com/p/fb5edf031afd 區塊鏈核心技術:拜占庭共識算法之PBFT。

Fabric源碼分析-共識模塊

正好這些天要有一個需求要幫客戶魔改Fabric-v0.6,把一些hyperchain的高級特性移植過去,藉此機會把之前看過的源碼在梳理一下。

下面就是對Fabric共識模塊的源碼分析和梳理,代碼都是以Fabric-v0.6-preview為例,在1.0及後續版本中都移除了PBFT部分,用了更好的SBFT,目前這一部分還在開發中。

目錄結構

可以看到共識模塊目錄如下。

consensus├── controller├── executor├── helper│ └── persist├── noops├── pbft└── util └── events目錄含義如下controller 用來控制Fabric選擇什麼樣的共識算法,默認是noops。executor 封裝了消息隊列中對交易的處理。helper 對外提供接口調用和數據持久化接口。noops 提供瞭如何編寫Fabric共識算法的Demo。pbft PBFT算法的具體實現。util 實現了一個peer節點到共識算法的一個消息通道,和一個消息隊列。流程概覽Fabric網絡通過一個EventLoop和共識算法進行交互,所有的操作都通過對事件循環中的事件監聽進行推進。

整體流程如下圖所示。

區塊鏈導航-超級賬本

Consensus模塊接口fabric/consensus/consensus.go對外提供共識模塊的方法調用。

其中最核心也是每個算法必須實現的接口是Consenter。

type ExecutionConsumer interface { Executed(tag interface{}) Committed(tag interface{}, target *pb.BlockchainInfo) RolledBack(tag interface{}) StateUpdated(tag interface{}, target *pb.BlockchainInfo)}type Consenter interface { RecvMsg(msg *pb.Message, senderHandle *pb.PeerID) error ExecutionConsumer}接口的具體實現在fabric/consensus/pbft/external.go。

因為對交易的操作都是異步的,所以必須手動實現Executed,Committed,RolledBack,StateUpdated方法來監聽對應動作的完成。

RecvMsg方法用來從不用的peer節點接收消息。

初始化共識模塊共識算法引擎在peer啟動的時候初始化,初始化的具體函數如下所示。

// consensus/helper/engine.gofunc GetEngine(coord peer.MessageHandlerCoordinator) (peer.Engine, error) { var err error engineOnce.Do(func() { engine = new(EngineImpl) engine.helper = NewHelper(coord) engine.consenter = controller.NewConsenter(engine.helper) engine.helper.setConsenter(engine.consenter) engine.peerEndpoint, err = coord.GetPeerEndpoint() engine.consensusFan = util.NewMessageFan() go func() { logger.Debug("Starting up message thread for consenter") for msg := range engine.consensusFan.GetOutChannel() { engine.consenter.RecvMsg(msg.Msg, msg.Sender) } }() }) return engine, err}GetEngine的作用是進行共識模塊的初始化,同時啟動一個goroutine等待消息進入。

具體的engine.consenter是在consensus/controller/controller.go裡選擇。

// consensus/controller/controller.gofunc NewConsenter(stack consensus.Stack) consensus.Consenter { plugin := strings.ToLower(viper.GetString("peer.validator.consensus.plugin")) if plugin == "pbft" { logger.Infof("Creating consensus plugin %s", plugin) return pbft.GetPlugin(stack) } logger.Info("Creating default consensus plugin (noops)") return noops.GetNoops(stack)}默認選擇的是noops,如果需要添加自己編寫的共識模塊需要在這裡自行添加判斷。

noops 只是演示如何編寫Fabric共識模塊,不要用在生產環境。如果選擇了PBFT則會調用consensus/pbft/pbft.go進行初始化。

使用PBFT的batch模式啟動時會調用newObcBatch進行PBFT算法初始化。

PBFT只有batch一種模式。// consensus/pbft/batch.gofunc newObcBatch(id uint64, config *viper.Viper, stack consensus.Stack) *obcBatch { var err error ... op.manager = events.NewManagerImpl() op.manager.SetReceiver(op) etf := events.NewTimerFactoryImpl(op.manager) op.pbft = newPbftCore(id, config, op, etf) op.manager.Start() blockchainInfoBlob := stack.GetBlockchainInfoBlob() op.externalEventReceiver.manager = op.manager ... return op}newObcBatch主要做了這幾項工作

初始化了eventLoop的消息隊列。設置了消息的接收者,用來處理對應的消息。創建監聽消息超時的定時器。初始化pbft算法。啟動消息隊列,不斷監聽事件的到來並且分發給接收者處理。消息處理Fabric的共識消息是通過eventLoop注射給對應處理函數的。

// consensus/util/events/events.gofunc SendEvent(receiver Receiver, event Event) { next := event for { next = receiver.ProcessEvent(next) if next == nil { break } }}func (em *managerImpl) Inject(event Event) { if em.receiver != nil { SendEvent(em.receiver, event) }}func (em *managerImpl) eventLoop() { for { select { case next :=

SendEvent函數實現非常有意思,如果receiver.ProcessEvent的返回不為nil則不斷的調用receiver.ProcessEvent直到找到對應的消息處理函數,在ProcessEvent函數中,其餘case均為事件處理函數,唯獨pbftMessage依賴SendEvent發送消息給其餘函數處理。

// consensus/pbft/pbft-core.gofunc (instance *pbftCore) ProcessEvent(e events.Event) events.Event { ... case *pbftMessage: return pbftMessageEvent(*et) case pbftMessageEvent: msg := et logger.Debugf("Replica %d received incoming message from %v", instance.id, msg.sender) next, err := instance.recvMsg(msg.msg, msg.sender) if err != nil { break } return next case *RequestBatch: err = instance.recvRequestBatch(et) case *PrePrepare: err = instance.recvPrePrepare(et) ...}可以看到*pbftMessage和pbftMessageEvent這兩個case通過recvMsg的返回值又把消息分發給其餘case,非常巧妙。

PBFT算法的不同階段都會按著上面的流程映射到不同的處理函數往前推進,本質上是一個狀態機。

至此Fabric的Consensus模塊主要流程已經梳理清楚,熟悉了這個流程以後再結合PBFT算法的過程就可以很容易在此基礎上添加新的功能了。


分享到:


相關文章: