Fabric1.4 编写链码下

Posted 杰西啊杰西

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Fabric1.4 编写链码下相关的知识,希望对你有一定的参考价值。

上一篇:(三)Fabric1.4 编写链码【上】

五、在开发者环境中加入couchdb

1、关闭网络

docker-compose -f docker-compose-simple.yaml down

2、编辑docker-compose-simple.yaml
首先在文件中添加couchdb段的配置:

couchdb:
    container_name: couchdb
    image: hyperledger/fabric-couchdb
    environment:
      - COUCHDB_USER=
      - COUCHDB_PASSWORD=
    ports:
      - 5984:5984
    networks:
      - default

在peer的environment部分添加:

environment:
    - CORE_LEDGER_STATE_STATEDATABASE=CouchDB
    - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984
    - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=
    - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=

在peer的depends_on部分添加:

depends_on:
    - couchdb

3、访问页面并查看区块数据
(这里的数据是最开始的链码代码那里的,新的我忘了截图orz)

小插曲:发现退出之后再启动无法进入cli,配置couchdb之后再也没办法进入cli

解决方法:

① 查看cli日志

docker logs cli

发现报错,说是连接被拒绝

Error: error getting endorser client for channel: endorser client failed to connect to peer:7051: failed to create new connection: connection error: desc = "transport: error while dialing: dial tcp 172.30.0.4:7051: connect: connection refused"


② 修改docker-compose-simple.yaml文件,等待一段时间使得couchdb完全启动
在cli的command部分,修改命名

command: /bin/bash -c 'sleep 20; ./script.sh'

③ 重新启动网络、chaincode容器和cli ,均成功

六、 使用链码使用富查询语句

因为普通查询只能根据主键查询,如果要使用根据其他字段查询就只能依靠搜索json字符串进行查询,这也是为什么使用couchdb 的原因
1、在链码文件夹下新建文件夹

2、编写索引文件
在该文件夹下新建 indexIDCard.json 文件,其中IDCard是要进行查询的字段

{
  "index":{
    "fields":["IDCard"]
  },
  "ddoc":"indexIDCard",
  "name":"indexIDCard",
  "type":"json"
}

3、修改链码,增加基于身份证IDCard的富查询、查看历史数据修改功能

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/protos/peer"
	"time"
)

type BasicInfo struct{
	//个人基本信息ID
	UserID string `json:"UserID"`
	//身份证
	IDCard string `json:"IDCard"`
	//姓名
	Name string `json:"Name"`
	//性别
	Sex string `json:"Sex"`
	//民族
	Nation string `json:"Nation"`
	//籍贯
	Native string `json:"Native"`
	//生日
	Birthday string `json:"Birthday"`
	//手机
	Phone string `json:"Phone"`
	//邮箱
	Email string `json:"Email"`
	//政治面貌
	PoliticalLook string `json:"PoliticalLook"`
	//家庭住址
	HomeAddress string `json:"HomeAddress"`
	//信息上传账号
	LoginUserID string `json:"LoginUserID"`
	//个人信息文件hash
	UserInfoFileHash string `json:"UserInfoFileHash"`
	//插入该数据的时间
	Time string `json:"Time"`

	Histories []HistoryItem  //当前BasicInfo的历史记录
}

type HistoryItem struct {
	TxId string    //交易hash,也是标志ID
	BasicInfo BasicInfo
}

type SmartContract struct{ //合约即链码
}

func (b *SmartContract) Init(stub shim.ChaincodeStubInterface) peer.Response{
	return shim.Success(nil)
}

//提供函数入口
func (b *SmartContract) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	function, args := stub.GetFunctionAndParameters()
	if function == "addBasicInfo" { //添加个人基本信息
		stub.SetEvent("addBasicInfo", []byte{})
		return b.addBasicInfo(stub, args)
	}else if function == "queryBasicByUserID" { //根据UserID查询个人基本信息
		stub.SetEvent("queryBasicByUserID", []byte{})
		return b.queryBasicByUserID(stub,args)
	}else if function == "delBasicInfoByUserID"{ // 根据UserID删除个人基础信息
		stub.SetEvent("delBasicInfoByUserID", []byte{})
		return b.delBasicInfoByUserID(stub, args)
	}else if function == "updateBasicInfoByUserID"{ // 根据UserID更新个人基础信息
		stub.SetEvent("updateBasicInfoByUserID", []byte{})
		return b.updateBasicInfoByUserID(stub, args)
	} else if function == "queryBasicByIDCard" { //根据IDCard查询个人基本信息
		stub.SetEvent("queryBasicByIDCard", []byte{})
		return b.queryBasicByIDCard(stub,args)
	}else if function == "queryHistoryBasic"{ // 查询历史变更数据
		stub.SetEvent("queryHistoryBasic", []byte{})
		return b.queryHistoryBasic(stub, args)
	}

	return shim.Error("Invalid BasicInfo Chaincode Smart Contract function name.")
}


