超级账本hyperledger fabric第十四集:外部服务SDK

外部服务分析:

  • 如何提供外部服务
    1. 企业内部各种数据接口,rpc  grpc
    2. 网站(web),手机(app),通过http(案例中使用的)
    3. 智能硬件,socket服务
  • SDK提供外部服务,SDK的语言选择
    1. node.js(官方推荐,效率9颗星)
    2. java(实际使用量最大的9颗星)
    3. python(使用不太多 3颗星)
    4. golang(不稳定,常用方法也没有 1颗星)
  • SDK的模块
    1. 区块链管理:例如通道的创建和加入、链码的安装、实例化等
    2. 数据查询:区块和交易的查询
    3. 区块链交互(链码交互):发起交易  invoke  query
    4. 事件监听:业务事件、系统事件

SDK的下载和使用:

这里使用测试3的版本

  • goland中创建application目录,首先将config.yaml拷贝到application目录,config.yaml文件是关于MSP、orederer、peer节点的配置信息
version: 1.0.0

#
# The client section used by GO SDK.
#
client:
  organization: org1
  logging:
    level: debug
  eventService:
    type: eventhub
  cryptoconfig:
    #修改路径如下
    path: ${GOPATH}/src/fabric_asset/deploy/crypto-config

  credentialStore:
    path: "/tmp/state-store"

    cryptoStore:
      path: /tmp/msp

  #密码学相关配置
  BCCSP:
    security:
     enabled: true
     default:
      provider: "SW"
     hashAlgorithm: "SHA2"
     softVerify: true
     level: 256

  tlsCerts:
    systemCertPool: true

    client:
      key:
        path: ${GOPATH}/src/github.com/hyperledger/fabric-sdk-go/test/fixtures/config/mutual_tls/client_sdk_go-key.pem
      cert:
        path: ${GOPATH}/src/github.com/hyperledger/fabric-sdk-go/test/fixtures/config/mutual_tls/client_sdk_go.pem

channels:
  assetschannel:
    orderers:
      - orderer.example.com

    peers:
      peer0.org1.example.com:
        endorsingPeer: true
        chaincodeQuery: true
        ledgerQuery: true
        eventSource: true

      peer1.org1.example.com:
        endorsingPeer: true
        chaincodeQuery: true
        ledgerQuery: true
        eventSource: true

    policies:
      queryChannelConfig:
        minResponses: 1
        maxTargets: 1
        retryOpts:
          attempts: 5
          initialBackoff: 500ms
          maxBackoff: 5s
          backoffFactor: 2.0


organizations:
  org1:
    mspid: Org1MSP

    cryptoPath:  peerOrganizations/org1.example.com/users/{username}@org1.example.com/msp

    peers:
      - peer0.org1.example.com

    certificateAuthorities:

  ordererorg:
      mspID: "OrdererMSP"

      cryptoPath: ordererOrganizations/example.com/users/{username}@example.com/msp


orderers:
  orderer.example.com:
    #生产环境下需要修改
    url: 127.0.0.1:7050
    grpcOptions:
      ssl-target-name-override: orderer.example.com
      keep-alive-time: 0s
      keep-alive-timeout: 20s
      keep-alive-permit: false
      fail-fast: false
      allow-insecure: true

    tlsCACerts:
      path: ${GOPATH}/src/github.com/hyperledger/fabric-sdk-go/${CRYPTOCONFIG_FIXTURES_PATH}/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem

peers:
  peer0.org1.example.com:
    url: 127.0.0.1:27051
    eventUrl: 127.0.0.1:27053

    grpcOptions:
      ssl-target-name-override: peer0.org1.example.com
      keep-alive-time: 0s
      keep-alive-timeout: 20s
      keep-alive-permit: false
      fail-fast: false
      allow-insecure: true

    tlsCACerts:
      path: ${GOPATH}/src/github.com/hyperledger/fabric-sdk-go/${CRYPTOCONFIG_FIXTURES_PATH}/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem


  • 该目录下,编写main.go,也就是http访问的代码
package main

import (
	"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
	"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
	"github.com/gin-gonic/gin"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger"
	"fmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
	"golang.org/x/net/context"
	"time"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/event"
	"net/http"
	"bytes"
)

