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

在本文将解释如何在超账本Hyperledger Fabric中使用Protobuf对数据进行序列化和结构化。

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

作者 | Deeptiman Pattnaik译者 | 火火酱,责编 | Carol出品 |  区块链大本营(blockchain_camp)

在本文中,我将解释如何在超账本Hyperledger Fabric中使用Protobuf对数据进行序列化和结构化。Protobuf简化了Hyperledger Fabric中数据处理和格式化过程。它使用特殊生成的源代码产生数据,从而在同一个智能合约中轻松地写入和读取数据。

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

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集合。在区块链状态下,所有这些记录都被存储在字节中,并且是不可变的。

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

Protobuf

协议缓冲区(简称protobuf)是谷歌的序列化结构化数据,其无关语言和平台,并且具有可扩展机制。与传统的数据格式(如XMLJSON)相比,序列化结构化数据以字节为单位进行编译,因此更小、更快、更简单。

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

为什么要使用Protobuf?

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

但是,如果我们从查询目的的角度来看smartcontract的话,两个数据集之间就没有关系了。我们不能为UserFinancial数据模型定义相同的ID作为键,因为分类帐数据存储在键值对中,如果出现相同的键,则信息将被覆盖。

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

这两条记录将在分类账状态中以两个不同的ID进行存储

为了解决这个问题,Protobuf提供了一个更快、更灵活的解决方案。我们只需编写一个.proto文件来描述数据结构,在本例中,是我们要存储的Financial数据结构。

因此,protobuf消息格式的字节结果直接调用到User SmartContract并完全删除Financial SmartContract。

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

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
    0x1f0x8b0x080x000x000x000x000x000x020xff0xe20xe20x4f0xcb0xcc0x4b,
    0xcc0x4b0xce0x4c0xcc0xd10x2b0x280xca0x2f0xc90x170x620xc90x4d0xcc,
    0xcc0x530x6a0x660xe40xe20x740x830xc90x080x490x710x710x240x250xe6,
    0x650xfb0x250xe60xa60x4a0x300x2a0x300x6a0x700x060xc10xf90x200xb9,
    0xcc0xb40xe20x640xe70xfc0x940x540x090x260x880x1c0x8c0x2f0x240xc3,
    0xc50x990x980x9c0xec0x570x9a0x9b0x940x5a0x240xc10x0c0x960x440x08,
    0x080x290x700x710x270x170xa50x260x960xa40xa60xb80x240x960xa40x4a,
    0xb00x800xe50x910x850x920xd80xc00x4e0x320x060x040x000x000xff0xff,
    0x440x010xf80x140xa50x000
现在,我们将在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()

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

总结

Protobuf简化了数据处理和格式化。它使用特殊生成的源代码来构造数据,从而实现在同一smartcontract中轻松写入和读取数据。

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

参考文献

1. 

2. 

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

希望你能有所收获!

原文链接:

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

本文来自,仅作分享,存在异议请联系平台删除。本文观点不代表刺猬财经 - 刺猬区块链资讯站立场。

(0)
上一篇 2020年5月18日
下一篇 2020年5月18日

相关推荐