HyperledgerFabric区块结构解析

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

HyperledgerFabric区块结构解析
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, },
"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\nbiBGcmFuY2lzY28x },
"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\nbiBGcmFuY2lzY28 },
"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, }],
"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\ncmFuY2lzY28xG },
"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, 14 }
}]
}
}
}]
}
}
}]
},
"metadata": {
"metadata": [{
"value": "\n\u0002\b\u0003",
"signatures": [{
"signature_header": {
"creator": {
"Mspid": "",
"IdBytes": "-----BEGIN CERTIFICATE-----\nMIICDTCCAbOgAwIBAgIRAKZzKwIm1fXv9TbfsLSlJpUwCgYIKoZIzj0EAwIwaTEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xFDA },
"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 }
}]
}, {
"value": {
"index": "3"
},
"signatures": []
},
[0]
]
}
}
⼤家可以通过对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
}
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数组,每个数组⼜包含两部分,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两个字段:
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包含namespace、rwset。

rwset包
含read(读集)、writes(写集,包含键、值、删除标志)、range_queries_info(范围查询信息)、metadata_writes。

response包含status(响应状态值)、message(响
应信息)、payload(返回的数据,⼀般是查询时采⽤此字段)。

3. endorsements
该字段包含背书者信息数组,每个背书者包含MspId、证书和此次背书的签名signature。

此结构⽐较简单,就不展开具体分析。

⼤家有需要可以⾃⼰通过在线json 解析⼯具进⾏查看。

Block MetaData部分
元数据:和当前区块相关的元数据,⽤于描述Data的相关信息,包含排序节点的MspId、证书和随机数,以及签名。

value的index属性及⼀些其他的字段含义也不太清楚,但是这些信息⼤多是空值并且与区块链相关状态信息并不相关,故并未仔细分析。

数据结构如下:
type BlockMetadata struct {
Metadata [][]byte
}
结语
上⾯的信息基本可以满⾜做区块链浏览器的需要了,但是还是缺少⼀部分信息,⽐如区块产⽣时间等等。

⽬前我是以区块中最后⼀个交易的产⽣时间来作为区块产⽣时间的,这在严格意义上来说是不对,因为产⽣最后⼀个交易后需要经过Orderer节点的排序、打包等操作才能产⽣区块。

最后衷⼼希望上述信息能给⼤家带来帮助!
本博客中部分信息参考于:
在此感谢此博客的作者!。

相关文档
最新文档