//定义SDK、channel等全局变量
var (
	sdk *fabsdk.FabricSDK
	//通道名称
	channelName = "assetschannel"
	//链码名称
	chaincodeName = "assets"
	//组织
	org = "org1"
	//用户
	user = "Admin"
	//配置文件位置,是当前位置下的配置
	configPath = "./config.yaml"
)

//初始化SDK
func init() {
	var err error
	sdk, err = fabsdk.New(config.FromFile(configPath))
	if err != nil {
		panic(err)
	}
}

func main() {
	//使用http服务
	//使用了gin框架,是go-web框架
	router := gin.Default()
	//定义http服务的路由
	{
		//注册用户
		router.POST("/users", userRegister)
		//查询用户,根据id
		router.GET("/users/:id", queryUser)
		//删除用户,根据id
		router.DELETE("/users/:id", deleteUser)
		//查询资产
		router.GET("/assets/get/:id", queryAsset)
		//资产登记
		router.POST("/assets/enroll", assetsEnroll)
		//资产变更
		router.POST("/assets/exchange", assetsExchange)
		//资产变更历史查询
		router.GET("/assets/exchange/history", assetsExchangeHistory)
	}
	router.Run()
}

//SDK有4个模块,这里只用到了区块链交互
//区块链管理
//区块链数据查询
//区块链交互
//事件监听

//区块链管理
func manageBolockchain() {
	//表明身份
	//向SDK说明身份
	//获取上下文对象
	ctx := sdk.Context(fabsdk.WithOrg(org), fabsdk.WithUser(user))

	//区块链管理的方法在fabric-sdk-go\pkg\client\resmgmt下
	//创建Client对象
	cli, err := resmgmt.New(ctx)
	if err != nil {
		panic(err)
	}

	//具体操作
	cli.SaveChannel(resmgmt.SaveChannelRequest{},
		resmgmt.WithOrdererEndpoint("orderer.example.com"),
		resmgmt.WithTargetEndpoints())

}

//区块链查询
//在fabric-sdk-go\pkg\client\ledger包下
func queryBlockchain() {
	//获取上下文对象
	ctx := sdk.ChannelContext(channelName, fabsdk.WithOrg(org), fabsdk.WithUser(user))
	//创建Client对象
	cli, err := ledger.New(ctx)
	if err != nil {
		panic(err)
	}

	//查询区块链当前状态
	resp, err := cli.QueryInfo(ledger.WithTargetEndpoints("peer0,org1.example.com"))
	if err != nil {
		panic(err)
	}
	fmt.Println(resp)

	//查询区块
	//方法1:
	cli.QueryBlockByHash(resp.BCI.CurrentBlockHash)
	//方法2:
	for i := uint64(0); i <= resp.BCI.Height; i++ {
		cli.QueryBlock(i)
	}
}

//事件监听
func eventHandle() {
	ctx := sdk.ChannelContext(channelName, fabsdk.WithOrg(org), fabsdk.WithUser(user))
	//创建Client对象
	//源码在fabric-sdk-go\pkg\client\channel下
	cli, err := event.New(ctx)
	if err != nil {
		panic(err)
	}
	//注册区块事件
	reg, blkevent, err := cli.RegisterBlockEvent()
	if err != nil {
		panic(err)
	}
	defer cli.Unregister(reg)
	timeoutctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()
	for {
		select {
		case evt := <-blkevent:
			fmt.Printf("接收了一个块", evt)
		case <-timeoutctx.Done():
			fmt.Println("事件超时")
			return
		}
	}
}

