WDK研究記_ブロックチェーントレーサビリティシステムに基づくバックエンドインターフェース開発

まとめ

先週の問題は、何度も解決を試みた結果、コードをリファクタリングして問題を正常に解決しました。その後の分析では、ノードがチャネルに参加できなかったことが原因であるはずです。チェーン コードをインストールするときに、ノードはインストールされませんでしたが、チェーンはインストールされました。コードが実行された場合、承認ポリシーを実行するために選択されたノードはこのノードであるため、チェーンコードの呼び出しでエラーが発生します。問題を解決した後、いくつかの小さな問題に遭遇しましたが、すべての問題を解決した後、チェーン コードを呼び出し、ブロックチェーン台帳にデータを挿入し、fabric-go-sdk でデータをクエリする操作を正常に実装できました。また、Gin フレームワークを使用して、保育園組織の基本的なバックエンド サーバー インターフェイスを構築します。また、農民組織のバックエンド サーバー インターフェイス、政府機能組織のバックエンド サーバー インターフェイス、一般ユーザー組織のバックエンド サーバー インターフェイスの構築と、バックエンドの開発も必要です。ブロックチェーントレーサビリティシステムに基づくサーバーがほぼ完成しました。


1.fabric-go-sdkの各パッケージ機能の機能紹介

1.1 指定されたノードチャネルがすでに存在するかどうかを問い合わせます (関数: QuerySavedChannel(…) )

リソース管理クライアント、クエリのあるノードのドメイン名、およびクエリ対象のチャネルを入力します。チャネルがすでに存在する場合は true を返し、責任者は false を返します。
この関数は、まずresmgmt.WithTargetEndpoints(c.Peer)reqPeer を使用してクエリ対象のノードのリクエストを作成し、次にsourceClient.QueryChannels(reqPeer)クエリ結果を array に保存しchannelInfo、次にクエリ対象のチャネル ID がすでに存在するかどうかを調べます。

func QuerySavedChannel(sourceClient *resmgmt.Client, info InfoSdk, c InstallCcInfo) (bool, error) {
    
    
	reqPeer := resmgmt.WithTargetEndpoints(c.Peer)
	channelInfo, err := sourceClient.QueryChannels(reqPeer)
	if err != nil {
    
    
		return false, fmt.Errorf("failed to query channel already exists: %v", err)
	}
	for _, v := range channelInfo.Channels {
    
    
		if v.ChannelId == info.ChannelID {
    
    
			return true, nil
		}
	}
	return false, nil
}

1.2 チャネルを作成して参加する (関数: CreateChannel(…) )

インスタンス化されたfabricsdk-sdk、リソース管理クライアントsourceClient、組織名、管理者名、チャネル ID、チャネル ファイル パス、チャネル組織ドメイン名を入力し、チャネルを作成し、組織をチャネルに追加します。
この機能は、mspclient.New(sdk.Context(), mspclient.WithOrg(info.OrgName))MSP クライアント mspClient を作成し、それを使用してmspClient.GetSigningIdentity(info.Admin)組織管理者 adminIdentity の ID 情報を取得し、resmgmt.SaveChannelRequest{ChannelID: info.ChannelID, ChannelConfigPath: info.ChannelConfigPath, SigningIdentities: []msp.SigningIdentity{adminIdentity}}カプセル化を使用してチャネル要求 channelReq を作成し、それを使用してsourceClient.SaveChannel(channelReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrgOrdererName))チャネルを作成し、最後にそれを使用してsourceClient.JoinChannel(info.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrgOrdererName))組織を追加します。チャンネル。

