前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Hyperledger Fabric私有数据

Hyperledger Fabric私有数据

作者头像
Zeal
发布2020-11-11 16:54:15
5290
发布2020-11-11 16:54:15
举报
文章被收录于专栏:Hyperledger实践Hyperledger实践

1. 使用场景

Fabric区块链网络一个channel即一个记账本, 在很多业务场景,一个记账本的数据自身组织可以读写,也可以提供给其它组织只读,部分读或部分写。数据隔离使用channel是粗粒度的,private data私有数据是fabric 1.2引入, 是为了在更细的粒度上控制数据访问。

2. 如何使用私有数据?

以fabric-sample/chaincode/marble02_private弹珠游戏为例.

(1) 文件collections_config.json

policy定义谁可以持久化(写)数据(符合policy的);

requiredPeerCount定义私有数据传播到给多少个peer节点;

blockToLive定义私有数据以区块的形式会被持久化多久,如果不想被清理, 配置为0;

memberOnlyRead定义true时则强制指定组织(根据policy确定)的客户端才能读取数据(设置为false会咋样?都不能读?还是所有都能读?)。

[

{

"name": "collectionMarbles",

"policy": "OR('Org1MSP.member', 'Org2MSP.member')",

"requiredPeerCount": 0,

"maxPeerCount": 3,

"blockToLive":1000000,

"memberOnlyRead": true

},

{

"name": "collectionMarblePrivateDetails",

"policy": "OR('Org1MSP.member')",

"requiredPeerCount": 0,

"maxPeerCount": 3,

"blockToLive":3,

"memberOnlyRead": true

}

]

定义两种私有数据类型

collectionMarbles的采访策略是Org1的成员或 Org2的成员都具有读写权限。

collectionMarblePrivateDetails的采访策略是Org1的成员具有读写权限。

私有数据设计思路是拆分数据, 公有的数据提取出来设置什么人可读写, 私有的提取出来让部分的人可读写。 跟我们平常说设置文件权限, 谁可读谁可写有些不一样。

policy首先是保证持久化的写权限, 数据要同步对应peer的记账本才行, peer节点没这些数据就谈不上读了。

(2) chaincode如何读写私有数据

marble的名称,颜色,大小和归属人对于org1和org2都开放

// Peers in Org1 and Org2 will have this private data in a side database

type marble struct {

ObjectType string `json:"docType"`

Name string `json:"name"`

Color string `json:"color"`

Size int `json:"size"`

Owner string `json:"owner"`

}

但marble的价格Org1才可以采访

// Only peers in Org1 will have this private data in a side database

type marblePrivateDetails struct {

ObjectType string `json:"docType"`

Name string `json:"name"`

Price int `json:"price"`

}

Org1节点数据的分布, 实际用到是side database边库来实现私有数据, 共有的状态还是一个库, marble基本属性一个私有库, 价格一个私有库。

Org2只能采访公共库,marble基本属性库, 没有价格私有库数据

直接上链码好了.

先调用initMarble初始化数据,链码初始化时设置的背书策略应该是Org1MSP或者Org2MSP

// export MARBLE=$(echo -n "{\"name\":\"marble1\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\",\"price\":99}" | base64)

// peer chaincode invoke -C mychannel -n marblesp -c '{"Args":["initMarble"]}' --transient "{\"marble\":\"$MARBLE\"}"

transientMap可以传入json数据, 也算是一种args, 这里应该需要用Org1MSP的节点, 因为分拆保存为两种私有数据。

// ============================================================

// initMarble - create a new marble, store into chaincode state

// ============================================================

