作者 | Deeptiman Pattnaik译者 | 火火酱,责编 | Carol出品 | 区块链大本营(blockchain_camp)
在本文中,我将解释如何在超账本Hyperledger Fabric中使用Protobuf对数据进行序列化和结构化。Protobuf简化了Hyperledger Fabric中数据处理和格式化过程。它使用特殊生成的源代码产生数据,从而在同一个智能合约中轻松地写入和读取数据。
Chaincode和SmartContract
在hyperledger fabric中,Chaincode(链码)是一个特定的程序,被用于处理由区块链网络的参与者所同意的核心业务逻辑。Hyperledger fabric还使用了一种名为SmartContract的数据格式技术,该技术是为Chaincode中的一组特定数据模型定义的。Chaincode可以有多组SmartContract,这些SmartContract可以控制不同数据模型的事务逻辑。简单来说,SmartContract管理事务,而Chaincode 管理如何部署SmartContract。
例如:如果需要将一些用户信息记录存储到分类帐中,那么我们就需要一个能够定义单个记录所需数据字段的SmartContract。
User(用户)SmartContract
type User struct {
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
Mobile string `json:"mobile"`
Age string `json:"age"`
}
在该SmartContract中,有ID、电子邮件、姓名、移动电话、年龄等与个人用户记录相关的数据字段。同样,如果我们需要存储每位用户的财务记录,那么我们就需要另一种名为Financial的smartcontract。
Financial (金融)SmartContract
type Financial struct {
ID string `json:"id"`
BankName string `json:"bankName"`
IFSCCode string `json:"ifscCode"`
AccNumber string `json:"accNumber"`
CreatedDate string `json:"createdDate"`
}
这两个smartcontract将被部署到Chaincode中,并且将处理两个分类帐状态—— 世界状态(WorldState)和区块链的事务逻辑。
SmartContract在世界状态下主要执行Put、Get、Delete和GetHistory。
1. PutState ——为每个不同的键创建新对象,或者覆盖现有对象。
2. GetState —— 从分类帐状态中检索不同键的对象。
3. DelState ——从分类账的世界状态中移除对象。
4. GetHistoryForKey —— 返回跨时间键的所有交易历史记录。
所有记录都作为世界状态记录被存储在CouchDB中。对象以JSON格式存储为键值对。CouchDB能更快地从数据库中查询JSON集合。在区块链状态下,所有这些记录都被存储在字节中,并且是不可变的。
Protobuf
协议缓冲区(简称protobuf)是谷歌的序列化结构化数据,其无关语言和平台,并且具有可扩展机制。与传统的数据格式(如XML或JSON)相比,序列化结构化数据以字节为单位进行编译,因此更小、更快、更简单。
为什么要使用Protobuf?
如我们所见,有User和Financial两个smartcontract将信息存储在同一个用户的分类账中,即User存储用户的基本的信息,Financial存储用户银行账户的详细信息。
但是,如果我们从查询目的的角度来看smartcontract的话,两个数据集之间就没有关系了。我们不能为User和Financial数据模型定义相同的ID作为键,因为分类帐数据存储在键值对中,如果出现相同的键,则信息将被覆盖。
这两条记录将在分类账状态中以两个不同的ID进行存储
为了解决这个问题,Protobuf提供了一个更快、更灵活的解决方案。我们只需编写一个.proto文件来描述数据结构,在本例中,是我们要存储的Financial数据结构。
因此,protobuf消息格式的字节结果直接调用到User SmartContract并完全删除Financial SmartContract。
Protobuf是如何运作的?
接下来,我们将了解如何设置protobuf编译器并生成protobuf消息格式。
安装
首先,我们需要遵循一定的安装流程才能使用protobuf-compiler。
$ go get github.com/golang/protobuf
$ go get github.com/golang/protobuf/proto
$ go get -u github.com/golang/protobuf/protoc-gen-go
$ export PATH=$PATH:$GOPATH/bin
现在,安装protobuf-compiler
$ sudo apt install protobuf-compiler
然后,在命令行中输入protoc’。应该会显示‘Missing input file’(缺少输入文件),这表示protobuf-compiler 已经成功安装。
示例
首先,我们需要创建一个financial.proto文件。它由Financial类型的消息格式组成,包含四个字段:银行名称、ifsc代码、帐号、创建日期。
financial.proto
syntax="proto3";
package main;
message Financial {
string bankName = 1;
string ifscCode = 2;
string accNumber = 3;
string createdDate = 4;
}
编译该proto文件,生成用于Financial消息格式的protobuf数据模型文件。
$ protoc --go_out=. *.proto
你会看到protobuf文件已生成为financial.pb.go。该文件是与proto包兼容的数据模型,将被用于把proto消息格式转换为字节。
financial.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: financial.proto
package main
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Financial struct {
BankName string `protobuf:"bytes,1,opt,name=bankName,proto3" json:"bankName,omitempty"`
IfscCode string `protobuf:"bytes,2,opt,name=ifscCode,proto3" json:"ifscCode,omitempty"`
AccNumber string `protobuf:"bytes,3,opt,name=accNumber,proto3" json:"accNumber,omitempty"`
CreatedDate string `protobuf:"bytes,4,opt,name=createdDate,proto3" json:"createdDate,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Financial) Reset() { *m = Financial{} }
func (m *Financial) String() string { return proto.CompactTextString(m) }
func (*Financial) ProtoMessage() {
func (*Financial) Descriptor() ([]byte, []int) {
return fileDescriptor_a283ebe7677acfbc, []int{0}
}
func (m *Financial) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Financial.Unmarshal(m, b)
}
func (m *Financial) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Financial.Marshal(b, m, deterministic)
}
func (m *Financial) XXX_Merge(src proto.Message) {
xxx_messageInfo_Financial.Merge(m, src)
}
func (m *Financial) XXX_Size() int {
return xxx_messageInfo_Financial.Size(m)
}
func (m *Financial) XXX_DiscardUnknown() {
xxx_messageInfo_Financial.DiscardUnknown(m)
}
var xxx_messageInfo_Financial proto.InternalMessageInfo
func (m *Financial) GetBankName() string {
if m != nil {
return m.BankName
}
return ""
}
func (m *Financial) GetIfscCode() string {
if m != nil {
return m.IfscCode
}
return ""
}
func (m *Financial) GetAccNumber() string {
if m != nil {
return m.AccNumber
}
return ""
}
func (m *Financial) GetCreatedDate() string {
if m != nil {
return m.CreatedDate
}
return ""
}
func init() {
proto.RegisterType((*Financial)(nil), "main.Financial")
}
func init() { proto.RegisterFile("financial.proto", fileDescriptor_a283ebe7677acfbc) }
var fileDescriptor_a283ebe7677acfbc = []byte{
// 136 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4f, 0xcb, 0xcc, 0x4b,
0xcc, 0x4b, 0xce, 0x4c, 0xcc, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, 0xcc,
0xcc, 0x53, 0x6a, 0x66, 0xe4, 0xe2, 0x74, 0x83, 0xc9, 0x08, 0x49, 0x71, 0x71, 0x24, 0x25, 0xe6,
0x65, 0xfb, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xf9, 0x20, 0xb9,
0xcc, 0xb4, 0xe2, 0x64, 0xe7, 0xfc, 0x94, 0x54, 0x09, 0x26, 0x88, 0x1c, 0x8c, 0x2f, 0x24, 0xc3,
0xc5, 0x99, 0x98, 0x9c, 0xec, 0x57, 0x9a, 0x9b, 0x94, 0x5a, 0x24, 0xc1, 0x0c, 0x96, 0x44, 0x08,
0x08, 0x29, 0x70, 0x71, 0x27, 0x17, 0xa5, 0x26, 0x96, 0xa4, 0xa6, 0xb8, 0x24, 0x96, 0xa4, 0x4a,
0xb0, 0x80, 0xe5, 0x91, 0x85, 0x92, 0xd8, 0xc0, 0x4e, 0x32, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff,
0x44, 0x01, 0xf8, 0x14, 0xa5, 0x00, 0
现在,我们将在User smartcontract中创建一个额外的数据字段financial。
type User struct {
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
Mobile string `json:"mobile"`
Age string `json:"age"`
Financial string `json:"financial"`
}
Financial消息格式参考
financial := &Financial {
ID: "F1",
BankName: "Hellenic Bank",
IFSCCode: "1234",
AccNumber: "8765",
CreatedDate : "12/12/08,
}
在将用户记录添加到分类帐时,还可以将financial 消息格式添加到相同的User smartcontract中。
package main
import (
"fmt"
"log"
"github.com/golang/protobuf/proto"
)
func main() {
financial := &Financial {
BankName: "Hellenic Bank",
IFSCCode: "1234",
AccNumber: "8765",
CreatedDate : "12/12/08,
}
financialdata, err := proto.Marshal(financial)
if err != nil {
log.Fatal("marshaling error: ", err)
}
userdata := &User {
ID: "1",
Email: "[email protected]",
Name: "James",
Mobile: "8765432",
Age: "34",
Financial: string(financialdata),
}
userDataJSONasBytes, err := json.Marshal(userdata)
if err != nil {
return shim.Error(err.Error())
}
indexName := "id"
userNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{userdata.ID})
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutState(userNameIndexKey, userDataJSONasBytes)
if err != nil {
return shim.Error(err.Error())
解析Protobuf
解析protobuf数据非常简单,因为它以字节的形式存储记录,可以使用Financial proto轻松地对其进行解析。
financialByteData, err := proto.Marshal(financialData)
if err != nil {
log.Fatal("marshaling error: ", err)
}
//Parsing the financial byte data
financial := &Financial{}
err = proto.Unmarshal(financialByteData, financial)
if err != nil {
log.Fatal("unmarshaling error: ", err)
}
fmt.Println("BankName : "+financial.GetBankName())
fmt.Println("IFSCCode : "+financial.GetIfscCode())
fmt.Println("AccNumber : "+financial.GetAccNumber())
fmt.Println("CreatedDate : "+financial.GetCreatedDate()
总结
Protobuf简化了数据处理和格式化。它使用特殊生成的源代码来构造数据,从而实现在同一smartcontract中轻松写入和读取数据。
参考文献
1.
2.
以上,就是在超级账本Hyperledger Fabric中将Protobu用于到SmartContract的基本概述。
希望你能有所收获!
原文链接:
本文为 CSDN 翻译,转载请注明出处。
本文来自,仅作分享,存在异议请联系平台删除。本文观点不代表刺猬财经 - 刺猬区块链资讯站立场。