// 输入实例化的`fabricsdk-sdk`、资源管理客户端`sourceClient`、组织名、管理员名、通道ID、通道文件路径、通道组织域名,创建通道,并将组织加入通道。
func CreateChannel(sdk *fabsdk.FabricSDK, sourceClient *resmgmt.Client, info InfoSdk) error {
    
    
	// New creates a new Client instance

	mspClient, err := mspclient.New(sdk.Context(), mspclient.WithOrg(info.OrgName))
	if err != nil {
    
    
		return fmt.Errorf("根据指定的 OrgName 创建 Org MSP 客户端实例失败: %v", err)
	}

	//  Returns: signing identity
	adminIdentity, err := mspClient.GetSigningIdentity(info.Admin)
	if err != nil {
    
    
		return fmt.Errorf("获取指定id的签名标识失败: %v", err)
	}

	// SaveChannelRequest holds parameters for save channel request
	channelReq := resmgmt.SaveChannelRequest{
    
    ChannelID: info.ChannelID, ChannelConfigPath: info.ChannelConfigPath, SigningIdentities: []msp.SigningIdentity{
    
    adminIdentity}}
	// save channel response with transaction ID
	_, err = sourceClient.SaveChannel(channelReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrgOrdererName))
	if err != nil {
    
    
		return fmt.Errorf("创建应用通道失败: %v", err)
	}

	fmt.Println("通道已成功创建,")

	// allows for peers to join existing channel with optional custom options (specific peers, filtered peers). If peer(s) are not specified in options it will default to all peers that belong to client's MSP.
	err = sourceClient.JoinChannel(info.ChannelID, resmgmt.WithRetry(retry.DefaultResMgmtOpts), resmgmt.WithOrdererEndpoint(info.OrgOrdererName))
	if err != nil {
    
    
		return fmt.Errorf("Peers加入通道失败: %v", err)
	}

	fmt.Println("peers 已成功加入通道.")

	return nil
}

1.3 指定されたノードの指定されたチェーンコードが既に存在するかどうかを問い合わせます (関数: QueryInstalledCC(…) )

リソース管理クライアント、クエリ ノード、クエリ チェーンコード名を入力し、指定されたノードのチェーンコードがすでに存在する場合は true を返し、それ以外の場合は false を返します。
この関数は、resmgmt.WithTargetEndpoints(c.Peer)クエリ ノード request をカプセル化することによって取得されreqPeerssourceClient.QueryInstalledChaincodes(reqPeers)クエリを実行し、結果を queryResult に保存するために使用されます。

//输入资源管理客户端、查询节点、查询链码名,给定节点上的链码已经存在就返回true,否则返回false
func QueryInstalledCC(sourceClient *resmgmt.Client, c InstallCcInfo) (bool, error) {
    
    
	reqPeers := resmgmt.WithTargetEndpoints(c.Peer)
	queryResult, err := sourceClient.QueryInstalledChaincodes(reqPeers)
	if err != nil {
    
    
		return false, fmt.Errorf("failed to query installed chaincode: %v", err)
	}
	for _, v := range queryResult.Chaincodes {
    
    
		if v.Name == c.CCID {
    
    
			return true, nil
		}
	}
	return false, nil
}

1.4 指定したノードにチェーンコードをインストールします (関数: InstallChaincode(…) )

リソース管理クライアント、チェーンコード パス、gopath パス、チェーンコード ID、チェーンコード バージョン、およびチェーンコードをインストールするノードを入力し、指定した組織にチェーンコードをインストールし、チェーンコード ID とバージョンを指定します。
この機能は、gopackager.NewCCPackage(c.CCPath, c.GoPath)チェーン コード パッケージをカプセル化して ccPkg を取得し、resmgmt.WithTargetEndpoints(c.Peer, c.Peer2)ノード リクエスト パッケージをカプセル化してパッケージを取得しreqPeer、最後にそれを使用してsourceClient.InstallCC(installReq, reqPeer)チェーン コードをインストールします。

installReq := resmgmt.InstallCCRequest{
    
    
		Name:    c.CCID,
		Path:    c.CCPath,
		Version: c.CCVersion,
		Package: ccPkg,
	}

取得するチェーンコードをインストールするためのリクエスト パッケージをカプセル化します。installReq

//输入资源管理客户端、链码路径、gopath路径、链码ID、链码版本、待安装链码的节点,在指定的组织上安装链码,并指定链码ID、版本。
func InstallChaincode(sourceClient *resmgmt.Client, c InstallCcInfo) error {
    
    
	ccPkg, err := gopackager.NewCCPackage(c.CCPath, c.GoPath)
	if err != nil {
    
    
		return fmt.Errorf("package chaincode failed: %v", err)
	}
	installReq := resmgmt.InstallCCRequest{
    
    
		Name:    c.CCID,
		Path:    c.CCPath,
		Version: c.CCVersion,
		Package: ccPkg,
	}
	reqPeer := resmgmt.WithTargetEndpoints(c.Peer, c.Peer2)
	_, err = sourceClient.InstallCC(installReq, reqPeer)
	if err != nil {
    
    
		return fmt.Errorf("Install chaincode failed: %v", c.Peer)
	}
	fmt.Println("Install chaincode sucessful")
	return nil
}

1.5 チェーンコードのインスタンス化 (関数: ChaincodeInit(…) )

