Hyperledger Fabric 区块结构解析
原文: https://www.cnblogs.com/yimeixiaobai1314/p/14359395.html
前言
最近在搞区块链浏览器,也就是通过网页来查看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
中包含了Header
和Data
两个字段,其中Header中又包含了ChannelHeader
和SignatureHeader
。具体看下面的数据结构及JSON信息截图。
type Payload struct {
Header *Header
Data []byte
}
type Header struct {
ChannelHeader *ChannelHeader,
SignatureHeader *SignatureHeader
}
-
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等类型)。 -
SignatureHeader
SignatureHeader
数据包括creator
(交易创建者的信息,具体的peer节点信息好像可以通过解析证书来实现,但我还未实现,有已经实现的朋友可以在下面留言告诉我,谢谢大家!)、nonce
(随机数),其中creator
包括创建者的证书和Mspid
(成员服务提供者的身份证书)。
Envelope.payload.data字段
data
包含一个actions
字段,对应的值是一个action数组,每个数组又包含两部分,header
和payload
。header
结构都与上面解析过的signature_header
相同(目前未搞懂fabric设计者的做此举目的),下面主要讨论payload
字段部分。
Envelope.payload.data.actions.payload字段
payload
字段包括chaincode_proposal_payload
(背书提案时调用链码的信息)和action
字段,action
字段又分为proposal_response_payload
(提案时响应信息,也就是是不是提案成功了,成功了返回状态码为200)及endorsements
(背书节点信息)字段。
下面具体分析chaincode_proposal_payload
与proposal_response_payload
两个字段:
-
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来获取到原数据。 -
proposal_response_payload
proposal_response_payload
字段包含链码模拟执行结果对KV类型状态数据库的读写集,包括proposal_hash
(背书哈希值)、results
(背书结果)、response
(背书响应)、chaincode_id
(链码信息)。results
包含data_model
(数据模型,但含义未知,一般为0)、ns_rwset
(读写集数组)。ns_rwset
包含namespace
、rwset
。rwset
包含read
(读集)、writes
(写集,包含键、值、删除标志)、range_queries_info
(范围查询信息)、metadata_writes
。response
包含status
(响应状态值)、message
(响应信息)、payload
(返回的数据,一般是查询时采用此字段)。 -
endorsements
该字段包含背书者信息数组,每个背书者包含
MspId
、证书和此次背书的签名signature
。此结构比较简单,就不展开具体分析。大家有需要可以自己通过在线json解析工具进行查看。
Block MetaData部分
元数据:和当前区块相关的元数据,用于描述Data的相关信息,包含排序节点的MspId、证书和随机数,以及签名。value的index属性及一些其他的字段含义也不太清楚,但是这些信息大多是空值并且与区块链相关状态信息并不相关,故并未仔细分析。数据结构如下:
type BlockMetadata struct {
Metadata [][]byte
}
结语
上面的信息基本可以满足做区块链浏览器的需要了,但是还是缺少一部分信息,比如区块产生时间等等。目前我是以区块中最后一个交易的产生时间来作为区块产生时间的,这在严格意义上来说是不对,因为产生最后一个交易后需要经过Orderer节点的排序、打包等操作才能产生区块。
最后衷心希望上述信息能给大家带来帮助!
本博客中部分信息参考于:https://blog.csdn.net/alextan_/article/details/110826476
在此感谢此博客的作者!
type ChannelHeader struct {
Type int32
Version int32
Timestamp *google_protobuf.Timestamp
ChannelId string
TxId string
Epoch uint64
Extension []byte
}
/*
Type: It denotes the transaction type used. ["MESSAGE", "CONFIG", "CONFIG_UPDATE", "ENDORSER_TRANSACTION",
"ORDERER_TRANSACTION", "DELIVER_SEEK_INFO", "CHAINCODE_PACKAGE"]
Version: The version number for protobuf used for serialization/de-serialization of the structures.
ChannelId: Channel name for the network
TxId: The transaction id for processing the transaction
Epoch: The fields are currently unused.
Extension: It contents Chaincode information that marshalled the ChaincodeHeaderExtension structure.
*/
https://blog.csdn.net/AlexTan_/article/details/110826476
type ChaincodeHeaderExtension struct { // The PayloadVisibility field controls to what extent the Proposal's payload // (recall that for the type CHAINCODE, it is ChaincodeProposalPayload // message) field will be visible in the final transaction and in the ledger. // Ideally, it would be configurable, supporting at least 3 main visibility // modes: // 1. all bytes of the payload are visible; // 2. only a hash of the payload is visible; // 3. nothing is visible. // Notice that the visibility function may be potentially part of the ESCC. // In that case it overrides PayloadVisibility field. Finally notice that // this field impacts the content of ProposalResponsePayload.proposalHash. PayloadVisibility []byte `protobuf:"bytes,1,opt,name=payload_visibility,json=payloadVisibility,proto3" json:"payload_visibility,omitempty"` // The ID of the chaincode to target. ChaincodeId *ChaincodeID `protobuf:"bytes,2,opt,name=chaincode_id,json=chaincodeId,proto3" json:"chaincode_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` }
https://pkg.go.dev/github.com/hyperledger/fabric/protos/peer#ChaincodeHeaderExtension
Fabric 2.2底层结构设计分析
https://www.cnblogs.com/veraland/p/13900791.html