//添加个人基本信息(链码函数)
/*
	**args:
		[0]:基本信息ID
		[1]:身份证
		[2]:姓名
		[3]:性别
		[4]:民族
		[5]:籍贯
		[6]:生日
		[7]:手机
		[8]:邮箱
		[9]:政治面貌
		[10]:家庭住址
		[11]:信息上传账号
		[12]:个人信息文件hash
*/
func (b *SmartContract) addBasicInfo(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	insert_time :=time.Now().Format("2006-01-02 15:04:05")
	basicInfo := BasicInfo{
			//个人基本信息id
			UserID:args[0],
			//身份证
			IDCard:args[1],
			//姓名
			Name:args[2],
			//性别
			Sex:args[3],
			//民族
			Nation:args[4],
			//籍贯
			Native:args[5],
			//生日
			Birthday:args[6],
			//手机
			Phone:args[7],
			//邮箱
			Email:args[8],
			//政治面貌
			PoliticalLook:args[9],
			//家庭住址
			HomeAddress:args[10],
			//信息上传账号
			LoginUserID:args[11],
			//个人信息文件hash
			UserInfoFileHash:args[12],
			//插入该数据的时间
			Time:insert_time, //time.Now().Format("2006-01-02 15:04:05")
	}

	basicInfoAsBytes, _ := json.Marshal(basicInfo)
	stub.PutState(basicInfo.UserID, basicInfoAsBytes)
	fmt.Println("Added", basicInfo)
	txid:=stub.GetTxID()  //获取当前交易的tx_id,每一笔交易和每一个客户端是唯一的
	return shim.Success([]byte(txid))   //返回区块txid
}


//根据用户ID进行查找人的基本信息(链码函数)
// args: UserID
func (b *SmartContract) queryBasicByUserID(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	//args传入函数多个参数,这里如果只传入UserID也可以不使用args []string 只用user_id string
	if len(args) < 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1")
	}
	basic_info, _ := stub.GetState(args[0])
	return shim.Success(basic_info)
}


// 根据UserID删除个人信息
// args: UserID
func (b *SmartContract) delBasicInfoByUserID(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("给定的参数个数不符合要求")
	}
	err := stub.DelState(args[0])
	if err != nil {
		return shim.Error("删除信息时发生错误")
	}
	return shim.Success([]byte("信息删除成功"))
}


//  根据UserID修改个人信息
//  args: UserID
func (b *SmartContract) updateBasicInfoByUserID(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	//反序列化传入参数
	insert_time :=time.Now().Format("2006-01-02 15:04:05")
	info := BasicInfo{
		//个人基本信息id
		UserID:args[0],
		//身份证
		IDCard:args[1],
		//姓名
		Name:args[2],
		//性别
		Sex:args[3],
		//民族
		Nation:args[4],
		//籍贯
		Native:args[5],
		//生日
		Birthday:args[6],
		//手机
		Phone:args[7],
		//邮箱
		Email:args[8],
		//政治面貌
		PoliticalLook:args[9],
		//家庭住址
		HomeAddress:args[10],
		//信息上传账号
		LoginUserID:args[11],
		//个人信息文件hash
		UserInfoFileHash:args[12],
		//插入该数据的时间
		Time:insert_time, //time.Now().Format("2006-01-02 15:04:05")
	}

	//根据userid查询信息
	basic, err:=stub.GetState(args[0])
	if err!=nil{
		return shim.Error("未找到要修改的信息")
	}

	var basic_info BasicInfo
	err = json.Unmarshal(basic,&basic_info)
	if err!=nil{
		return shim.Error("反序列化basic_info失败")
	}

	basic_info.IDCard=info.IDCard
	basic_info.Name=info.Name
	basic_info.Sex=info.Sex
	basic_info.Nation=info.Nation
	basic_info.Native=info.Native
	basic_info.Birthday=info.Birthday
	basic_info.Phone=info.Phone
	basic_info.Email=info.Email
	basic_info.PoliticalLook=info.PoliticalLook
	basic_info.HomeAddress=info.HomeAddress
	basic_info.LoginUserID=info.LoginUserID
	basic_info.UserInfoFileHash=info.UserInfoFileHash
	basic_info.Time=info.Time

	basic, err = json.Marshal(basic_info)
	if err != nil {
		return shim.Error("序列化basic_info失败")
	}

	// 保存basic_info状态
	err = stub.PutState(basic_info.UserID, basic)
	if err != nil {
		return shim.Error("更新basic_info失败")
	}

	return shim.Success([]byte("修改信息成功"))   //返回区块txid
}