リソース管理クライアント、チェーンコード ID、チェーンコード パス、チェーンコード バージョン、初期化引数、承認ポリシーを入力し、チェーンコードをインスタンス化します。
この関数は、次ccPolicy, err :=policydsl.FromString(c.Policy)を使用して承認ポリシーをカプセル化することで ccPolicy を取得します。

	req := resmgmt.InstantiateCCRequest{
    
    
		Name:    c.CCID,
		Path:    c.CCPath,
		Version: c.CCVersion,
		Args:    c.InitArgs,
		Policy:  ccPolicy,
	}

インスタンス化されたチェーンコード要求パケットをカプセル化して req を取得し、次にresmgmt.WithTargetEndpoints(c.Peer, c.Peer2)ノード要求パケットを使用してインストールされるチェーンコードをカプセル化して reqPeers を取得し、最後にsourceClient.InstantiateCC(c.ChannelID, req, reqPeers)指定されたノードを使用してチェーンコードをインスタンス化します。

//输入资源管理客户端、链码ID、链码路径、链码版本、初始化Args、背书策略,实例化链码
func ChaincodeInit(sourceClient *resmgmt.Client, c InstallCcInfo) error {
    
    
	ccPolicy, err := policydsl.FromString(c.Policy)
	if err != nil {
    
    
		return fmt.Errorf("create policy failed: %v", err)
	}
	req := resmgmt.InstantiateCCRequest{
    
    
		Name:    c.CCID,
		Path:    c.CCPath,
		Version: c.CCVersion,
		Args:    c.InitArgs,
		Policy:  ccPolicy,
	}
	reqPeers := resmgmt.WithTargetEndpoints(c.Peer, c.Peer2)
	_, err = sourceClient.InstantiateCC(c.ChannelID, req, reqPeers)
	if err != nil {
    
    
		return fmt.Errorf("init chaincode failed: %v", err)
	}
	fmt.Println("init chaincode sucessful")
	return nil
}

1.6 チェーンコードの呼び出し (データの追加: 関数: InvokeCC(…) )

チャネル管理クライアント、チェーンコードID、チェーンコードを呼び出す関数、実行する呼び出しコマンドを入力し、格納されているシリアル化データを返します。
この機能が使われているのは

req := channel.Request{
    
    
		ChaincodeID: c.CCID,
		Fcn:         c.Fcn,
		Args:        c.InvokeArgs,
	}

呼び出しチェーンコード要求をカプセル化して req を取得し、それを使用してchannel.WithTargetEndpoints(c.Peer)呼び出しチャネルの要求パッケージ reqPeers を取得し、最後にそれを使用してcc.Execute(req, reqPeers)呼び出しチェーンコード操作を実行します。

//输入通道管理客户端、链码ID、调用链码的函数、待执行的调用指令,返回已经存储的序列化后的数据
func InvokeCC(cc *channel.Client, c InstallCcInfo) ([]byte, error) {
    
    
	req := channel.Request{
    
    
		ChaincodeID: c.CCID,
		Fcn:         c.Fcn,
		Args:        c.InvokeArgs,
	}
	reqPeers := channel.WithTargetEndpoints(c.Peer)
	result, err := cc.Execute(req, reqPeers)
	if err != nil {
    
    
		return nil, fmt.Errorf("invoke chaincode failed: %v", err)
	}
	var d GenerateCrop
	err = json.Unmarshal(result.Payload, &d)
	if err != nil {
    
    
		return nil, fmt.Errorf("failed to unmarshal in InvokeCC")
	}
	fmt.Printf("Invoke chaincode sucessful already saved: %v\n", d)
	return result.Payload, nil
}

1.7 チェーンコードの呼び出し (クエリ操作: 関数: QueryCC(…) )

チャネル管理クライアント、チェーンコード ID、データをクエリするために呼び出される関数、クエリ コマンドを入力し、クエリ データ (シリアル化された) を返します。
この関数は最初に使用します。

	req := channel.Request{
    
    
		ChaincodeID: c.CCID,
		Fcn:         c.QueryFcn,
		Args:        c.QueryArgs,
	}

チェーンコードクエリリクエストパケットをカプセル化してreqを取得し、cc.Query(req)クエリデータパケットデータを使用します。

