Hyperledger 源碼分析之 Fabric
Deliver,意味著用戶端通過 gRPC 介面從 Ordering 服務擷取資料(例如指定區塊的資料)。
Orderer 節點收到請求訊息,會首先交給 orderer.common.server 包中 server 結構體的 Deliver(srv ab.AtomicBroadcast_DeliverServer) error 方法處理。該方法進一步調用 orderer.common.deliver 包中 deliverServer 結構的 Handle(srv ab.AtomicBroadcast_DeliverServer) error 方法進行處理。
deliverServer 結構體十分重要,完成對 Deliver 請求的處理過程。
type deliverServer struct { sm SupportManager}func (ds *deliverServer) Handle(srv ab.AtomicBroadcast_DeliverServer) error
整體過程
整體處理過程如下圖所示。
Handle(srv ab.AtomicBroadcast_DeliverServer) error 方法會開啟一個迴圈來從 srv 中不斷讀取請求訊息並進行處理,直到結束。
核心代碼如下所示,包括提取訊息和對訊息進行處理兩個步驟。
for { envelope, err := srv.Recv() // 從請求中提取一個 Envelope 訊息 ds.deliverBlocks(srv, envelope) // 對訊息進行處理並回覆,核心過程}
可見,對單個請求的處理都在 deliverBlocks(srv ab.AtomicBroadcast_DeliverServer, envelope *cb.Envelope) 方法中。該方法的處理過程包括解析訊息、檢查合法性、發送區塊以及返迴響應四個步驟。
下面具體對其進行具體分析。 解析訊息
首先,從請求的 Envelope 結構中提取載荷(Payload),進一步從載荷中提取通道頭部資訊。利用通道頭部資訊擷取對應的本地鏈結構,並擷取當前最新的配置序號。
// 提取載荷payload, err := utils.UnmarshalPayload(envelope.Payload)// 提取通道頭chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)// 擷取鏈結構,映射到 orderer.common.multichannel 包中 Registrar 結構體中對應方法chain, ok := ds.sm.GetChain(chdr.ChannelId)// 擷取當前配置序號lastConfigSequence := chain.Sequence()
檢查合法性
包括對許可權和 seekInfo 資料進行檢查。
首先,檢查請求方是否對通道擁有讀許可權。
sf := msgprocessor.NewSigFilter(policies.ChannelReaders, chain.PolicyManager())if err := sf.Apply(envelope); err != nil { logger.Warningf("[channel: %s] Received unauthorized deliver request from %s: %s", chdr.ChannelId, addr, err) return sendStatusReply(srv, cb.Status_FORBIDDEN)}
接下來,從 Envelope 結構的 payload.data 域中解析出 seekInfo 結構,並檢查其合法性。
proto.Unmarshal(payload.Data, seekInfo)chain.Reader().Iterator(seekInfo.Start)// 檢查 seekInfo 的cursor, number := chain.Reader().Iterator(seekInfo.Start)switch stop := seekInfo.Stop.Type.(type) {case *ab.SeekPosition_Oldest: // 截止到最早的區塊 stopNum = numbercase *ab.SeekPosition_Newest: // 截止到最新的區塊 stopNum = chain.Reader().Height() - 1case *ab.SeekPosition_Specified: // 截止到特定的區塊 stopNum = stop.Specified.Number if stopNum < number { logger.Warningf("[channel: %s] Received invalid seekInfo message from %s: start number %d greater than stop number %d", chdr.ChannelId, addr, number, stopNum) return sendStatusReply(srv, cb.Status_BAD_REQUEST) }}
發送區塊
在指定的起始和截止範圍內,逐個從本地賬本讀取區塊,並發送對應的區塊資料,
核心代碼如下所示。
for { block, status := cursor.Next() // 擷取區塊 sendBlockReply(srv, block) // 發送區塊 if stopNum == block.Header.Number { break }}
返迴響應
如果處理成功,則返回成功響應訊息。
sendStatusReply(srv, cb.Status_SUCCESS)
===========================
《區塊鏈原理、設計與應用》一書已經正式出版,以超級賬本項目為例,介紹了區塊鏈和分散式總帳技術的底層原理、設計架構、應用實踐的大量細節,歡迎大家閱讀指正。
===== 關於 TechFirst 公眾號 =====
專註金融科技、人工智慧、雲端運算、大資料相關領域的熱門技術與前瞻方向。
發送關鍵詞(如區塊鏈、雲端運算、大資料、容器),擷取熱門點評與技術乾貨。
歡迎投稿。
如果你喜歡公眾號內容,歡迎鼓勵一杯 coffee~