第十六章 九析带你轻松完爆 hyperledger fabric - 背书策略 Policy

本系列文章:


第一章:九析带你完爆 hyperledger fabric - 快速上手 basic-network 样例

第二章:九析带你完爆 hyperledger fabric - 快速搭建 GO 环境

第三章:九析带你完爆 hyperledger fabric - 快速上手 first-network 样例

第四章:九析带你完爆 hyperledger fabric - 系统逻辑架构篇

第五章:九析带你完爆 hyperledger fabirc - 网络节点介绍

第六章:  九析带你轻松完爆 hyperledger fabric - 区块和区块链介绍

第七章:  九析带你轻松完爆 hyperledger fabric - 区块链防篡改机制

第八章:九析带你轻松完爆 hyperledger fabric - Couchdb 安装

第九章:九析带你轻松完爆 hyperledger fabric - NVM 和 Nodejs 安装

第十章:九析带你轻松完爆 hyperledger fabric - chaincode 生命周期介绍

第十一章: 九析带你轻松完爆 hyperledger fabric - 创建联盟

第十二章: 九析带你轻松完爆 hyperledger fabric - configtxlator 尝鲜

第十三章: 九析带你轻松完爆 hyperledger fabric - 创建静态组织

第十四章: 九析带你轻松完爆 WARNING: The COMPOSE_PROJECT_NAME

第十五章: 九析带你轻松完爆 hyperledger fabric - chaincode 链码调用

第十六章: 九析带你轻松完爆 hyperledger fabric - 背书策略 Policy


1 前言

        fabric 的交易过程是三段式的,即 execution -> order -> validate。客户端提交交易申请给不同的背书节点,背书节点模拟执行后将执行结果加密、签名后返回给客户端,客户端收集到所有的背书节点的执行结果后再将交易发送给 order 节点进行排序、打包、分发。分发后的结果会发送给 peer 节点,peer 节点收到 order 节点分发过来的 block 后,会对其中的交易进行验证,如果交易不符合预先定义的背书策略,本次交易就不会生效。因此背书策略真正发挥作用是在交易的校验阶段。

        如果你对博客有疑问,可以加本人微信询问:1.jpg


2 背书策略语法

        背书策略共有三种语法: AND、OR、OutOf。

        AND 语法:

AND( 'Org1MSP.member', 'Org2MSP.member', 'Org3MSP.member' ): 要求三个组织机构每一个组织机构成员都要签名

        OR 语法:

OR( 'Org1MSP.member', 'Org2MSP.member' ):要求两个组织机构至少有一个组织机构成员签名

        OutOf 语法:

OutOf( 2, 'Org1MSP.member', 'Org2MSP.member', 'Org3MSP.member' ):要求三个组织机构中,至少有两个组织机构成员签名

        组合语法:

AND('Org1MSP.member', OR('Org2MSP.member', 'Org3MSP.member')): 要求同时有组织机构 org1 所有成员签名,以及组织机构 org2MSP 和 org3MSP 中至少一个组织机构成员的签名


3 环境准备

        环境准备请参考本人的<<链码调用>>章节,整个目录结构截图如下:image2.png

3.1 生成证书

cryptogen generate --config=./crypto-config.yaml

3.2 生成创世区块

configtxgen -profile JiuxiOrdererGenesis -outputBlock ./config/genesis.block

3.3 创建通道交易文件

configtxgen -profile JiuxiChannel -outputCreateChannelTx ./config/channel1.tx -channelID channel1

3.4 创建 Anchor 节点的 MSP 会话文件

configtxgen -profile JiuxiChannel -outputAnchorPeersUpdate ./config/channel1Org1MSPanchors.tx -channelID channel1 -asOrg Org1MSP

configtxgen -profile JiuxiChannel -outputAnchorPeersUpdate ./config/channel1Org2MSPanchors.tx -channelID channel1 -asOrg Org2MSP

3.5 启动网络

docker-compose -f docker-compose.yaml up &

        启动后的截图如下:image3.png

3.6 创建通道

        根据上面创建的通道交易文件创建通道 channel1,通道创建成功后,会生成通道区块文件 .block。

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org1.jiuxi.org peer channel create -o orderer.jiuxi.org:7050 -c channel1 -f /etc/hyperledger/configtx/channel1.tx

