Hyperledger Fabric 区块结构解析

Hyperledger Fabric 区块结构解析

前言

​ 最近在搞区块链浏览器,也就是通过网页来查看Fabric区块链的各项信息,主要包含区块、交易、链码、节点信息等等。这些信息主要来源于从Fabric中获取的区块JSON数据。因为网上关于Fabric区块链的各项资料不是很多,故而自己整理了一份简略的资料。同时也希望这份资料能帮助到大家。

区块结构介绍

​ 区块链中的区块结构一般分为区块头和区块体两部分,但是Fabric区块的数据结构分为三大部分:Header(区块头)、Data(区块体,包含所有的交易信息)、MetaData(和当前区块相关的元数据)。区块数据结构如下:

type Block struct {
    Header *BlockHeader,
    Data *BlockData,
    MetaData *BlockMetaData
}

​ 以下三张图来源于网络以及其他博客,第一张是英文版的区块结构图,第二张是中文版的区块结构图,第三张是整个区块数据结构的分解图,仅供大家参考。

英文版:
英文版区块结构图

中文版:
中文版区块结构图

区块数据结构:
区块数据结构图

下面提供一个区块json数据供大家分析。

{
	"header": {
		"number": "14",
		"previous_hash": "057935b395be9d6757f61a62eea2fd5c37e7089f3c991a7a9a131aefb255d450",
		"data_hash": "39ba8f0e54e75980414b301a343f42981ba63f8f105cd72d0e039010843aa920"
	},
	"data": {
		"data": [{
			"signature": {
				"type": "Buffer",
				"data": [48, 68, 2, 32, 53, 212, 86, 141, 134, 170, 144, 75, 132, 68, 229, 103, 122, 240, 21, 201, 139, 191, 77, 193, 50, 192, 31, 9, 15, 187, 65, 112, 239, 36, 205, 182, 2, 32, 98, 217, 249, 62, 93, 24, 158, 247, 180, 186, 122, 237, 141, 54, 228, 20, 218, 234, 24, 246, 118, 205, 134, 187, 250, 198, 255, 79, 129, 159, 164, 220]
			},
			"payload": {
				"header": {
					"channel_header": {
						"type": 3,
						"version": 1,
						"timestamp": "2021-01-10T12:01:29.673Z",
						"channel_id": "common",
						"tx_id": "ff28b4847400b16742245590d908b57a3643e4cc62baf3264dd8751070342314",
						"epoch": "0",
						"extension": {
							"type": "Buffer",
							"data": [18, 11, 18, 9, 99, 104, 97, 105, 110, 99, 111, 100, 101]
						},
						"typeString": "ENDORSER_TRANSACTION"
					},
					"signature_header": {
						"creator": {
							"Mspid": "org1",
							"IdBytes": "-----BEGIN CERTIFICATE-----\nMIICcTCCAhegAwIBAgIUbtNpC7qvKr1n5OxgOZiaBRu2VtgwCgYIKoZIzj0EAwIw\nczELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT\nE2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMjEwMTEwMTE1NjAwWhcNMjIwMTEwMTIw\nMTAwWjAvMRwwDQYDVQQLEwZjbGllbnQwCwYDVQQLEwRvcmcxMQ8wDQYDVQQDEwZh\nZG1pbjEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASlFdeOOyJRw4/25L62W/KA\nmYpCFsV0CwnezSVEuJL44vC0vqpYUlk1CdR1UO8bkcutgBHXsly+gWyH+GZtSD59X\no4HMMIHJMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSu\nKGe8YTCjfa0+f+l0Q6Woq+S94zArBgNVHSMEJDAigCA2OH3OJFJR5m75pJceBgqt\ni7zg18hHdCVIjZOnvoUxazBdBggqAwQFBgcIAQRReyJhdHRycyI6eyJoZi5BZmZp\nbGlhdGlvbiI6Im9yZzEiLCJoZi5FbnJvbGxtZW50SUQiOiJhZG1pbjEiLCJoZi5U\neXBlIjoiY2xpZW50In19MAoGCCqGSM49BAMCA0gAMEUCIQCiNYmu/NaH/pBStOZf\nfa2OcoTmBsJvztFikt/+CGjZ0gIgUJd1Ay3vi1V/WNMoAxr/3uA84qwAP0TRwcQw\nIjyEzZA=\n-----END CERTIFICATE-----\n"
						},
						"nonce": {
							"type": "Buffer",
							"data": [217, 120, 226, 190, 89, 228, 29, 80, 164, 122, 27, 114, 128, 137, 117, 209, 53, 235, 81, 90, 147, 12, 11, 218]
						}
					}
				},
				"data": {
					"actions": [{
						"header": {
							"creator": {
								"Mspid": "org1",
								"IdBytes": "-----BEGIN CERTIFICATE-----\nMIICcTCCAhegAwIBAgIUbtNpC7qvKr1n5OxgOZiaBRu2VtgwCgYIKoZIzj0EAwIw\nczELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT\nE2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMjEwMTEwMTE1NjAwWhcNMjIwMTEwMTIw\nMTAwWjAvMRwwDQYDVQQLEwZj1bGllbnQwCwYDVQQLEwRvcmcxMQ8wDQYDVQQDEwZh\nZG1pbjEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASlFdeOOyJRw4/25L62W/KA\nmYpCFsV0CwnezSVEuJL44vC0vqpYUlk1CdRUO8bkcutgBHXsly+gWyH+GZtSD59X\no4HMMIHJMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSu\nKGe8YTCjfa0+f+l0Q6Woq+S94zArBgNVHSMEJDAigCA2OH3OJFJR5m75pJceBgqt\ni7zg18hHdCVIjZOnvoUxazBdBggqAwQFBgcIAQRReyJhdHRycyI6eyJoZi5BZmZp\nbGlhdGlvbiI6Im9yZzEiLCJoZi5FbnJvbGxtZW50SUQiOiJhZG1pbjEiLCJoZi5U\neXBlIjoiY2xpZW50In19MAoGCCqGSM49BAMCA0gAMEUCIQCiNYmu/NaH/pBStOZf\nfa2OcoTmBsJvztFikt/+CGjZ0gIgUJd1Ay3vi1V/WNMoAxr/3uA84qwAP0TRwcQw\nIjyEzZA=\n-----END CERTIFICATE-----\n"
							},
							"nonce": {
								"type": "Buffer",
								"data": [217, 120, 226, 190, 89, 228, 29, 80, 164, 122, 27, 114, 128, 137, 117, 209, 53, 235, 81, 90, 147, 12, 11, 218]
							}
						},
						"payload": {
							"chaincode_proposal_payload": {
								"input": {
									"chaincode_spec": {
										"type": 1,
										"typeString": "GOLANG",
										"input": {
											"args": [{
												"type": "Buffer",
												"data": [112, 117, 116]
											}, {
												"type": "Buffer",
												"data": [123, 34, 117, 115, 101, 114, 110, 97, 109, 101, 34, 12, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 97, 99, 116, 105, 111, 110, 34, 58, 34, 115, 116, 111, 114, 101, 34, 44, 34, 109, 111, 110, 101, 121, 34, 58, 34, 50, 48, 48, 34, 125]
											}],
											"decorations": {}
										},
										"chaincode_id": {
											"path": "",
											"name": "chaincode",
											"version": ""
										},
										"timeout": 0
									}
								}
							},
							"action": {
								"proposal_response_payload": {
									"proposal_hash": "932fb85e4b503cfdf2efbd8b9f45df5240da040cacd4c7970659263633c3afc3",
									"extension": {
										"results": {
											"data_model": 0,
											"ns_rwset": [{
												"namespace": "chaincode",
												"rwset": {
													"reads": [],
													"range_queries_info": [],
													"writes": [{
														"key": "\u0000neil\u0000store\u0000200\u0000",
														"is_delete": false,
														"value": "{\"username\":\"neil\",\"action\":\"store\",\"money\":\"200\"}"
													}],
													"metadata_writes": []
												},
												"collection_hashed_rwset": []
											}, {
												"namespace": "lscc",
												"rwset": {
													"reads": [{
														"key": "chaincode",
														"version": {
															"block_num": "5",
															"tx_num": "0"
														}
													}],
													"range_queries_info": [],
													"writes": [],
													"metadata_writes": []
												},
												"collection_hashed_rwset": []
											}]
										},
										"events": {
											"chaincode_id": "",
											"tx_id": "",
											"event_name": "",
											"payload": {
												"type": "Buffer",
												"data": []
											}
										},
										"response": {
											"status": 200,
											"message": "",
											"payload": ""
										},
										"chaincode_id": {
											"path": "",
											"name": "chaincode",
											"version": "1.0"
										}
									}
								},
								"endorsements": [{
									"endorser": {
										"Mspid": "org1",
										"IdBytes": "-----BEGIN CERTIFICATE-----\nMIICGTCCAcCgAwIBAgIRAP5eKKLGhfTuzLVPIrPcbTwwCgYIKoZIzj0EAwIwczEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjEwMTEwMTA1NTAwWhcNMzEwMTA4MTA1NTAw\nWjBbMQswCQ1YDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN\nU2FuIEZyYW5jaXNjbzEfMB0GA1UEAxMWcGVlcjAub3JnMS5leGFtcGxlLmNvbTBZ\nMBMGByqGSM49AgEGCCqGSM49AwEHA0IABFo3HMBdd10LjYeZwMtR59Byjjp11pd8\nlvWaItRsbrwDvAugdGDZ3KC1FVHLhblCbFp4sDyRDPwJIDnXYIZUpFWjTTBLMA4G\nA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMCsGA1UdIwQkMCKAIDY4fc4kUlHm\nbvmklx4GCq2LvODXyEd0JUiNk6e+hTFrMAoGCCqGSM49BAMCA0cAMEQCIE0PYDKu\ncPYusfTbqem0AwtrqMx/2kUSP9X6/HA5en0lAiB/YePKYDu91h336nNcxal98vNB\nPKyoL+zVCRqL/MYvCQ==\n-----END CERTIFICATE-----\n"
									},
									"signature": {
										"type": "Buffer",
										"data": [48, 68, 2, 32, 14, 77, 226, 146, 105, 55, 164, 194, 11, 71, 51, 147, 63, 74, 207, 104, 106, 187, 117, 175, 187, 194, 244, 165, 25, 132, 52, 8, 190, 217, 81, 46, 2, 32, 22, 123, 212, 121, 242, 138, 121, 213, 55, 113, 46, 11, 23, 119, 148, 62, 172, 83, 199, 24, 133, 151, 60, 144, 177, 255, 65, 182, 177, 225, 0, 222]
									}
								}]
							}
						}
					}]
				}
			}
		}]
	},
	"metadata": {
		"metadata": [{
				"value": "\n\u0002\b\u0003",
				"signatures": [{
					"signature_header": {
						"creator": {
							"Mspid": "orderer.example.com",
							"IdBytes": "-----BEGIN CERTIFICATE-----\nMIICDTCCAbOgAwIBAgIRAKZzKwIm1fXv9TbfsLSlJpUwCgYIKoZIzj0EAwIwaTEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xFDASBgNVBAoTC2V4YW1wbGUuY29tMRcwFQYDVQQDEw5jYS5leGFt\ncGxlLmNvbTAeFw0yMTAxMTAxMDU1MDBaFw0zMTAxMDgxMDU1MDBaMFgxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNp\nc2NvMRwwGgYDVQQDExNvcmRlcmVyLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYI\nKoZIzj0DAQcDQgAEYh4Kui3YB02J9uUjX7+nn+l5ZT6DFWXjhB6X3fjUjowvySzf\nfqn4s95o6qc0jYCaiC47cfUlJKvlPsBQX8XzyKNNMEswDgYDVR0PAQH/BAQDAgeA\nMAwGA1UdEwEB/wQCMAAwKwYDVR0jBCQwIoAg1HCS4tEmcKJX2ttGPCY4mw2VSA5lP\nkVeYrnAulge4U0gwCgYIKoZIzj0EAwIDSAAwRQIhAJQom8NqJdKvG3uYkbcuFRbx\nUxVltC+/OdqLZ2ByCVWzAiAv0mGOCKPogI1QZdowWXsHtS9bZw3ymtwVToLXI+zP\nzg==\n-----END CERTIFICATE-----\n"
						},
						"nonce": {
							"type": "Buffer",
							"data": [32, 137, 232, 144, 240, 168, 86, 24, 236, 47, 151, 23, 182, 223, 129, 176, 92, 209, 74, 104, 78, 83, 86, 125]
						}
					},
					"signature": {
						"type": "Buffer",
						"data": [48, 69, 2, 33, 0, 159, 188, 112, 227, 84, 54, 225, 211, 227, 157, 120, 16, 218, 64, 137, 137, 94, 9, 217, 83, 249, 31, 24, 66, 75, 78, 14, 219, 182, 220, 75, 223, 2, 32, 106, 39, 131, 38, 77, 200, 222, 147, 195, 62, 173, 63, 254, 133, 174, 73, 161, 75, 40, 248, 173, 62, 83, 56, 141, 99, 162, 94, 181, 72, 68, 188]
					}
				}]
			}, {
				"value": {
					"index": "3"
				},
				"signatures": []
			},
			[0]
		]
	}
}