func QueryCC(cc *channel.Client, c InstallCcInfo) ([]byte, error) {
    
    
	req := channel.Request{
    
    
		ChaincodeID: c.CCID,
		Fcn:         c.QueryFcn,
		Args:        c.QueryArgs,
	}
	data, err := cc.Query(req)
	if err != nil {
    
    
		return nil, fmt.Errorf("failed to invoke chaincode: %v", err)
	}
	return data.Payload, nil
}

2、チェーンコード

2.1 ブロックチェーンにデータを追加する

保存するシリアル化データを入力し、stub.PutState() 関数を通じてブロックチェーン台帳にデータを保存します。
データの追加は主にstub.PutState(data.ID, d)関数を使用します。data.ID (文字列型) はデータの Key 値、d は格納するデータの型 ([]byte) であり、シリアル化されたデータです。

func PutData(stub shim.ChaincodeStubInterface, data GenerateCrop) ([]byte, bool) {
    
    
	d, err := json.Marshal(data)
	if err != nil {
    
    
		return nil, false
	}
	err = stub.PutState(data.ID, d)
	if err != nil {
    
    
		return nil, false
	}
	return d, true
}
func (t *GenerateChaincode) AddData(stub shim.ChaincodeStubInterface, args []string) peer.Response {
    
    
	var data GenerateCrop
	err := json.Unmarshal([]byte(args[0]), &data)
	if err != nil {
    
    
		return shim.Error("data Unmarshal failed")
	}
	d, e := PutData(stub, data)
	if e == false {
    
    
		return shim.Error("data saved failed")
	}
	if d == nil {
    
    
		return shim.Error("failed to marshal data")
	}
	return shim.Success(d)
}

2.2 ブロックチェーン台帳内のデータをクエリする

クエリするデータ ID を入力し、stub.GetState()その ID で最新のデータをクエリし、stub.GetHistoryForKey()その ID のすべてのデータをトレースして構造体に保存し、JSON.Marshal()データをシリアル化して返します。

func (t *GenerateChaincode) QueryDataByID(stub shim.ChaincodeStubInterface, ID string) peer.Response {
    
    
	queryResult, err := stub.GetState(ID)
	if err != nil {
    
    
		return shim.Error("failed to query data")
	}
	if queryResult == nil {
    
    
		return shim.Error("not found datas")
	}
	var UnmarResult GenerateCrop
	err = json.Unmarshal(queryResult, &UnmarResult)
	if err != nil {
    
    
		return shim.Error("failed to UnmarResult in QueryDataByID")
	}
	iterator, err := stub.GetHistoryForKey(ID)
	if err != nil {
    
    
		return shim.Error("根据指定的身份证号码查询对应的历史变更数据失败")
	}
	defer iterator.Close()
	// 迭代处理
	var historys []HistoryItem
	var hisGenerate GenerateCrop
	for iterator.HasNext() {
    
    
		hisData, err := iterator.Next()
		if err != nil {
    
    
			return shim.Error("获取edu的历史变更数据失败")
		}
		var historyItem HistoryItem
		historyItem.TxId = hisData.TxId
		err = json.Unmarshal(hisData.Value, &hisGenerate)
		if err != nil {
    
    
			shim.Error("failed to Unmarshal in QueryDataByID with GetGistoryForKey")
		}
		if hisData.Value == nil {
    
    
			var empty GenerateCrop
			historyItem.Generate = empty
		} else {
    
    
			historyItem.Generate = hisGenerate
		}

		historys = append(historys, historyItem)

	}

	UnmarResult.History = historys

	// 返回
	result, err := json.Marshal(UnmarResult)
	if err != nil {
    
    
		return shim.Error("序列化edu信息时发生错误")
	}
	return shim.Success(result)
}

3. バックエンドサーバーのインターフェース

3.1 主な機能

  1. この関数は最初に FabricNetworkInit() 関数を呼び出して、SDK、リソース管理クライアントを作成し、チャネルを作成して組織をチャネルに参加させ、チェーン コードをインストールして初期化します。
  2. Gin フレームワークを使用してルーティング グループを構築し、CorsMiddle.CorsMiddleware() を使用して Cors クロスドメイン問題を解決します。
  3. 保存するデータにアクセスし127.0.0.1:6060/generate/addDataて渡すには、AddData() 関数を呼び出してデータをブロックチェーンに保存します。
  4. 127.0.0.1:6060/generate/queryDataクエリ対象のデータの Key 値にアクセスして渡すと、QueryData() 関数が実行され、ブロックチェーン内のデータがクエリされ、クエリ結果が返されます