//定义Execute,处理后面的注册、注销、资产相关
func channelExecute(fcn string, args [][]byte) (channel.Response, error) {
	//上下文对象
	ctx := sdk.ChannelContext(channelName, fabsdk.WithOrg(org), fabsdk.WithUser(user))
	//创建Client对象
	//源码在fabric-sdk-go\pkg\client\channel下
	cli, err := channel.New(ctx)
	if err != nil {
		return channel.Response{}, err
	}

	//Execute():更新状态,增删改
	//参数指定了请求内容和交易发送到的节点
	resp, err := cli.Execute(channel.Request{
		ChaincodeID: chaincodeName,
		Fcn:         fcn,
		Args:        args,
	}, channel.WithTargetEndpoints("peer0.org1.example.com"))
	if err != nil {
		return channel.Response{}, err
	}

	//监听
	go func() {
		//使用channel模块的事件监听
		reg, ccevt, err := cli.RegisterChaincodeEvent(chaincodeName, "eventname")
		if err != nil {
			return
		}
		defer cli.UnregisterChaincodeEvent(reg)
		timeoutctx, cancel := context.WithTimeout(context.Background(), time.Minute)
		defer cancel()
		for {
			select {
			case evt := <-ccevt:
				fmt.Printf("接收到事件 %s: %+v", resp.TransactionID, evt)
			case <-timeoutctx.Done():
				fmt.Println("事件超时")
				return
			}
		}
	}()

	//交易状态事件监听
	go func() {
		eventcli, err := event.New(ctx)
		if err != nil {
			return
		}
		//注册交易事件
		reg, status, err := eventcli.RegisterTxStatusEvent(string(resp.TransactionID))
		defer eventcli.Unregister(reg)

		timeoutctx, cancel := context.WithTimeout(context.Background(), time.Minute)
		defer cancel()
		for {
			select {
			case evt := <-status:
				fmt.Printf("接收到事件 %s: %+v", resp.TransactionID, evt)
			case <-timeoutctx.Done():
				fmt.Println("事件超时")
				return
			}
		}
	}()
	return resp, nil
}

//一会儿用于调用的数据查询
func channelQuery(fcn string, args [][]byte) (channel.Response, error) {
	//上下文对象
	ctx := sdk.ChannelContext(channelName, fabsdk.WithOrg(org), fabsdk.WithUser(user))
	//创建Client对象
	//源码在fabric-sdk-go\pkg\client\channel下
	cli, err := channel.New(ctx)
	if err != nil {
		return channel.Response{}, err
	}

	//状态查询
	return cli.Query(channel.Request{
		ChaincodeID: chaincodeName,
		Fcn:         fcn,
		Args:        args,
	}, channel.WithTargetEndpoints("peer0.org1.example.com"))
}

//定义接收的用户相关参数
//在客户端发送时必须存在,否则报错,gin框架
type UserRegisterRequest struct {
	Id   string `form:"id" binding:"required"`
	Name string `form:"name" binding:"required"`
}

//用户注册
func userRegister(ctx *gin.Context) {
	req := new(UserRegisterRequest)
	if err := ctx.ShouldBind(req); err != nil {
		ctx.AbortWithError(400, err)
		return
	}
	resp, err := channelExecute("userRegister", [][]byte{
		[]byte(req.Name),
		[]byte(req.Id),
	})
	if err != nil {
		ctx.String(http.StatusBadGateway, err.Error())
		return
	}
	ctx.JSON(http.StatusOK, resp)
}

//查询用户
func queryUser(ctx *gin.Context) {
	//取到id,根据id去查询
	userId := ctx.Param("id")
	//调用上面定义的查询的方法
	resp, err := channelQuery("queryUser", [][]byte{
		[]byte(userId),
	})
	if err != nil {
		ctx.String(http.StatusBadGateway, err.Error())
		return
	}
	ctx.String(http.StatusOK, bytes.NewBuffer(resp.Payload).String())
}

//用户注销
func deleteUser(ctx *gin.Context) {
	userId := ctx.Param("id")
	resp, err := channelExecute("userDestory", [][]byte{
		[]byte(userId),
	})
	if err != nil {
		ctx.String(http.StatusBadGateway, err.Error())
		return
	}
	ctx.JSON(http.StatusOK, resp)
}

//资产查询
func queryAsset(ctx *gin.Context) {
	assetId := ctx.Param("id")
	resp, err := channelQuery("queryAsset", [][]byte{
		[]byte(assetId),
	})
	if err != nil {
		ctx.String(http.StatusBadGateway, err.Error())
		return
	}
	ctx.String(http.StatusOK, bytes.NewBuffer(resp.Payload).String())
}

//定义接收的资产相关的参数
type AssetsEnrollRequest struct {
	AssetId   string `form:"assetsid" binding:"required"`
	AssetName string `form:"assetname" binding:"required"`
	Metadata  string `form:"metadata" binding:"required"`
	OwnerId   string `form:"ownerid" binding:"required"`
}