大家可以通过在线json解析工具对json数据进行层次解析后再查看,这样会更加方便。我下面的对具体数据结构以及字段的分析和讲解也会依赖于这个解析工具的部分截图。
整个区块结构如下图所示:

区块结构截图

Block Header部分

区块头包含三个字段,number(当前区块号)、previous_hash前一个区块头哈希)、data_hash当前区块的数据哈希)。

值得一提的是data_hash并非当前区块哈希,只是当前区块数据体的哈希值,大家在呈现区块数据时要注意这一点。

type BlockHeader struct {
	    Number   	    uint64
	    PreviousHash    []byte
	    DataHash        []byte
}

Block Data部分

区块体中只有一个data字段,data字段对应的属性值中也只有一个data字段。这个data字段对应是Envelope数据,即一种展示交易信息的数据结构,具体看下面的JSON信息截图。

Envelope数据

此数据类型主要用于存储区块中的交易信息。交易信息包括两个字段,signature(交易发送者的签名)、payload(数据载荷)(具体看上方截图和下面的数据类型)。signature是一个buffer数组类型的签名数据,并无其他可用信息,故下面主要分析payload数据载荷字段部分。

type Envelope struct {
     Payload   []byte 
     Signature []byte
}

Envelope.payload字段

Palyload中包含了HeaderData两个字段,其中Header中又包含了ChannelHeaderSignatureHeader。具体看下面的数据结构及JSON信息截图。