func main() {
    
    

	//1. init fabric network
	err := FabricNetworkInit()
	defer Manage.Sdk.Close()
	if err != nil {
    
    
		fmt.Println(err.Error())
	}
	r := gin.Default()
	r.Use(CorsMiddle.CorsMiddleware())
	//2. insert data
	r.POST("generate/addData", AddData)
	//3. query data
	r.GET("generate/queryData", QueryData)
	r.Run(":6060")
	

}

3.2 FabricNetworkInit() 関数

チェーンコードを呼び出す前に、Sdk の作成、リソース管理クライアントの作成、チャネルの作成などの初期化操作を実行します。

func FabricNetworkInit() error {
    
    
	//1. 创建SDK
	Manage.Sdk, _ = SetupSDK(Sdkinfo.ConfigFilePath)
	//2. 创建资源管理客户端。(可用该客户端创建通道,安装链码等操作)
	Manage.Sc, err = SetupResmg(Manage.Sdk, Sdkinfo)
	if err != nil {
    
    
		return err
	}
	//3. 查询节点ccinfo.Peer是否已加入通道sdkinfo.ChannelID,没加入则创建通道
	queryChannelResult, err := QuerySavedChannel(Manage.Sc, Sdkinfo, Ccinfo)
	if err != nil {
    
    
		return err
	}
	if !queryChannelResult {
    
    
		err = CreateChannel(Manage.Sdk, Manage.Sc, Sdkinfo)
		if err != nil {
    
    
			return err
		}
	} else {
    
    
		fmt.Printf("channel '%v' already exist\n", Sdkinfo.ChannelID)
	}

	//4. 查询链码ccinfo.CCID在节点ccinfo.Peer上是否已安装,没安装则安装并初始化
	queryResult, err := QueryInstalledCC(Manage.Sc, Ccinfo)
	if err != nil {
    
    
		fmt.Println(err.Error())
		return err
	}
	if !queryResult {
    
    

		err = InstallChaincode(Manage.Sc, Ccinfo)
		if err != nil {
    
    
			return fmt.Errorf("install chaincode failed: %v", err)
		}
		err = ChaincodeInit(Manage.Sc, Ccinfo)
		if err != nil {
    
    
			return fmt.Errorf("init chaincode failed: %v", err)
		}
	} else {
    
    
		fmt.Printf("chaincode with name '%v' already exists\n", Ccinfo.CCID)
	}

	//5. 创建通道管理客户端。(可用该客户端调用链码)
	Manage.Cc, err = SetupChannelMg(Manage.Sdk, Ccinfo.ChannelID, Sdkinfo.User, Sdkinfo.OrgName)
	if err != nil {
    
    
		return fmt.Errorf("failed to Create channel client: %v", err)
	}
	return nil

}

3.3 AddData() 関数

この関数はJSON型データを受け取り、1.6の関数(InvokeCC(…))を呼び出してブロックチェーン台帳にデータを格納します。

func AddData(c *gin.Context) {
    
    
	chick, err := json.Marshal(ChickData)
	if err != nil {
    
    
		c.JSON(400, gin.H{
    
    
			"err": err.Error(),
		})
	}
	Ccinfo.InvokeArgs = [][]byte{
    
    chick}
	_, err = InvokeCC(Manage.Cc, Ccinfo)
	if err != nil {
    
    
		c.JSON(400, gin.H{
    
    
			"err": err.Error(),
		})
	}
	c.JSON(http.StatusOK, gin.H{
    
    
		"err": nil,
	})
}

3.4 QueryData() 関数

この関数は、クエリ対象のデータのキー値 (ID) を受け取り、1.7 のクエリ チェーンコード関数 ( QueryCC(…) ) を呼び出し、データをクエリして、データを返します。

func QueryData(c *gin.Context) {
    
    
	Ccinfo.QueryArgs = [][]byte{
    
    []byte(ChickData.ID)}
	data, err := QueryCC(Manage.Cc, Ccinfo)
	if err != nil {
    
    
		c.JSON(400, gin.H{
    
    
			"err":  err.Error(),
			"data": nil,
		})
	}
	var d GenerateCrop
	err = json.Unmarshal(data, &d)
	if err != nil {
    
    
		c.JSON(400, gin.H{
    
    
			"err":  err.Error(),
			"data": nil,
		})
	}
	c.JSON(http.StatusOK, gin.H{
    
    
		"err":  nil,
		"data": d,
	})

}

おすすめ

転載: blog.csdn.net/sunningzhzh/article/details/123965778