//资产登记
func assetsEnroll(ctx *gin.Context) {
	req := new(AssetsEnrollRequest)
	if err := ctx.ShouldBind(req); err != nil {
		ctx.AbortWithError(400, err)
		return
	}

	//调用添加执行的方法
	resp, err := channelExecute("assetEnroll", [][]byte{
		[]byte(req.AssetName),
		[]byte(req.AssetId),
		[]byte(req.Metadata),
		[]byte(req.OwnerId),
	})

	if err != nil {
		ctx.String(http.StatusBadGateway, err.Error())
		return
	}
	ctx.JSON(http.StatusOK, resp)
}

//定义接收的资产变更的相关的参数
type AssetsExchangeRequest struct {
	AssetId        string `form:"assetsid" binding:"required"`
	OriginOwnerId  string `form:"originownerid" binding:"required"`
	CurrentOwnerId string `form:"currentownerid" binding:"required"`
}

//资产转让
func assetsExchange(ctx *gin.Context) {
	req := new(AssetsExchangeRequest)
	//参数绑定
	if err := ctx.ShouldBind(req); err != nil {
		ctx.AbortWithError(400, err)
		return
	}

	resp, err := channelExecute("assetExchange", [][]byte{
		[]byte(req.OriginOwnerId),
		[]byte(req.AssetId),
		[]byte(req.CurrentOwnerId),
	})
	if err != nil {
		ctx.String(http.StatusBadGateway, err.Error())
		return
	}
	ctx.JSON(http.StatusOK, resp)
}

//资产变更历史查询
func assetsExchangeHistory(ctx *gin.Context) {
	assetId := ctx.Query("assetid")
	queryType := ctx.Query("querytype")

	resp, err := channelQuery("queryAssetHistory", [][]byte{
		[]byte(assetId),
		[]byte(queryType),
	})
	if err != nil {
		ctx.String(http.StatusBadGateway, err.Error())
		return
	}
	ctx.JSON(http.StatusOK, resp)
}
  • 编写完成后,将application目录整个拖到linux对应目录下,在application目录下编译

超级账本hyperledger fabric第十四集:外部服务SDK_第1张图片

  • 运行程序,发布在8080端口

超级账本hyperledger fabric第十四集:外部服务SDK_第2张图片

超级账本hyperledger fabric第十四集:外部服务SDK_第3张图片

  • 要保证智能合约没有问题,再去访问,也就是启动网络,创建通道
  • 启动网络

超级账本hyperledger fabric第十四集:外部服务SDK_第4张图片

  • 交互执行:docker exec -it cli bash
  • 创建通道:

peer channel create -o orderer.example.com:7050 -c assetschannel -f /etc/hyperledger/config/assetschannel.tx

  • 加入通道:

peer channel join -b assetschannel.block

  • 安装链码:

peer chaincode install -n assets -v 1.0.0 -l golang -p github.com/chaincode/assetsExchange

  • 实例化链码:

peer chaincode instantiate -o orderer.example.com:7050 -C assetschannel -n assets -l golang -v 1.0.0 -c '{"Args":["init"]}'


SDK的测试:

  • 安装postman,直接关闭注册界面,就进入了

超级账本hyperledger fabric第十四集:外部服务SDK_第5张图片

  • 注册用户测试

超级账本hyperledger fabric第十四集:外部服务SDK_第6张图片

  • 用户查询测试

超级账本hyperledger fabric第十四集:外部服务SDK_第7张图片

  • 资产登记

超级账本hyperledger fabric第十四集:外部服务SDK_第8张图片

  • 再次查询,user6已经有资产了

超级账本hyperledger fabric第十四集:外部服务SDK_第9张图片

  • 单独查资产

超级账本hyperledger fabric第十四集:外部服务SDK_第10张图片

  • 再创建一个用户,用于资产转让测试

超级账本hyperledger fabric第十四集:外部服务SDK_第11张图片

  • 将user6的资产,转给user7

超级账本hyperledger fabric第十四集:外部服务SDK_第12张图片

  • 此时,可以查到,资产6已经转移到user7上

超级账本hyperledger fabric第十四集:外部服务SDK_第13张图片

  • 资产历史变更查询,这里由于转码了,看不到内容

超级账本hyperledger fabric第十四集:外部服务SDK_第14张图片

  • 删除user7

  • 此时,user7没有了

超级账本hyperledger fabric第十四集:外部服务SDK_第15张图片

 

你可能感兴趣的:(超级账本,区块链)