type Payload struct {
     Header *Header 
     Data   []byte 
}
type Header struct {
    ChannelHeader *ChannelHeader,
    SignatureHeader *SignatureHeader
}
  1. channelHeader

    channelHeader数据包括type(头类型)、version(版本)、timestamp(时间戳,即交易产生时间)、channel_id(通道id)、tx_id(交易id,即交易哈希)、epoch(时期,该字段当前未使用)、extension(可附加的扩展)、typeString(类型字符串,主要包括MESSAGE、CONFIG(表示当前块为区块链配置块)、CONFIG_UPDATE、ENDORSER_TRANSACTION(表示当前块为区块链正常交易块,大多数区块都为此类型)、ORDERER_TRANSACTION、DELIVER_SEEK_INFO、CHAINCODE_PACKAGE等类型)。

  2. SignatureHeader

    SignatureHeader数据包括creator(交易创建者的信息,具体的peer节点信息好像可以通过解析证书来实现,但我还未实现,有已经实现的朋友可以在下面留言告诉我,谢谢大家!)、nonce(随机数),其中creator包括创建者的证书和Mspid(成员服务提供者的身份证书)。

Envelope.payload.data字段

data包含一个actions字段,对应的值是一个action数组,每个数组又包含两部分,headerpayloadheader结构都与上面解析过的signature_header相同(目前未搞懂fabric设计者的做此举目的),下面主要讨论payload字段部分。