3.7 将组织添加到通道中

        添加 org1 组织 pee0 节点:

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org1.jiuxi.org peer channel create -o orderer.jiuxi.org:7050 -c channel1 -f /etc/hyperledger/configtx/channel1.tx

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org1.jiuxi.org ls

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org1.jiuxi.org peer channel join -b channel1.block

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org1.jiuxi.org peer channel list

        添加 org1 组织 peer1 节点:

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer1.org1.jiuxi.org peer channel fetch 0 channel1.block -o orderer.jiuxi.org:7050 -c channel1

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer1.org1.jiuxi.org ls

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer1.org1.jiuxi.org peer channel join -b channel1.block

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer1.org1.jiuxi.org peer channel list

        添加 org2 组织 pee0 节点:

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org2.jiuxi.org peer channel fetch 0 channel1.block -o orderer.jiuxi.org:7050 -c channel1

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org2.jiuxi.org ls

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org2.jiuxi.org peer channel join -b channel1.block

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer0.org2.jiuxi.org peer channel list

        添加 org2 组织 peer1 节点:

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer1.org2.jiuxi.org peer channel fetch 0 channel1.block -o orderer.jiuxi.org:7050 -c channel1

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer1.org2.jiuxi.org ls

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer1.org2.jiuxi.org peer channel join -b channel1.block

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/[email protected]/msp" peer1.org2.jiuxi.org peer channel list


4 链码准备

4.1 链码编写

        链码文件如下,采用 go 语言编写:

/*

Copyright IBM Corp. 2016 All Rights Reserved.


Licensed under the Apache License, Version 2.0 (the "License");

you may not use this file except in compliance with the License.

You may obtain a copy of the License at


http://www.apache.org/licenses/LICENSE-2.0


Unless required by applicable law or agreed to in writing, software

distributed under the License is distributed on an "AS IS" BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and

limitations under the License.

*/


package main


//WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of

//calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has

//to be modified as well with the new ID of chaincode_example02.

//chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of

//hard-coding.


import (

"fmt"

"strconv"


"github.com/hyperledger/fabric/core/chaincode/shim"

pb "github.com/hyperledger/fabric/protos/peer"

)


// SimpleChaincode example simple Chaincode implementation

type SimpleChaincode struct {

}


func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {

fmt.Println("ex02 Init")

_, args := stub.GetFunctionAndParameters()

var A, B string    // Entities

var Aval, Bval int // Asset holdings

var err error


if len(args) != 4 {

return shim.Error("Incorrect number of arguments. Expecting 4")

}


// Initialize the chaincode

A = args[0]

Aval, err = strconv.Atoi(args[1])

if err != nil {

return shim.Error("Expecting integer value for asset holding")

}

B = args[2]

Bval, err = strconv.Atoi(args[3])

if err != nil {

return shim.Error("Expecting integer value for asset holding")

}

fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)


// Write the state to the ledger

err = stub.PutState(A, []byte(strconv.Itoa(Aval)))

if err != nil {

return shim.Error(err.Error())

}


err = stub.PutState(B, []byte(strconv.Itoa(Bval)))

if err != nil {

return shim.Error(err.Error())

}


return shim.Success(nil)

}


func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {

fmt.Println("ex02 Invoke")

function, args := stub.GetFunctionAndParameters()

if function == "invoke" {

// Make payment of X units from A to B

return t.invoke(stub, args)

} else if function == "delete" {

// Deletes an entity from its state

return t.delete(stub, args)

} else if function == "query" {

// the old "Query" is now implemtned in invoke

return t.query(stub, args)

}


return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"")

}


// Transaction makes payment of X units from A to B

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

var A, B string    // Entities

var Aval, Bval int // Asset holdings

var X int          // Transaction value

var err error


if len(args) != 3 {

return shim.Error("Incorrect number of arguments. Expecting 3")

}


A = args[0]

B = args[1]


// Get the state from the ledger

// TODO: will be nice to have a GetAllState call to ledger

Avalbytes, err := stub.GetState(A)

if err != nil {

return shim.Error("Failed to get state")

}

if Avalbytes == nil {

return shim.Error("Entity not found")

}

Aval, _ = strconv.Atoi(string(Avalbytes))


Bvalbytes, err := stub.GetState(B)

if err != nil {

return shim.Error("Failed to get state")

}

if Bvalbytes == nil {

return shim.Error("Entity not found")

}

Bval, _ = strconv.Atoi(string(Bvalbytes))


// Perform the execution

X, err = strconv.Atoi(args[2])