//根据身份证号查询人的基本信息(链码函数)
// args: IDCard
func (b *SmartContract) queryBasicByIDCard(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("给定的参数个数不符合要求")
	}
	idCard := args[0]
	// 使用富查询进行查询数据
	// 拼装CouchDB所需要的查询字符串(是标准的一个JSON串)
	queryString := fmt.Sprintf("{\\"selector\\":{\\"IDCard\\":\\"%s\\"}}", idCard)
	resultsIterator, err := stub.GetQueryResult(queryString)
	if err != nil {
		return shim.Error(err.Error())
	}
	defer  resultsIterator.Close()

	// buffer is a JSON array containing QueryRecords
	var buffer bytes.Buffer

	bArrayMemberAlreadyWritten := false
	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}
		// Add a comma before array members, suppress it for the first array member
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString(",")
		}

		// Record is a JSON object, so we write as-is
		buffer.WriteString(string(queryResponse.Value))
		bArrayMemberAlreadyWritten = true
	}

	fmt.Printf("- queryAllBasicInfo:\\n%s\\n", buffer.String())

	return shim.Success(result)
}


//根据UserID查询历史变更数据
// args: UserID
func (b *SmartContract) queryHistoryBasic(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	if len(args) != 1 {
		return shim.Error("给定的参数个数不符合要求")
	}
	// 根据UserID查询basic-info状态
	basic, err := stub.GetState(args[0])
	if err != nil {
		return shim.Error("根据UserID查询信息失败")
	}

	if basic == nil {
		return shim.Error("根据UserID没有查询到相关的信息")
	}

	// 对查询到的状态进行反序列化
	var basic_info BasicInfo
	err = json.Unmarshal(basic, &basic_info)
	if err != nil {
		return  shim.Error("反序列化basic_info信息失败")
	}

	// 获取历史变更数据
	iterator, err := stub.GetHistoryForKey(basic_info.UserID)
	if err != nil {
		return shim.Error("根据指定的UserID查询对应的历史变更数据失败")
	}
	defer iterator.Close()

	// 迭代处理
	var histories []HistoryItem
	var hisBasicInfo BasicInfo
	for iterator.HasNext(){
		hisData, err :=iterator.Next()
		if err !=nil{
			return shim.Error("获取basic_info的历史变更数据失败")
		}

		var historyItem HistoryItem
		historyItem.TxId = hisData.TxId
		json.Unmarshal(hisData.Value, &hisBasicInfo)

		if hisData.Value == nil{
			var empty BasicInfo
			historyItem.BasicInfo = empty
		}else {
			historyItem.BasicInfo = hisBasicInfo
		}

		histories = append(histories,historyItem)

	}

	basic_info.Histories=histories

	//返回
	res, err := json.Marshal(basic_info)

	if err != nil{
		return shim.Error("序列化sco信息时发生错误")
	}

	return shim.Success(res)
}


func main(){
	err := shim.Start(new(SmartContract))
	if err != nil{
		fmt.Printf("启动SmartContract时发生错误: %s\\n", err)
	}
}

4、按照之前的方法,先开启网络,然后使用运行的chaincode镜像编译链码,通过后部署到区块链测试网络fabric-samples上。

以上是关于Fabric1.4 编写链码下的主要内容,如果未能解决你的问题,请参考以下文章

Fabric1.4 编写链码

Fabric1.4:链码管理与测试

Fabric1.4源码中链码容器启动过程解析

Fabric1.4.1视频教程即将推出

菜鸟学习Fabric源码学习 — 背书节点和链码容器交互

Fabric1.4 Fabric-SDK-go及web应用