超级账本Hyperledger Fabric中的Protobuf到底是什么?

作者 | 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 )是谷歌的序列化结构化数据,其无关语言和平台,并且具有可扩展机制。与传统的数据格式(如 XMLJSON )相比,序列化结构化数据以字节为单位进行编译,因此更小、更快、更简单。

为什么要使用Protobuf?

如我们所见,有 UserFinancial 两个smartcontract将信息存储在同一个用户的分类账中,即User存储用户的基本的信息,Financial存储用户银行账户的详细信息。

但是,如果我们从查询目的的角度来看smartcontract的话,两个数据集之间就没有关系了。我们不能为 UserFinancial 数据模型定义相同的 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:         "james@example.com",
      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. https://developers.google.com/protocol-buffers

2. https://developers.google.com/protocol-buffers/docs/gotutorial

以上,就是在超级账本Hyperledger Fabric中将Protobu用于到SmartContract的基本概述。

希望你能有所收获!

原文链接:https://hackernoon.com/what-is-protobuf-in-hyperledger-fabric-explained-gk7s32fz

本文为 CSDN 翻译,转载请注明出处。

更多精彩推荐
☞完了!CPU 一味求快出事儿了!| 原力计划
☞美国对华为出口管制升级;微信红包生成器App遭腾讯起诉;Python 3.8.3 发布 | 极客头条
☞潘石屹 Python 考试成绩 99 分,网友:还有一分怕你骄傲
☞发家致富靠 AI ?使用 keras 预测NBA比赛赚钱,回报率达136%……
☞看完这篇操作系统,和面试官扯皮就没问题了!
☞赠书 | 要解决区块链的可扩展性问题,有哪些可行的方法?
你点的每个“在看”,我都认真当成了喜欢
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章