if err != nil {

return shim.Error("Invalid transaction amount, expecting a integer value")

}

Aval = Aval - X

Bval = Bval + X

fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)


// Write the state back to the ledger

err = stub.PutState(A, []byte(strconv.Itoa(Aval)))

if err != nil {

return shim.Error(err.Error())

}


err = stub.PutState(B, []byte(strconv.Itoa(Bval)))

if err != nil {

return shim.Error(err.Error())

}


return shim.Success(nil)

}


// Deletes an entity from state

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

if len(args) != 1 {

return shim.Error("Incorrect number of arguments. Expecting 1")

}


A := args[0]


// Delete the key from the state in ledger

err := stub.DelState(A)

if err != nil {

return shim.Error("Failed to delete state")

}


return shim.Success(nil)

}


// query callback representing the query of a chaincode

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

var A string // Entities

var err error


if len(args) != 1 {

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

}


A = args[0]


// Get the state from the ledger

Avalbytes, err := stub.GetState(A)

if err != nil {

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

return shim.Error(jsonResp)

}


if Avalbytes == nil {

jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"

return shim.Error(jsonResp)

}


jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"

fmt.Printf("Query Response:%s\n", jsonResp)

return shim.Success(Avalbytes)

}


func main() {

err := shim.Start(new(SimpleChaincode))

if err != nil {

fmt.Printf("Error starting Simple chaincode: %s", err)

}

}

4.2 安装和初始化链码

        链码初始化过程中,配置了背书策略 AND( "Org1MSP.member", "Org2MSP.member" ),该背书策略配置后,针对该链码的所有 invoke 操作都必须同时发送给 Org1 和 Org2 才能生效,否则操作无效;但是针对该链码的所有 query 操作都是可以正确执行的。

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" cli peer chaincode install -n jiuxi -v 1.0.0 -p github.com/jiuxi

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" cli peer chaincode instantiate -o orderer.jiuxi.org:7050 -C channel1 -n jiuxi -v 1.0.0 -c '{"Args": ["init", "a", "100", "b", "200"]}' -P 'AND ("Org1MSP.member","Org2MSP.member")'

docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" cli peer chaincode query -C channel1 -n jiuxi -c '{"Args": ["query", "a"]}'

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_ADDRESS=peer0.org2.jiuxi.org:7051" -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.jiuxi.org/users/[email protected]/msp" cli peer chaincode install -n jiuxi -v 1.0.0 -p github.com/jiuxi

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_ADDRESS=peer0.org2.jiuxi.org:7051" -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.jiuxi.org/users/[email protected]/msp" cli peer chaincode query -C channel1 -n jiuxi -c '{"Args": ["query", "a"]}'

4.2.1 有效操作样例

        发送链码 invoke 操作时通过 --peerAddresses 参数,指定要发送的 peer 节点,本例中根据上面链码初始化时的背书策略,invoke 操作会同时发送给 org1 和 org2。

docker exec cli peer chaincode invoke -C channel1 -n jiuxi --peerAddresses peer0.org1.jiuxi.org:7051 --peerAddresses peer0.org2.jiuxi.org:7051 -c '{"Args": ["invoke", "a", "b", "10"]}'

docker exec cli peer chaincode query -C channel1 -n jiuxi -c '{"Args": ["query", "a"]}'

docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_ADDRESS=peer0.org2.jiuxi.org:7051" -e "CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.jiuxi.org/users/[email protected]/msp" cli peer chaincode query -C channel1 -n jiuxi -c '{"Args": ["query", "a"]}'

        invoke 操作成功之后,会发现此时 a 账户已经从 100 变成了 90。如下图所示:image4.png

4.2.2 无效操作样例

        如果在调用链码时,不指定 -peerAddress 参数,则并不会发送账户的改变。执行语句如下:

docker exec cli peer chaincode invoke -C channel1 -n jiuxi --peerAddresses peer0.org1.jiuxi.org:7051 --peerAddresses peer0.org2.jiuxi.org:7051 -c '{"Args": ["invoke", "a", "b", "10"]}'

        执行截图如下:image5.png

        再次执行查询语句如下:

docker exec cli peer chaincode query -C channel1 -n jiuxi -c '{"Args": ["query", "a"]}'

        发现账户还是 90,也就是说 invoke 操作并没有生效。

        自此,九析带你轻松完爆了 hyperledger fabric 的背书策略。

猜你喜欢

转载自blog.51cto.com/14625168/2473233