Envelope.payload.data.actions.payload字段

payload字段包括chaincode_proposal_payload(背书提案时调用链码的信息)和action字段,action字段又分为proposal_response_payload(提案时响应信息,也就是是不是提案成功了,成功了返回状态码为200)及endorsements(背书节点信息)字段。

​ 下面具体分析chaincode_proposal_payloadproposal_response_payload两个字段:

  1. chaincode_proposal_payload

    chaincode_proposal_payload具体结构参见上面的截图。chaincode_proposal_payload含有一个input字段,该字段中又包含chaincode_spec字段。chaincode_spec字段包含链码信息和调用期间使用的参数。type是链码类型,typeString是链码使用的语言,input是使用链码的参数,decoration字段含义未知(中文含义为装饰物),但对应的值一般为空。chaincode_id字段包含链码的路径、名称和版本信息。

    最后具体说一下input字段,它包含一个args数组,数组中含有两个元素,第一个元素是调用链码的函数名,第二个是函数参数,都为buffer数组,大家可以通过将buffer转换为string来获取到原数据。

  2. proposal_response_payload

    proposal_response_payload字段包含链码模拟执行结果对KV类型状态数据库的读写集,包括proposal_hash(背书哈希值)、results(背书结果)、response(背书响应)、chaincode_id(链码信息)。results包含data_model(数据模型,但含义未知,一般为0)、ns_rwset(读写集数组)。ns_rwset包含namespacerwsetrwset包含read(读集)、writes(写集,包含键、值、删除标志)、range_queries_info(范围查询信息)、metadata_writesresponse包含status(响应状态值)、message(响应信息)、payload(返回的数据,一般是查询时采用此字段)。

  3. endorsements

    该字段包含背书者信息数组,每个背书者包含MspId、证书和此次背书的签名signature。此结构比较简单,就不展开具体分析。大家有需要可以自己通过在线json解析工具进行查看。

Block MetaData部分

元数据:和当前区块相关的元数据,用于描述Data的相关信息,包含排序节点的MspId、证书和随机数,以及签名。value的index属性及一些其他的字段含义也不太清楚,但是这些信息大多是空值并且与区块链相关状态信息并不相关,故并未仔细分析。数据结构如下:

type BlockMetadata struct {
    Metadata   [][]byte 
}

结语

上面的信息基本可以满足做区块链浏览器的需要了,但是还是缺少一部分信息,比如区块产生时间等等。目前我是以区块中最后一个交易的产生时间来作为区块产生时间的,这在严格意义上来说是不对,因为产生最后一个交易后需要经过Orderer节点的排序、打包等操作才能产生区块。

最后衷心希望上述信息能给大家带来帮助!

本博客中部分信息参考于:https://blog.csdn.net/alextan_/article/details/110826476
在此感谢此博客的作者!

posted @ 2021-02-01 22:16  己平事  阅读(4769)  评论(2编辑  收藏  举报