下载fabric1.2,使用bootstrap.sh下载所需镜像。
注意:fabric12.需要go的版本 为1.10.X。
1.定义私有数据集合的配置文件
// collections_config.json
[
{
"name": "collectionMarbles",
"policy": "OR('Org1MSP.member', 'Org2MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":1000000
},
{
"name": "collectionMarblePrivateDetails",
"policy": "OR('Org1MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":3
}
]
集合定义配置文件由5个属性组成:
* name:集合名称
* policy:定义组织peer节点,允许使用签名策略语法来持久化收集数据,每个成员被包含在签名策略列表中。
* requiredPeerCount:在peer节点签名背书并返回提案响应给客户端,成功传送私有数据的最小认可节点。
* maxPeerCount:为了数据冗余保存的目的,需要同步保存私有数据的被认可的最大peer节点数量。如果一个可信节点宕机后,其他的持有私有冗余数据的节点依然可以提供数据请求访问服务。
* blockToLive:私有数据在sideDB保存时长。如果要一致保存,则配置为0。
2. 使用链码API对私有数据进行读写操作
代码如下:
//marbles_private.go文件:
package main
import (
"encoding/json"
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)
// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}
// Org1 与 Org2 组织中的peer节点可以从sidedb中访问私有数据
type marble struct {
ObjectType string `json:"docType"`
Name string `json:"name"`
Color string `json:"color"`
Size int `json:"size"`
Owner string `json:"owner"`
}
// Org1组织中的Peer节点可以从sidedb中访问私有数据
type marblePrivateDetails struct {
ObjectType string `json:"docType"`
Name string `json:"name"`
Price int `json:"price"`
}
// ===================================================================================
// Main
// ===================================================================================
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}
// Init initializes chaincode
// ===========================
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Success(nil)
}
// Invoke - Our entry point for Invocations
// ========================================
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
fmt.Println("invoke is running " + function)
// Handle different functions
if function == "invokeMarble" {
return t.invokeMarble(stub, args)
} else if function == "readMarble" { //read a marble
return t.readMarble(stub, args)
} else if function == "readMarblePrivateDetails" { //find marbles for owner X using rich query
return t.readMarblePrivateDetails(stub, args)
}
fmt.Println("invoke did not find func: " + function) //error
return shim.Error("Received unknown function invocation")
}
func (t *SimpleChaincode) invokeMarble(stub shim.ChaincodeStubInterface, args []string) pb.Response {
// === Save marble to state ===
marbles := []marble{
marble{ObjectType: "marble", Name: "marble2", Color: "blue", Size: 15, Owner : "tom"},
marble{ObjectType: "marble", Name: "marble3", Color: "blue", Size: 50, Owner : "tom"},
marble{ObjectType: "marble", Name: "marble4", Color: "red", Size: 35, Owner : "bob"},
marble{ObjectType: "marble", Name: "marble5", Color: "red", Size: 15, Owner : "bob"},
marble{ObjectType: "marble", Name: "marble6", Color: "red", Size: 8, Owner : "pp"},
}
i := 0
for i < len(marbles){
marbleJSONasBytes,_ := json.Marshal(marbles[i])
err := stub.PutPrivateData("collectionMarbles", "marble"+strconv.Itoa(i+2), marbleJSONasBytes)
if err != nil {
return shim.Error(err.Error())
}
i = i + 1
}
// ==== Save marble private details ====
objectType := "marblePrivateDetails"
marblePrivateDetails := &marblePrivateDetails{objectType, "marble1", 100}
marblePrivateDetailsBytes, err := json.Marshal(marblePrivateDetails)
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutPrivateData("collectionMarblePrivateDetails", "marble1", marblePrivateDetailsBytes)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
// ===============================================
// 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)
}
定义的规则如下:
- "name,color,size,and owner" 可以被Org1和Org2组织下的所有成员访问
- "price" 仅仅被Org1组织下的成员访问
其中,链码的shim APIs接口提供常用私有数据操作方法:
- PutPrivateData(collection,key,value)
- GetPrivateData(collection,key)
- GetPrivateDataByRange(collection, startKey, endKey string)
- GetPrivateDataByPartialCompositeKey(collection, objectType string, keys []string)
-
GetPrivateDataQueryResult(collection, query string) (*只有使用couchdb才能使用富查询语句
注意:fabric1.2的e2e默认背书策略为AND,invoke等操作都需要在两个组织的背书节点上进行背书。
3.安装链码 (script.sh)
peer chaincode install -n mycc5 -v 1.0 -p github.com/hyperledger/fabric/examples/chaincode/go/marbles_private
4.初始化链码 (script.sh)
peer chaincode instantiate -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc5 -v 1.0 -c '{"Args":["init"]}' -P "AND ('Org1MSP.peer','Org2MSP.peer')" --collections-config /opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go/marbles_private/collections_config.json
注意在链码初始化的时候使用命令加载该配置文件
peer chaincode instantiate ... --collections-config
5.插入弹珠(script.sh)
peer chaincode invoke -o orderer.example.com:7050 --tls --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc4 $PEER_CONN_PARMS -c '{"Args":["invokeMarble"]}'
6.查询私有数据
peer chaincode query -C mychannel -n mycc -c '{"Args":["readMarble","marble1"]}'
peer chaincode query -C mychannel -n mycc -c '{"Args":["readMarblePrivateDetails","marble1"]}'
注意:若查询未被授权的私有数据,则会返回Error:nil
7. 清理私有数据
在私有数据集合中配置的属性blockToLive,可以设置在多少个区块后清理私有数据,只留下数据的哈希值,作为交易的不可改变的证据。
比如我们在创建上面的样例链码的时候,设置私有数据集合属性blockToLive=4。
在上面的样例中,我们已经初始化了一个marble1,其中price=99,使用如下命令查询:
详见https://blog.csdn.net/songbin830/article/details/81224203
参考https://hyperledger-fabric.readthedocs.io/en/release-1.2/private_data_tutorial.html#