func (t *SimpleChaincode) initMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {

var err error

type marbleTransientInput struct {

Name string `json:"name"` //the fieldtags are needed to keep case from bouncing around

Color string `json:"color"`

Size int `json:"size"`

Owner string `json:"owner"`

Price int `json:"price"`

}

// ==== Input sanitation ====

fmt.Println("- start init marble")

if len(args) != 0 {

return shim.Error("Incorrect number of arguments. Private marble data must be passed in transient map.")

}

transMap, err := stub.GetTransient()

if err != nil {

return shim.Error("Error getting transient: " + err.Error())

}

if _, ok := transMap["marble"]; !ok {

return shim.Error("marble must be a key in the transient map")

}

if len(transMap["marble"]) == 0 {

return shim.Error("marble value in the transient map must be a non-empty JSON string")

}

var marbleInput marbleTransientInput

err = json.Unmarshal(transMap["marble"], &marbleInput)

if err != nil {

return shim.Error("Failed to decode JSON of: " + string(transMap["marble"]))

}

if len(marbleInput.Name) == 0 {

return shim.Error("name field must be a non-empty string")

}

if len(marbleInput.Color) == 0 {

return shim.Error("color field must be a non-empty string")

}

if marbleInput.Size <= 0 {

return shim.Error("size field must be a positive integer")

}

if len(marbleInput.Owner) == 0 {

return shim.Error("owner field must be a non-empty string")

}

if marbleInput.Price <= 0 {

return shim.Error("price field must be a positive integer")

}

// ==== Check if marble already exists ====

marbleAsBytes, err := stub.GetPrivateData("collectionMarbles", marbleInput.Name)

if err != nil {

return shim.Error("Failed to get marble: " + err.Error())

} else if marbleAsBytes != nil {

fmt.Println("This marble already exists: " + marbleInput.Name)

return shim.Error("This marble already exists: " + marbleInput.Name)

}

// ==== Create marble object, marshal to JSON, and save to state ====

marble := &marble{

ObjectType: "marble",

Name: marbleInput.Name,

Color: marbleInput.Color,

Size: marbleInput.Size,

Owner: marbleInput.Owner,

}

marbleJSONasBytes, err := json.Marshal(marble)

if err != nil {

return shim.Error(err.Error())

}

// === Save marble to state ===

err = stub.PutPrivateData("collectionMarbles", marbleInput.Name, marbleJSONasBytes)

if err != nil {

return shim.Error(err.Error())

}

// ==== Create marble private details object with price, marshal to JSON, and save to state ====

marblePrivateDetails := &marblePrivateDetails{

ObjectType: "marblePrivateDetails",

Name: marbleInput.Name,

Price: marbleInput.Price,

}

marblePrivateDetailsBytes, err := json.Marshal(marblePrivateDetails)

if err != nil {

return shim.Error(err.Error())

}

err = stub.PutPrivateData("collectionMarblePrivateDetails", marbleInput.Name, marblePrivateDetailsBytes)

if err != nil {

return shim.Error(err.Error())

}

// ==== Index the marble to enable color-based range queries, e.g. return all blue marbles ====

// An 'index' is a normal key/value entry in state.

// The key is a composite key, with the elements that you want to range query on listed first.

// In our case, the composite key is based on indexName~color~name.

// This will enable very efficient state range queries based on composite keys matching indexName~color~*

indexName := "color~name"

colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marble.Color, marble.Name})

if err != nil {

return shim.Error(err.Error())

}

// Save index entry to state. Only the key name is needed, no need to store a duplicate copy of the marble.

// Note - passing a 'nil' value will effectively delete the key from state, therefore we pass null character as value

value := []byte{0x00}

stub.PutPrivateData("collectionMarbles", colorNameIndexKey, value)

// ==== Marble saved and indexed. Return success ====

fmt.Println("- end init marble")

return shim.Success(nil)

}

读取私有数据, readMarblePrivateDetails应该要Org2MSP才能读取

// ===============================================

// readMarble - read a marble from chaincode state

// ===============================================

func (t *SimpleChaincode) readMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {

var name, jsonResp string

var err error

if len(args) != 1 {

return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")

}

name = args[0]

valAsbytes, err := stub.GetPrivateData("collectionMarbles", name) //get the marble from chaincode state

if err != nil {

jsonResp = "{\"Error\":\"Failed to get state for " + name + "\"}"

return shim.Error(jsonResp)

} else if valAsbytes == nil {

jsonResp = "{\"Error\":\"Marble does not exist: " + name + "\"}"

return shim.Error(jsonResp)

}

return shim.Success(valAsbytes)

}

// ===============================================

// readMarblereadMarblePrivateDetails - read a marble private details from chaincode state

// ===============================================

func (t *SimpleChaincode) readMarblePrivateDetails(stub shim.ChaincodeStubInterface, args []string) pb.Response {

var name, jsonResp string

var err error

if len(args) != 1 {

return shim.Error("Incorrect number of arguments. Expecting name of the marble to query")

}

name = args[0]

valAsbytes, err := stub.GetPrivateData("collectionMarblePrivateDetails", name) //get the marble private details from chaincode state

if err != nil {

jsonResp = "{\"Error\":\"Failed to get private details for " + name + ": " + err.Error() + "\"}"

return shim.Error(jsonResp)

} else if valAsbytes == nil {

jsonResp = "{\"Error\":\"Marble private details does not exist: " + name + "\"}"

return shim.Error(jsonResp)

}

return shim.Success(valAsbytes)

}

更多细节参考官方文档

https://hyperledger-fabric.readthedocs.io/en/release-1.4/private_data_tutorial.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Hyperledger实践 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档