fabric的點對點(peer-to-peer)通訊是建立在允許雙向的基於流的訊息gRPC上的。它使用Protocol Buffers來序列化peer之間傳輸的資料結構。Protocol buffers 是語言無關,平台無關並具有可擴充機制來序列化結構化的資料的技術。資料結構,訊息和服務是使用 proto3 language注釋來描述的。 3.1 訊息
訊息在節點之間通過Messageproto 結構封裝來傳遞的,可以分為 4 種類型:發現(Discovery), 交易(Transaction), 同步(Synchronization)和共識(Consensus)。每種類型在payload中定義了多種子類型。
message Message { enum Type { UNDEFINED = 0; DISC_HELLO = 1; DISC_DISCONNECT = 2; DISC_GET_PEERS = 3; DISC_PEERS = 4; DISC_NEWMSG = 5; CHAIN_STATUS = 6; CHAIN_TRANSACTION = 7; CHAIN_GET_TRANSACTIONS = 8; CHAIN_QUERY = 9; SYNC_GET_BLOCKS = 11; SYNC_BLOCKS = 12; SYNC_BLOCK_ADDED = 13; SYNC_STATE_GET_SNAPSHOT = 14; SYNC_STATE_SNAPSHOT = 15; SYNC_STATE_GET_DELTAS = 16; SYNC_STATE_DELTAS = 17; RESPONSE = 20; CONSENSUS = 21; } Type type = 1; bytes payload = 2; google.protobuf.Timestamp timestamp = 3;}
payload是由不同的訊息類型所包含的不同的像Transaction或Response這樣的對象的不透明的位元組數組。例如:type為CHAIN_TRANSACTION那麼payload就是一個Transaction對象。 3.1.1 發現訊息
在啟動時,如果CORE_PEER_DISCOVERY_ROOTNODE被指定,那麼 peer 就會運行發現協議。CORE_PEER_DISCOVERY_ROOTNODE是網路(任意peer)中扮演用來發現所有 peer 的起點角色的另一個 peer 的 IP 位址。協議序列以payload是一個包含:
message HelloMessage { PeerEndpoint peerEndpoint = 1; uint64 blockNumber = 2;}message PeerEndpoint { PeerID ID = 1; string address = 2; enum Type { UNDEFINED = 0; VALIDATOR = 1; NON_VALIDATOR = 2; } Type type = 3; bytes pkiID = 4;}message PeerID { string name = 1;}
這樣的端點的HelloMessage對象的DISC_HELLO訊息開始的。
域的定義: PeerID 是在啟動時或設定檔中定義的 peer 的任意名字 PeerEndpoint 描述了端點和它是驗證還是非驗證 peer pkiID 是 peer 的加密ID address 以ip:port這樣的格式表示的 peer 的主機名稱或IP和連接埠 blockNumber 是 peer 的區塊鏈的當前的高度
如果收到的DISC_HELLO 訊息的塊的高度比當前 peer 的塊的高度高,那麼它馬上初始化同步協議來追上當前的網路。
DISC_HELLO之後,peer 會周期性的發送DISC_GET_PEERS來發現任意想要加入網路的 peer。收到DISC_GET_PEERS後,peer 會發送payload 包含PeerEndpoint的數組的DISC_PEERS作為響應。這是不會使用其它的發現訊息類型。 3.1.2 交易訊息
有三種不同的交易類型:部署(Deploy),調用(Invoke)和查詢(Query)。部署交易向鏈上安裝指定的鏈碼,調用和查詢交易會調用部署號的鏈碼。另一種需要考慮的類型是建立(Create)交易,其中部署好的鏈碼是可以在鏈上執行個體化並定址的。這種類型在寫這份文檔時還沒有被實現。 3.1.2.1 交易的資料結構
CHAIN_TRANSACTION和CHAIN_QUERY類型的訊息會在payload帶有Transaction對象:
message Transaction { enum Type { UNDEFINED = 0; CHAINCODE_DEPLOY = 1; CHAINCODE_INVOKE = 2; CHAINCODE_QUERY = 3; CHAINCODE_TERMINATE = 4; } Type type = 1; string uuid = 5; bytes chaincodeID = 2; bytes payloadHash = 3; ConfidentialityLevel confidentialityLevel = 7; bytes nonce = 8; bytes cert = 9; bytes signature = 10; bytes metadata = 4; google.protobuf.Timestamp timestamp = 6;}message TransactionPayload { bytes payload = 1;}enum ConfidentialityLevel { PUBLIC = 0; CONFIDENTIAL = 1;}
域的定義: type - 交易的類型, 為1時表示: UNDEFINED - 為未來的使用所保留. CHAINCODE_DEPLOY - 代表部署新的鏈碼. CHAINCODE_INVOKE - 代表一個鏈碼函數被執行並修改了世界狀態 CHAINCODE_QUERY - 代表一個鏈碼函數被執行並可能唯讀取了世界狀態 CHAINCODE_TERMINATE - 標記的鏈碼不可用,所以鏈碼中的函數將不能被調用 chaincodeID - 鏈碼源碼,路徑,建構函式和參數雜湊所得到的ID payloadHash - TransactionPayload.payload所定義的雜湊位元組. metadata - 應用可能使用的,由自己定義的任意交易相關的中繼資料 uuid - 交易的唯一ID timestamp - peer 收到交易時的時間戳記 confidentialityLevel - 資料保密的層級。當前有兩個層級。未來可能會有多個層級。 nonce - 為安全而使用 cert - 交易者的認證 signature - 交易者的簽名 TransactionPayload.payload - 交易的payload所定義的位元組。由於payload可以很大,所以交易訊息只包含payload的雜湊
交易安全的詳細資料可以在第四節找到 3.1.2.2 交易規範
一個交易通常會關聯鏈碼定義及其執行環境(像語言和安全上下文)的鏈碼規範。現在,有一個使用Go語言來編寫鏈碼的實現。將來可能會添加新的語言。
message ChaincodeSpec { enum Type { UNDEFINED = 0; GOLANG = 1; NODE = 2; } Type type = 1; ChaincodeID chaincodeID = 2; ChaincodeInput ctorMsg = 3; int32 timeout = 4; string secureContext = 5; ConfidentialityLevel confidentialityLevel = 6; bytes metadata = 7;}message ChaincodeID { string path = 1; string name = 2;}message ChaincodeInput { string function = 1; repeated string args = 2;}
域的定義: chaincodeID - 鏈碼源碼的路徑和名字 ctorMsg - 調用的函數名及參數 timeout - 執行證券交易所需的時間(以毫秒錶示) confidentialityLevel - 這個交易的保密層級 secureContext - 交易者的安全上下文 metadata - 應用想要傳遞下去的任何資料
當 peer 收到chaincodeSpec後以合適的交易訊息封裝它並廣播到網路 3.1.2.3 部署交易
部署交易的類型是CHAINCODE_DEPLOY,且它的payload包含ChaincodeDeploymentSpec對象。
message ChaincodeDeploymentSpec { ChaincodeSpec chaincodeSpec = 1; google.protobuf.Timestamp effectiveDate = 2; bytes codePackage = 3;}
域的定義: chaincodeSpec - 參看上面的3.1.2.2節. effectiveDate - 鏈碼準備好可被調用的時間 codePackage - 鏈碼源碼的gzip
當驗證 peer 部署鏈碼時,它通常會校正codePackage的雜湊來保證交易被部署到網路後沒有被篡改。 3.1.2.4 調用交易
調用交易的類型是CHAINCODE_DEPLOY,且它的payload包含ChaincodeInvocationSpec對象。
message ChaincodeInvocationSpec { ChaincodeSpec chaincodeSpec = 1;}
3.1.2.5 查詢交易
查詢交易除了訊息類型是CHAINCODE_QUERY其它和調用交易一樣 3.1.3 同步訊息
同步協議以3.1.1節描述的,當 peer 知道它自己的區塊落後於其它 peer 或和它們不一樣後所發起的。peer 廣播SYNC_GET_BLOCKS,SYNC_STATE_GET_SNAPSHOT或SYNC_STATE_GET_DELTAS並分別接收SYNC_BLOCKS, SYNC_STATE_SNAPSHOT或SYNC_STATE_DELTAS。
安裝的共識外掛程式(如:pbft)決定同步協議是如何被應用的。每個小時是針對具體的狀態來設計的:
SYNC_GET_BLOCKS 是一個SyncBlockRange對象,包含一個連續區塊的範圍的payload的請求。
message SyncBlockRange { uint64 start = 1; uint64 end = 2; uint64 end = 3;}
接收peer使用包含 SyncBlocks對象的payload的SYNC_BLOCKS資訊來響應
message SyncBlocks { SyncBlockRange range = 1; repeated Block blocks = 2;}
start和end標識包含的區塊的開始和結束,返回區塊的順序由start和end的值定義。如:當start=3,end=5時區塊的順序將會是3,4,5。當start=5,end=3時區塊的順序將會是5,4,3。
SYNC_STATE_GET_SNAPSHOT 請求當前世界狀態的快照。 payload是一個SyncStateSnapshotRequest對象
message SyncStateSnapshotRequest { uint64 correlationId = 1;}
correlationId是請求 peer 用來追蹤響應訊息的。接受 peer 回複payload為SyncStateSnapshot執行個體的SYNC_STATE_SNAPSHOT資訊
message SyncStateSnapshot { bytes delta = 1; uint64 sequence = 2; uint64 blockNumber = 3; SyncStateSnapshotRequest request = 4;}
這條訊息包含快照或以0開始的快照流序列中的一塊。終止訊息是len(delta) == 0的塊
SYNC_STATE_GET_DELTAS 請求連續區塊的狀態變化。預設情況下總賬維護500筆交易變化。 delta(j)是block(i)和block(j)之間的狀態轉變,其中i=j-1。 payload包含SyncStateDeltasRequest執行個體