Blockchain house traceability project construction tutorial

Origins

​ In today's production environment, the traceability system is mainly used in the fields of food traceability, imported product traceability, and brand product traceability, because there are often some counterfeit and shoddy products in these fields, which are shoddy, so it is very important to use the blockchain to realize product traceability. Extra important.

​The problem that the case we are going to solve: We can solve some renting chaos in real life through the house traceability project, so that users can have a general understanding of the house they will rent before signing the rental contract. Of course, our small project is only a demo level after all, with limited functions. We aim to use it to learn our fabric.

Program Architecture Design

As shown below:

Program frame diagram - 1543517351099

​ The APP_Service of this project is implemented using the beego framework, and calls the native Fabric-SDK-Go to complete the purpose of accessing the ledger data.

​ Before the business of the program starts, we need to complete the construction of the Fabric_Services environment, and then use the SDK to upload the chain code we have written to the Fabric environment we configured earlier.

Fabric-Services environment preparation

Install the software environment

This project is deployed in a Linux environment:

1、安装git

2. Install golang

3. Install docker

​ 4、docker-compose

Write a configuration file

agreement

Three organizations are set up in this project: the Land Management Bureau, the Housing Management Bureau, and the rental platform

Number of peer nodes in each organization: 2

Each organization corresponds to a separate channel: tgjchannel , fgjchannel , zfptchannel

The three organizations have a common channel: unionchannel

Use the root domain name: itcast.cn

1. crypto-config.yaml

switch working directory

$ cd $GOPATH/origin_fabric_servic

Use the following command to generate a file template

cryptogen showtemplate > crypto-config.yaml

Modify the content according to the actual definition, the final result is:

OrdererOrgs:
  - Name: Orderer
    Domain: itcast.cn

    Specs:
      - Hostname: orderer

PeerOrgs:
    # 房管局组织
  - Name: Ofgj
    Domain: ofgj.itcast.cn
    # 是否支持 Nodejs
    EnableNodeOUs: true
    # 组织下面peer节点的个数
    Template:
      Count: 2
    # 创建的普通用户的个数
    Users:
      Count: 1

    # 土管局组织
  - Name: Otgj
    Domain: otgj.itcast.cn
    # 是否支持 Nodejs
    EnableNodeOUs: true
    # 组织下面peer节点的个数
    Template:
      Count: 2
    # 创建的普通用户的个数
    Users:
      Count: 1

    # 租房平台组织
  - Name: Ozfpt
    Domain: ozfpt.itcast.cn
    # 是否支持 Nodejs
    EnableNodeOUs: true
    # 组织下面peer节点的个数
    Template:
      Count: 2
    # 创建的普通用户的个数
    Users:
      Count: 1
  • Generate a certificate directory under the crypto-config directory:
$ cryptogen generate --config=./crypto-config.yaml

2. configtx.yaml - genesis block/channel file

Get the template from the fabric configuration file example

$ cp $GOPATH/src/github.com/hyperledger-fabric/fabric-samples/first-network/configtx.yaml ./

Modified color content:

Profiles:
  ThreeOrgsOrdererGenesis:
    Capabilities:
      <<: *ChannelCapabilities
    Orderer:
      <<: *OrdererDefaults
      Organizations:
        - *Orderer
      Capabilities:
        <<: *OrdererCapabilities
	# 联盟
    Consortiums:
      # 三个组织的联盟
      ThreeOrgsConsortium:   # 名字可改
        Organizations:
          - *Ofgj
          - *Otgj
          - *Ozfpt
      # 房管局私有链
      OfgjConsortium:
        Organizations:
          - *Ofgj
      # 土改局私有链
      OtgjConsortium:
        Organizations:
          - *Otgj
      #租房平台私有链
      OzfptConsortium:
        Organizations:
          - *Ozfpt
     
  # 生成 unionchannel
  unionOrgschannel:
    Consortium: ThreeOrgsConsortium
    Application:
      <<: *ApplicationDefaults
      Organizations:
        - *Ofgj
        - *Otgj
        - *Ozfpt
      Capabilities:
        <<: *ApplicationCapabilities
   #生成 fgjchannel
  fgjOrgchannel:
   	Consortium:OfgjConsortium
   	Application:
      <<: *ApplicationDefaults
      Organizations:
        - *Ofgj
      Capabilities:
        <<: *ApplicationCapabilities
        
    #生成 tgjchannel
  tgjOrgchannel:
   	Consortium:OtgjConsortium
   	Application:
      <<: *ApplicationDefaults
      Organizations:
        - *Otgj
      Capabilities:
        <<: *ApplicationCapabilities
        
     #生成 zfptchannel
  zfptOrgchannel:
   	Consortium:OzfptConsortium
   	Application:
      <<: *ApplicationDefaults
      Organizations:
        - *Ozfpt
      Capabilities:
        <<: *ApplicationCapabilities
        
Organizations:				# 固定的,不能改
  - &Orderer				# 相当于定义了一个变量,其他地方可以引用它
    Name: Orderer			# 排序节点的组织名
    ID: itcast.cn			# 排序节点组织的ID
    MSPDir: crypto-config/ordererOrganizations/itcast.cn/msp # 组织的 MSP 账号

  - &Ofgj					# 房管局组织
    Name: OfgjMSP			# 组织名称
    ID: ofgj.itcast.cn		# 组织ID,该属性在设置背书策略时需要使用
    MSPDir: crypto-config/peerOrganizations/ofgj.itcast.cn/msp	#组织的 MSP 账号
    AnchorPeers:			# 锚节点
      - Host: peer0.ofgj.itcast.cn
        Port: 7051

  - &Otgj
    Name: OtgjMSP
    ID: otgj.itcast.cn
    MSPDir: crypto-config/peerOrganizations/otgj.itcast.cn/msp
    AnchorPeers:
    - Host: peer0.otgj.itcast.cn
      Port: 7051

  - &Ozfpt
    Name: OzfptMSP
    ID: ozfpt.itcast.cn
    MSPDir: crypto-config/peerOrganizations/ozfpt.itcast.cn/msp
    AnchorPeers:
    - Host: peer0.ozfpt.itcast.cn
      Port: 7051

Orderer: &OrdererDefaults
  # 共识机制 == 排序算法
  OrdererType: solo	  # 共识机制 - solo 算法之支持一个排序节点
  Addresses:          # orderer 节点的网络位置
    - orderer.itcast.cn:7050
  BatchTimeout: 5s    # 产生一个区块的时间单位
  BatchSize:
    MaxMessageCount: 10		# 交易的最大数量,交易数量达到之后会产生一个区块
    AbsoluteMaxBytes: 98 MB # 数据量达到该制定的值,也会产生一个区块
    PreferredMaxBytes: 512 KB # 首选 MaxBytes 
  
  Kafka:			  # 本例中的排序算法指定的是solo,此项可以不指定
    Brokers:
      - 127.0.0.1:9092   	# 指定 Kafka 排序集群所在的网络位置
  MaxChannels: 0
  Organizations:

# 此处不需要修改,默认即可
Application: &ApplicationDefaults
  Organizations:
 
# Fabric_v1.1 之后的内容,全部设为true,是为了兼容 1.1 前面的版本
Capabilities:
    Global: &ChannelCapabilities
        V1_1: true
    Orderer: &OrdererCapabilities
        V1_1: true
    Application: &ApplicationCapabilities
        V1_1: true
  • Execute the command to generate the file:

    • Generate genesis block file

      $ mkdir channel-artifacts
      $ configtxgen -profile ThreeOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
      
    • Generate channel file

      # 生成 unionchannel 文件
      $ echo "---------------- Create unionchannel.tx file BEGIN -------------------"
      $ configtxgen -profile UnionOrgsChannel -outputCreateChannelTx ./channel-artifacts/unionchannel.tx -channelID unionchannel
      $ echo "---------------- Create unionchannel.tx file END -------------------"
      
      # 生成 fgjchannel
      $ echo "---------------- Create fgjchannel.tx file BEGIN -------------------"
      $ configtxgen -profile fgjOrgchannel -outputCreateChannelTx ./channel-artifacts/fgjchannel.tx -channelID fgjchannel
      $ echo "---------------- Create fgjchannel.tx file END -------------------"
      
      # 生成 tgjchannel
      $ echo "---------------- Create tgjchannel.tx file BEGIN -------------------"
      $ configtxgen -profile tgjOrgchannel -outputCreateChannelTx ./channel-artifacts/tgjchannel.tx -channelID tgjchannel
      $ echo "---------------- Create tgjchannel.tx file END -------------------"
      
      # 生成 tgjchannel
      $ echo "---------------- Create zfptchannel.tx file BEGIN -------------------"
      $ configtxgen -profile zfptOrgchannel -outputCreateChannelTx ./channel-artifacts/zfptchannel.tx -channelID zfptchannel
      $ echo "---------------- Create zfptchannel.tx file END -------------------"
      
    • Generate anchor node update file

      This action is optional

      $ echo "---------------- Create AnchorPeerUpdate.tx file BEGIN -----------------"
      $ configtxgen -profile UnionOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/OfgjMSPanchors.tx -channelID unionchannel -asOrg OfgjMSP
      $ configtxgen -profile UnionOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/OtgjMSPanchors.tx -channelID unionchannel -asOrg OtgjMSP
      $ configtxgen -profile UnionOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/OzfptMSPanchors.tx -channelID unionchannel -asOrg OzfptMSP
      $ echo "---------------- Create AnchorPeerUpdate.tx file BEGIN ------------------"
      

    After the above command is executed, check the generated results. If you ask about it, it can be generated successfully, indicating that the above operations are all right:

    channel-artifacts/
    ├── fgjchannel.tx
    ├── OfgjMSPanchors.tx
    ├── OtgjMSPanchors.tx
    ├── OzfptMSPanchors.tx
    ├── unionchannel.tx
    └── zfptchannel.tx
    

3. docker-compose.yaml file

Copy the template file, and then modify it based on the go template file

$ cp $GOPATH/src/github.com/hyperledger-fabric/fabric-samples/first-network/docker-compose-cli.yaml

Modified result:

version: '2'

networks:
  ZFW_suyuan:

services:
  orderer.itcast.cn:
    image: hyperledger/fabric-orderer:latest
    container_name: orderer.itcast.cn
    environment:
      - ORDERER_GENERAL_LOGLEVEL=debug			# 日志级别
      - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 	# orderer节点监听的地址
      - ORDERER_GENERAL_LISTENPORT=7050			# orderer 默认监听7050,监听的端口号可以修改
      - ORDERER_GENERAL_GENESISPROFILE=Orderer	
      - ORDERER_GENERAL_GENESISMETHOD=file		# 创世块的来源,file表示来自于文件 
      # 指定创世块文件的路径
      - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block 
      - ORDERER_GENERAL_LOCALMSPID=itcast.cn	# Orderer组织的ID[Organizations -> Orderer -> ID]
      # 当前节点的 MSP 账号路径
      - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp 
      - ORDERER_GENERAL_TLS_ENABLED=true		# 通信的时候是否使用 TLS 加密 
      - ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key		# 私钥文件
      - ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt		# 证书文件
      - ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt]			# 根证书文件  

    working_dir: /opt/gopath/src/github.com/hyperledger/fabric
    command: orderer
    volumes:
      - ./channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
      - ./crypto-config/ordererOrganizations/itcast.cn/orderers/orderer.itcast.cn/msp:/var/hyperledger/orderer/msp
      - ./crypto-config/ordererOrganizations/itcast.cn/orderers/orderer.itcast.cn/tls:/var/hyperledger/orderer/tls
    ports:
      - 7050:7050
    networks:
      default:
        aliases:
          - orderer.itcast.cn

  # ofgj
  peer0.ofgj.itcast.cn:
    image: hyperledger/fabric-peer:latest
    container_name: peer0.ofgj.itcast.cn
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_PEER_ID=peer0.ofgj.itcast.cn			# peer 节点的名字
      - CORE_PEER_ADDRESS=peer0.ofgj.itcast.cn:7051 # 当前 peer 节点的地址信息
      - CORE_VM_DOCKER_ATTACHSTDOUT=true
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_ADDRESSAUTODETECT=true
      # 为了别其他节点感知到,如果不设置,其他节点不知道该节点的存在
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.ofgj.itcast.cn:7051
      - CORE_PEER_GOSSIP_USELEADERELECTION=true		# leader 节点自动选举机制
      - CORE_PEER_GOSSIP_ORGLEADER=false			# 当前节点是不是 leader 节点,结果应由选举得出所以此处给出 false
      - CORE_PEER_GOSSIP_SKIPHANDSHAKE=true
      # 当前组织的ID[Organizations -> ID]
      - CORE_PEER_LOCALMSPID=ofgj.itcast.cn
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_MSPCONFIGPATH=/var/hyperledger/msp
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_SERVERHOSTOVERRIDE=peer0.ofgj.itcast.cn
      - CORE_PEER_TLS_CERT_FILE=/var/hyperledger/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/var/hyperledger/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/tls/ca.crt
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start
    volumes:
      - /var/run/:/host/var/run/
      - ./crypto-config/peerOrganizations/ofgj.itcast.cn/peers/peer0.ofgj.itcast.cn/msp:/var/hyperledger/msp
      - ./crypto-config/peerOrganizations/ofgj.itcast.cn/peers/peer0.ofgj.itcast.cn/tls:/var/hyperledger/tls
    ports:
      - 7051:7051
      - 7053:7053
    depends_on:
      - orderer.itcast.cn
    links:
      - orderer.itcast.cn
    networks:
      default:
        aliases:
          - peer0.ofgj.itcast.cn

  peer1.ofgj.itcast.cn:
    image: hyperledger/fabric-peer:latest
    container_name: peer1.ofgj.itcast.cn
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_VM_DOCKER_ATTACHSTDOUT=true
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/var/hyperledger/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/var/hyperledger/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/tls/ca.crt
      - CORE_PEER_ID=peer1.ofgj.itcast.cn
      - CORE_PEER_ADDRESSAUTODETECT=true
      - CORE_PEER_ADDRESS=peer1.ofgj.itcast.cn:7051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.ofgj.itcast.cn:7051
      - CORE_PEER_GOSSIP_USELEADERELECTION=true
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_GOSSIP_SKIPHANDSHAKE=true
      - CORE_PEER_LOCALMSPID=ofgj.itcast.cn
      - CORE_PEER_MSPCONFIGPATH=/var/hyperledger/msp
      - CORE_PEER_TLS_SERVERHOSTOVERRIDE=peer1.ofgj.itcast.cn
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start
    volumes:
      - /var/run/:/host/var/run/
      - ./crypto-config/peerOrganizations/ofgj.itcast.cn/peers/peer1.ofgj.itcast.cn/msp:/var/hyperledger/msp
      - ./crypto-config/peerOrganizations/ofgj.itcast.cn/peers/peer1.ofgj.itcast.cn/tls:/var/hyperledger/tls
    ports:
      - 8051:7051
      - 8053:7053
    depends_on:
      - orderer.itcast.cn
    links:
      - orderer.itcast.cn
    networks:
      default:
        aliases:
          - peer1.ofgj.itcast.cn

  # otgj
  peer0.otgj.itcast.cn:
    image: hyperledger/fabric-peer:latest
    container_name: peer0.otgj.itcast.cn
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_VM_DOCKER_ATTACHSTDOUT=true
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/var/hyperledger/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/var/hyperledger/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/tls/ca.crt
      - CORE_PEER_ID=peer0.otgj.itcast.cn
      - CORE_PEER_ADDRESSAUTODETECT=true
      - CORE_PEER_ADDRESS=peer0.otgj.itcast.cn:7051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.otgj.itcast.cn:7051
      - CORE_PEER_GOSSIP_USELEADERELECTION=true
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_GOSSIP_SKIPHANDSHAKE=true
      - CORE_PEER_LOCALMSPID=otgj.itcast.cn
      - CORE_PEER_MSPCONFIGPATH=/var/hyperledger/msp
      - CORE_PEER_TLS_SERVERHOSTOVERRIDE=peer0.otgj.itcast.cn
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start
    volumes:
      - /var/run/:/host/var/run/
      - ./crypto-config/peerOrganizations/otgj.itcast.cn/peers/peer0.otgj.itcast.cn/msp:/var/hyperledger/msp
      - ./crypto-config/peerOrganizations/otgj.itcast.cn/peers/peer0.otgj.itcast.cn/tls:/var/hyperledger/tls
    ports:
      - 9051:7051
      - 9053:7053
    depends_on:
      - orderer.itcast.cn
    links:
      - orderer.itcast.cn
    networks:
      default:
        aliases:
          - peer0.otgj.itcast.cn

  peer1.otgj.itcast.cn:
    image: hyperledger/fabric-peer:latest
    container_name: peer1.otgj.itcast.cn
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_VM_DOCKER_ATTACHSTDOUT=true
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/var/hyperledger/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/var/hyperledger/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/tls/ca.crt
      - CORE_PEER_ID=peer1.otgj.itcast.cn
      - CORE_PEER_ADDRESSAUTODETECT=true
      - CORE_PEER_ADDRESS=peer1.otgj.itcast.cn:7051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.otgj.itcast.cn:7051
      - CORE_PEER_GOSSIP_USELEADERELECTION=true
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_GOSSIP_SKIPHANDSHAKE=true
      - CORE_PEER_LOCALMSPID=otgj.itcast.cn
      - CORE_PEER_MSPCONFIGPATH=/var/hyperledger/msp
      - CORE_PEER_TLS_SERVERHOSTOVERRIDE=peer1.otgj.itcast.cn
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start
    volumes:
      - /var/run/:/host/var/run/
      - ./crypto-config/peerOrganizations/otgj.itcast.cn/peers/peer1.otgj.itcast.cn/msp:/var/hyperledger/msp
      - ./crypto-config/peerOrganizations/otgj.itcast.cn/peers/peer1.otgj.itcast.cn/tls:/var/hyperledger/tls
    ports:
      - 10051:7051
      - 10053:7053
    depends_on:
      - orderer.itcast.cn
    links:
      - orderer.itcast.cn
    networks:
      default:
        aliases:
          - peer1.otgj.itcast.cn

  # ozfpt
  peer0.ozfpt.itcast.cn:
    image: hyperledger/fabric-peer:latest
    container_name: peer0.ozfpt.itcast.cn
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_VM_DOCKER_ATTACHSTDOUT=true
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/var/hyperledger/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/var/hyperledger/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/tls/ca.crt
      - CORE_PEER_ID=peer0.ozfpt.itcast.cn
      - CORE_PEER_ADDRESSAUTODETECT=true
      - CORE_PEER_ADDRESS=peer0.ozfpt.itcast.cn:7051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.ozfpt.itcast.cn:7051
      - CORE_PEER_GOSSIP_USELEADERELECTION=true
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_GOSSIP_SKIPHANDSHAKE=true
      - CORE_PEER_LOCALMSPID=ozfpt.itcast.cn
      - CORE_PEER_MSPCONFIGPATH=/var/hyperledger/msp
      - CORE_PEER_TLS_SERVERHOSTOVERRIDE=peer0.ozfpt.itcast.cn
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start
    volumes:
      - /var/run/:/host/var/run/
      - ./crypto-config/peerOrganizations/ozfpt.itcast.cn/peers/peer0.ozfpt.itcast.cn/msp:/var/hyperledger/msp
      - ./crypto-config/peerOrganizations/ozfpt.itcast.cn/peers/peer0.ozfpt.itcast.cn/tls:/var/hyperledger/tls
    ports:
      - 11051:7051
      - 11053:7053
    depends_on:
      - orderer.itcast.cn
    links:
      - orderer.itcast.cn
    networks:
      default:
        aliases:
          - peer0.ozfpt.itcast.cn

  peer1.ozfpt.itcast.cn:
    image: hyperledger/fabric-peer:latest
    container_name: peer1.ozfpt.itcast.cn
    environment:
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_VM_DOCKER_ATTACHSTDOUT=true
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_TLS_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/var/hyperledger/tls/server.crt
      - CORE_PEER_TLS_KEY_FILE=/var/hyperledger/tls/server.key
      - CORE_PEER_TLS_ROOTCERT_FILE=/var/hyperledger/tls/ca.crt
      - CORE_PEER_ID=peer1.ozfpt.itcast.cn
      - CORE_PEER_ADDRESSAUTODETECT=true
      - CORE_PEER_ADDRESS=peer1.ozfpt.itcast.cn:7051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.ozfpt.itcast.cn:7051
      - CORE_PEER_GOSSIP_USELEADERELECTION=true
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_GOSSIP_SKIPHANDSHAKE=true
      - CORE_PEER_LOCALMSPID=ozfpt.itcast.cn
      - CORE_PEER_MSPCONFIGPATH=/var/hyperledger/msp
      - CORE_PEER_TLS_SERVERHOSTOVERRIDE=peer1.ozfpt.itcast.cn
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start
    volumes:
      - /var/run/:/host/var/run/
      - ./crypto-config/peerOrganizations/ozfpt.itcast.cn/peers/peer1.ozfpt.itcast.cn/msp:/var/hyperledger/msp
      - ./crypto-config/peerOrganizations/ozfpt.itcast.cn/peers/peer1.ozfpt.itcast.cn/tls:/var/hyperledger/tls
    ports:
      - 12051:7051
      - 12053:7053
    depends_on:
      - orderer.itcast.cn
    links:
      - orderer.itcast.cn
    networks:
      default:
        aliases:
          - peer1.ozfpt.itcast.cn

4. Start the network

Since the name of the configuration file used is docker-compose.yaml, which is the default value of docker-compose, the following command can be used to start the network:

  • $ docker-compose up -d
    

In this example, we have 1 orderer node and 6 peer nodes, so if all 7 docker containers can be started successfully, then our fabric network environment has been started successfully, as shown in the following figure:

$ docker-compose ps

Verify that the docker container is started successfully

So far, the entire fabric network environment has been running. When we were learning Fabric, when we started the docker container, we started a cli container at the same time. It can use the CLI tool to complete some operations of channel and chaincode, as follows:

  • Create channels, done by client nodes

  • Add each node of each organization to the channel -> client to complete

    • A client can only connect to one peer node at the same time
  • Install smart contract -> chain code for each peer node (program: go, node.js, java)

  • Initialize the smart contract, corresponding to the Init function in the smart contract

    • It only needs to be initialized once on any node, and the data will be automatically synchronized with each node of each organization
  • Query data -> read

  • call to data -> write

After the previous explanation, we all know that a client can only connect to one specified node. If you want the client to connect to other nodes, you must modify the relevant environment variables in the current client

In this project, we do not use the cli tool, but use Fabric_SDK_go to complete the corresponding work.

APP_Service application development

At the application layer, the first thing we need to do is to design and write the chain code, we need to use the hyperledger/fabric framework. So, first we need to download the fabric source code to the $GOPATH/src/github.com/hyperledger directory

If you have already downloaded it before, this step can be ignored

Next we can write our chaincode. So before that, we need to design our chain code in advance.

Chaincode Design

As shown in the figure below, in the ledger, we must at least save the following data:

1543343702707

As shown in the figure, the listing information includes housing information, community information around the housing, and order information related to the housing listing. These information are stored in our hyperledger by using RentingID as the key, so we need to declare the corresponding structure in our chain code to store the corresponding information.

The corresponding structure declaration is as follows:

  • HouseInfo is used to store some data related to housing information. In this project scenario, this data should be maintained by the "House Management Bureau Organization", so the operation of this data can go through fgjchannel.
// 房屋信息  ofgj
type HouseInfo struct {
    
    
	HouseID    string `json:"house_id"`    // 房产证编号
	HouseOwner string `json:"house_owner"` // 房主
	RegDate    string `json:"reg_date"`    // 登记日期
	HouseArea  string `json:"house_area"`  // 住房面积
	HouseUsed  string `json:"house_used"`  // 房屋设计用途
	IsMortgage string `json:"is_mortgage"` // 是否抵押
}
  • AreaInfo is used to store some community information related to the housing source, through which users can learn some external information, infrastructure and other information that can affect the rental housing source. This data should be maintained by the "Land Management Bureau", so , the operation on the data can go through tgjchannel.
// 社区信息 otgj
type AreaInfo struct {
    
    
	AreaID       string `json:"area_id"`        // 社区编号
	AreaAddress  string `json:"area_address"`   // 房源所在区域
	BasicNetWork string `json:"basic_net_work"` // 区域基础网络编号
	CPoliceName  string `json:"c_police_name"`  // 社区民警姓名
	CPoliceNum   string `json:"c_police_num"`   // 社区民警工号
}
  • OrderInfo is used to store historical rental information related to housing sources, through which users can learn about the previous rent of the housing and other information. This data should be maintained by the "rental platform organization", so the operation of this data can go through zfptchannel.
// 订单信息	ozfpt
type OrderInfo struct {
    
    
	DocHash   string `json:"doc_hash"`   // 电子合同Hash
	OrderID   string `json:"order_id"`   // 订单编号
	RenterID  string `json:"renter_id"`  // 承租人信息
	RentMoney string `json:"rent_money"` // 租金
	BeginDate string `json:"begin_date"` // 开始日期
	EndDate   string `json:"end_date"`   // 结束日期
	Note      string `json:"note"`       // 备注
}

From this, we can get the declaration prototype of the "property information" structure:

// 房源信息
type RentingHouseInfo struct {
    
    
	RentingID        string    `json:"renting_id"`         // 统一编码
	RentingHouseInfo HouseInfo `json:"renting_house_info"` // 房屋信息
	RentingAreaInfo  AreaInfo  `json:"renting_area_info"`  //区域信息
	RentingOrderInfo OrderInfo `json:"renting_order_info"` //订单信息
}

Chaincode implementation

Now that we have cleared some data models that chaincode needs to operate, we can write our chaincode below. Before we start writing chaincode, we need to declare a chaincode structure and overload its Init and Invoke methods, where the Init method parses out the content of the request and invokes different requests. This is our template for writing chaincode.

type HouseChainCode struct {
    
    
   // 房屋溯源链代码
}
// Init 方法
func (this *HouseChainCode) Init(stub shim.ChaincodeStubInterface) peer.Response {
    
    }
// Invoke 方法
func (this *HouseChainCode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    
    
   // 获取 函数名 和 参数列表
   fn, parameters := stub.GetFunctionAndParameters()

   // 通过函数名匹配对应的链代码函数调用
   if fn == "addHouseInfo" {
    
    
   	// 添加房源信息
   	return this.addHouseInfo(stub, parameters)
   } else if fn == "getHouseInfo" {
    
    
   	// 获取房源信息
   	return this.getHouseInfo(stub, parameters)
   } else if fn == "addOrderInfo" {
    
    
   	// 添加订单信息
   	return this.addOrderInfo(stub, parameters)
   } else if fn == "getOrderInfo" {
    
    
   	// 获取订单信息
   	return this.getOrderInfo(stub, parameters)
   } else if fn == "addAreaInfo" {
    
    
   	// 添加社区信息
   	return this.addAreaInfo(stub, parameters)
   } else if fn == "getAreaInfo" {
    
    
   	// 获取社区信息
   	return this.getAreaInfo(stub, parameters)
   }

   // 没有任何函数被匹配到,返回错误消息
   fmt.Println("==== fn = ", fn)
   return shim.Error("Received unknow function invocation")
}

Through the display of the project, we have basically understood the general model of the project. The following piece of code is the implementation of the addHouseCode function. Its main function is to write the house information entered by the user into the hyperbook. It receives user parameters and Write the data into the ledger in the form of rentingID as key and args transfer encoding data as value.

// 添加房屋信息
func (this *HouseChainCode) addHouseInfo(stub shim.ChaincodeStubInterface, args []string) peer.Response {
    
    
	// 定义一个房屋信息结构体
	var HouseInfos RentingHouseInfo

	if len(args) != 7 {
    
    
		return shim.Error("Incorrect number oof arguments.")
	}

	HouseInfos.RentingID = args[0]
	if HouseInfos.RentingID == "" {
    
    
		return shim.Error("RentingId can't be empty.")
	}

	HouseInfos.RentingHouseInfo.HouseID = args[1]
	HouseInfos.RentingHouseInfo.HouseOwner = args[2]
	HouseInfos.RentingHouseInfo.RegDate = args[3]
	HouseInfos.RentingHouseInfo.HouseArea = args[4]
	HouseInfos.RentingHouseInfo.HouseUsed = args[5]
	HouseInfos.RentingHouseInfo.IsMortgage = args[6]

	HouseInfosJsonBytes, err := json.Marshal(HouseInfos)
	if err != nil {
    
    
		return shim.Error(err.Error())
	}

	err = stub.PutState(HouseInfos.RentingID, HouseInfosJsonBytes)
	if err != nil {
    
    
		return shim.Error(err.Error())
	}

	return shim.Success([]byte("ok"))
}

Earlier, we have written the housing information into the blockchain, and the following piece of code is to read out the data just written into the blockchain, so as to achieve the purpose of data reference.

// 查看房屋信息
func (this *HouseChainCode) getHouseInfo(stub shim.ChaincodeStubInterface, args []string) peer.Response {
    
    
	// 判断参数个数
	if len(args) != 1 {
    
    
		return shim.Error("Incorrect number of arguments.")
	}
	// 获取唯一标识码
	rentingID := args[0]

	// 获得查询结果迭代器
	resultItreator, err := stub.GetHistoryForKey(rentingID)
	if err != nil {
    
    
		return shim.Error(err.Error())
	}

	// 释放迭代器资源
	defer resultItreator.Close()

	var rentHouseInfo HouseInfo

	// 使用迭代器遍历查询结果集
	for resultItreator.HasNext() {
    
    
		var HouseInfos RentingHouseInfo
		// 取一条结果
		response, err := resultItreator.Next()
		if err != nil {
    
    
			return shim.Error(err.Error())
		}

		err = json.Unmarshal(response.Value, &HouseInfos)
		if err != nil {
    
    
			return shim.Error(err.Error())
		}

		if HouseInfos.RentingHouseInfo.HouseOwner != "" {
    
    
			rentHouseInfo = HouseInfos.RentingHouseInfo
			continue
		}
	}

	jsonAsBytes, err := json.Marshal(rentHouseInfo)
	if err != nil {
    
    
		return shim.Error(err.Error())
	}

	// 返回数据查询结果
	return shim.Success(jsonAsBytes)
}

The above two ends of the code are only information related to the house, and we can refer to these two to write the other two.

Framework construction of APP_Service

So far, we should have finished the work related to Fabric_Services, and then we need to implement the functions for APP_Services. Our APP_Services uses the beego framework, so the first thing we need to do is to set up our routing, and then implement the corresponding processing function once according to the routing.

1. route configuration

​ According to the Invoke function branch call model in the chain code, the routing design is as follows:

func init() {
    
    
    beego.Router("/", &controllers.MainController{
    
    })
	beego.Router("/form_area",&controllers.MainController{
    
    },"get:FormArea")
	beego.Router("/form_house",&controllers.MainController{
    
    },"get:FormHouse")
	beego.Router("/form_orderer",&controllers.MainController{
    
    },"get:FormOrderer")
	beego.Router("/area_search",&controllers.MainController{
    
    },"get:AreaSearch")
	beego.Router("/house_search",&controllers.MainController{
    
    },"get:HouseSearch" )
	beego.Router("/orderer_search",&controllers.MainController{
    
    },"get:OrdererSearch")
}

2. Controller function prototype

​ According to the designed route, the corresponding function in the controllers module is implemented:

// 发布房源信息
func (this *MainController) FormHouse() {
    
    }
// 查询房源信息
func (this *MainController) HouseSearch() {
    
    }
// 发布社区信息
func (this *MainController) FormArea() {
    
    }
// 查询社区信息
func (this *MainController) AreaSearch() {
    
    }
// 发布订单信息
func (this *MainController) FormOrderer() {
    
    }
// 查询订单信息
func (this *MainController) OrdererSearch() {
    
    }

​ Let’s take “property information” as an example. We need to obtain the input of the front-end user inside the function, and call the business logic of the models layer to complete the entry of the data into the ledger. The specific implementation is as follows:

// 发布房源信息
func (this *MainController) FormHouse() {
    
    
	// 响应 /form_house 请求,将 form_house.html 页面返回给客户端
	this.TplName = "form_house.html"

	rentingID := this.GetString("rentingID")
	if rentingID == "" {
    
    
		return
	} else {
    
    
		// 获取用户输入
		fczbh := this.GetString("fczbh")
		fzxm := this.GetString("fzxm")
		djrq := this.GetString("djrq")
		zfmj := this.GetString("zfmj")
		fwsjyt := this.GetString("fwsjyt")
		sfdy := this.GetString("sfdy")

		// 组织参数
		var args []string
		args = append(args, "addHouseInfo")
		args = append(args, rentingID)
		args = append(args, fczbh)
		args = append(args, fzxm)
		args = append(args, djrq)
		args = append(args, zfmj)
		args = append(args, fwsjyt)
		args = append(args, sfdy)

		//TODO  调用models 层业务逻辑,把数据记入超级账本
	}
	// 最后返回在首页
	this.TplName = "index.html"
}

The above TODO needs to rely on the business logic of the models layer, so next we will write the implementation of the models layer to support the development of the upper layer

3. Write the SDK configuration file

We can refer to $GOPATH/src/github.com/hyperledger/fabric-sdk-go/pkg/core/config/testdata/template/config.yamlthe template file to complete the writing of our SDK configuration file. At the same time, fabric-sdk-go provides us with two demo programs. We can also take their configuration files for reference. These two files are in the following directory.

$GOPATH/src/github.com/hyperledger/fabric-sdk-go/test/fixtures/config/config_test.yaml
$GOPATH/src/github.com/hyperledger/fabric-sdk-go/pkg/core/config/testdata/config_test.yaml 

The relevant configuration is as follows:

# 配置文件的名字,可以不写
name: "suyuan-service-network"

version: 1.0.0

# client 相关配置
client:
  # 此 SDK 实例属于哪个组织
  organization: Ofgj
  # 日志级别
  logging:
    level: info
  # 证书所在目录
  cryptoconfig:
    path: /home/stu/workspace/src/origins/conf/crypto-config
  # 这种方式就是把用户名和密码直接存储在本地的一个文件中,而用户和密码对通过一个别名来引用,这样可以避免密码明文格式可能会存在的安全问题
  credentialStore:
    path: /tmp/suyuan-service-store

  # 区块链密码服务提供者,指定加密策略
  BCCSP:
    security:
      enabled: true
      default:
        provider: "SW"
      hashAlgorithm: "SHA2"
      softVerify: true
      level: 256

  tlsCerts:
  	# 证书池策略,默认为false,提高身份认证速率
    systemCertPool: true
    client:
      keyfile: /home/stu/workspace/src/origins/conf/crypto-config/peerOrganizations/ofgj.itcast.cn/users/[email protected]/tls/client.key
      certfile: /home/stu/workspace/src/origins/conf/crypto-config/peerOrganizations/ofgj.itcast.cn/users/[email protected]/tls/client.crt


# channel 相关配置
channels:
  # channelID
  fgjchannel:
    # orderer 组织必须指定
    orderers:
      - orderer.itcast.cn
	# 添加到该 channel 中的组织的 peer 列表
    peers:
      peer0.ofgj.itcast.cn:
        endorsingPeer: true
        chaincodeQuery: true
        ledgerQuery: true
        eventSource: true

      peer1.ofgj.itcast.cn:
        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 相关配置
organizations:
  ofgj:
    # configtx.yaml organizations -> ID
    mspid: ofgj.itcast.cn

    cryptoPath: /home/stu/workspace/src/origins/conf/crypto-config/peerOrganizations/ofgj.itcast.cn/users/{
    
    userName}@ofgj.itcast.cn/msp
    peers:
    - peer0.ofgj.itcast.cn
    - peer1.ofgj.itcast.cn


  ordererorg:
    mspID: itcast.cn
    cryptoPath: /home/stu/workspace/src/origins/conf/crypto-config/ordererOrganizations/itcast.cn/users/[email protected]/msp

# orderer 相关配置
orderers:
  orderer.itcast.cn:
    url: localhost:7050
    grpcOptions:
      ssl-target-name-override: orderer.itcast.cn
      keep-alive-time: 0s
      keep-alive-timeout: 20s
      keep-alive-permit: false
      fail-fast: false
      allow-insecure: false

    tlsCACerts:
      path: /home/stu/workspace/src/origins/conf/crypto-config/ordererOrganizations/itcast.cn/tlsca/tlsca.itcast.cn-cert.pem

# peer 相关配置
peers:
  peer0.ofgj.itcast.cn:
    url: grpc://localhost:7051
    eventUrl: localhost:7053
    grpcOptions:
      ssl-target-name-override: peer0.ofgj.itcast.cn
      keep-alive-time: 0s
      keep-alive-timeout: 20s
      keep-alive-permit: false
      fail-fast: false
      allow-insecure: false

    tlsCACerts:
      path: /home/stu/workspace/src/origins/conf/crypto-config/peerOrganizations/ofgj.itcast.cn/tlsca/tlsca.ofgj.itcast.cn-cert.pem

  peer1.ofgj.itcast.cn:
    url: localhost:8051
    eventUrl: localhost:8053
    grpcOptions:
      ssl-target-name-override: peer1.ofgj.itcast.cn
      keep-alive-time: 0s
      keep-alive-timeout: 20s
      keep-alive-permit: false
      fail-fast: false
      allow-insecure: false

    tlsCACerts:
      path: /home/stu/workspace/src/origins/conf/crypto-config/peerOrganizations/ofgj.itcast.cn/tlsca/tlsca.ofgj.itcast.cn-cert.pem

4. Model design

We need to use Fabric_SDK to operate at the models layer, so the first thing we need to do is to get the SDK handle, create a channel, add nodes, install the chain code, and instantiate the hand of the chain code before performing data operations. First, we define a structure, which stores some configuration information related to creating Fabric-SDK. The format is as follows:

// FabricSetup implementation
type FabricSetup struct {
    
    
	ConfigFile      string				// 初始化 SDK 对应的配置文件
	OrdererID       string				// orderer节点
	ChannelID       string				// ChannelID
	ChainCodeID     string				// ChainCodeID
	ChannelConfig   string				// 与channel相关的配置文件
	ChaincodeGoPath string				// GOPATH 环境变量
	ChaincodePath   string				// ChainCode 存放路径
	OrgAdmin        string				// Admin 用户名
	OrgName         string				// 组织名称
	UserName        string				// 普通用户名
	eventID         string				// eventID
    
	client          *channel.Client		
	admin           *resmgmt.Client
	sdk             *fabsdk.FabricSDK
	event           *event.Client
}

We create a new SDK_Init.go file, define our structure variable in it, and initialize it:

	fSetup := FabricSetup{
    
    
		// Network parameters
		OrdererID: "orderer.itcast.cn",

		// Channel parameters
		ChannelID:     "fgjchannel",
		ChannelConfig: os.Getenv("GOPATH") + "/src/origins/conf/channel-artifacts/fgjchannel.tx",

		// Chaincode parameters
		ChainCodeID:     "mycc",
		ChaincodeGoPath: os.Getenv("GOPATH"),
		ChaincodePath:   "origins/chaincode",
		OrgAdmin:        "Admin",
		OrgName:         "ofgj",
		ConfigFile:      "conf/config.yaml",

		// User parameters
		UserName: "User1",
		eventID:"eventInvoke",
	}

So far, we have only set up some configuration information related to the SDK, we also need to call the SDK-AP to complete what we said above, create a new setup.go file, and the displayed content is as follows:

package models

import (
	"fmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
	"github.com/hyperledger/fabric-sdk-go/pkg/core/config"
	mspclient "github.com/hyperledger/fabric-sdk-go/pkg/client/msp"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/msp"
	"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
	"github.com/pkg/errors"
)

// SDK 初始化
func (setup *FabricSetup) Initialize() error{
    
    

	// 1> 利用配置文件初始化一个 SDK,我们使用 fabsdk.New 方法
	sdk,err := fabsdk.New(config.FromFile(setup.ConfigFile))
	if err!=nil{
    
    
		return errors.WithMessage(err,"failed to create SDK")
	}
	fmt.Println("SDK created")
	setup.sdk = sdk

	// 2> 利用第一步得到的 SDK ,创建 channel
	// 在创建channel之前我们需要创建一个资源管理客户端
	// 创建上下文
	resourceManagerClientContext := setup.sdk.Context(fabsdk.WithUser(setup.OrgAdmin),fabsdk.WithOrg(setup.OrgName))
	// 创建资源管理客户端
	resMgmtClient,err := resmgmt.New(resourceManagerClientContext)
	if err!=nil{
    
    
		return errors.WithMessage(err,"failed to chreate channel manager client from Admin identity!")
	}
	setup.resMgmtClient = resMgmtClient
	fmt.Println("Resource managerment client created!")


	// 3> 创建一个 MSP 客户端,相当于创建一个组织的句柄
	// 创建 ofgj 组织的 MSP client
	mspClient, err := mspclient.New(sdk.Context(), mspclient.WithOrg(setup.OrgName))
	if err != nil {
    
    
		return errors.WithMessage(err, "failed to create MSP client")
	}
	// 对该组织中的 Admin 用户进行身份签署
	adminIdentity, err := mspClient.GetSigningIdentity(setup.OrgAdmin)
	if err != nil {
    
    
		return errors.WithMessage(err, "failed to get admin signing identity")
	}
	req := resmgmt.SaveChannelRequest{
    
    ChannelID:setup.channelID,ChannelConfigPath:setup.channelConfigPath,SigningIdentities:[]msp.SigningIdentity{
    
    adminIdentity}}

	txID,err := setup.resMgmtClient.SaveChannel(req,resmgmt.WithOrdererEndpoint(setup.OrdererID))
	if err!=nil||txID.TransactionID==""{
    
    
		return errors.WithMessage(err,"Failed to create channel!")
	}
	fmt.Println("Channel Created!")

	// 4> 将 MSP 句柄对应的组织节点加入到先前创建的 channel,默认会讲所有的组织添加到channel中
	err = setup.resMgmtClient.JoinChannel(setup.channelID,resmgmt.WithRetry(retry.DefaultResMgmtOpts),resmgmt.WithOrdererEndpoint(setup.OrdererID))
	if err!=nil{
    
    
		return errors.WithMessage(err,"Failed to join channel!")
	}

	fmt.Println("Joined channel!")

	fmt.Println("Initialization Successful!")

	return nil
}

// chaincode 的安装和实例化
func (setup *FabricSetup) InstallAndInstantiateCC() error {
    
    

	// Create the chaincode package that will be sent to the peers
	ccPkg, err := packager.NewCCPackage(setup.ChaincodePath, setup.ChaincodeGoPath)
	if err != nil {
    
    
		return errors.WithMessage(err, "failed to create chaincode package")
	}
	fmt.Println("ccPkg created")

	// Install example cc to org peers
	installCCReq := resmgmt.InstallCCRequest{
    
    Name: setup.ChainCodeID, Path: setup.ChaincodePath, Version: "0", Package: ccPkg}
	_, err = setup.admin.InstallCC(installCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts))
	if err != nil {
    
    
		return errors.WithMessage(err, "failed to install chaincode")
	}
	fmt.Println("Chaincode installed")

	// Set up chaincode policy
	ccPolicy := cauthdsl.SignedByAnyMember([]string{
    
    "ofgj.itcast.cn"})


	resp, err := setup.admin.InstantiateCC(setup.ChannelID, resmgmt.InstantiateCCRequest{
    
    Name: setup.ChainCodeID, Path: setup.ChaincodeGoPath, Version: "0", Args: [][]byte{
    
    []byte("init")}, Policy: ccPolicy})
	if err != nil || resp.TransactionID == "" {
    
    
		return errors.WithMessage(err, "failed to instantiate the chaincode")
	}
	fmt.Println("Chaincode instantiated")

	// Channel client is used to query and execute transactions
	clientContext := setup.sdk.ChannelContext(setup.ChannelID, fabsdk.WithUser(setup.UserName))
	setup.client, err = channel.New(clientContext)
	if err != nil {
    
    
		return errors.WithMessage(err, "failed to create new channel client")
	}
	fmt.Println("Channel client created")

	// Creation of the client which will enables access to our channel events
	setup.event, err = event.New(clientContext)
	if err != nil {
    
    
		return errors.WithMessage(err, "failed to create new event client")
	}

	fmt.Println("Event client created")

	fmt.Println("Chaincode Installation & Instantiation Successful")
	return nil
}

func (setup *FabricSetup) CloseSDK() {
    
    
	setup.sdk.Close()
}

Now, our chaincode has been installed on Fabric_Service, then we can call the SDK in the models layer to complete the data operation, we also take FormHouse as an example, create a new addHouseItem.go file in the models layer to record the house information Into the blockchain, the implementation is as follows:

func (this *Application)AddHouseItem(args[]string)(string,error){
    
    

	tempDataMap := make(map[string][]byte)
	tempDataMap["result"]=[]byte("Transient data in AddHouseItem")

	reg,_,err := this.FabricSetup.event.RegisterChaincodeEvent(this.FabricSetup.ChainCodeID,this.FabricSetup.eventID)
	if err!=nil{
    
    
		errors.WithMessage(err,"RegisterChaincodeEvent err...")
		return "",err
	}

	defer this.FabricSetup.event.Unregister(reg)

	var tempArgs [][]byte
	for i:=1;i<len(args);i++{
    
    
		tempArgs = append(tempArgs,[]byte(args[i]))
	}

	request := channel.Request{
    
    ChaincodeID:this.FabricSetup.ChainCodeID,Fcn:args[0],Args:[][]byte{
    
    []byte(args[1]),[]byte(args[2]),[]byte(args[3]),[]byte(args[4]),[]byte(args[5]),[]byte(args[6]),[]byte(args[7])},TransientMap:tempDataMap}
	response ,err := this.FabricSetup.client.Execute(request)
	if err!=nil{
    
    
		// 资产转移失败
		return "",err
	}

	return string(response.TransactionID),nil
}

At this time, our FormHouse() function in the controller layer can be written as follows:

// 发布房源信息
func (this *MainController) FormHouse() {
    
    
	// 响应 /form_house 请求,将 form_house.html 页面返回给客户端
	this.TplName = "form_house.html"

	rentingID := this.GetString("rentingID")
	if rentingID == "" {
    
    
		return
	} else {
    
    
		// 获取用户输入
		fczbh := this.GetString("fczbh")
		fzxm := this.GetString("fzxm")
		djrq := this.GetString("djrq")
		zfmj := this.GetString("zfmj")
		fwsjyt := this.GetString("fwsjyt")
		sfdy := this.GetString("sfdy")

		// 组织参数
		var args []string
		args = append(args, "addHouseInfo")
		args = append(args, rentingID)
		args = append(args, fczbh)
		args = append(args, fzxm)
		args = append(args, djrq)
		args = append(args, zfmj)
		args = append(args, fwsjyt)
		args = append(args, sfdy)

		// 调用models 层业务逻辑,把数据记入超级账本
		ret, err := models.App.AddHouseItem(args)
		if err != nil {
    
    
			fmt.Println("AddHouseItem err...")
		}

		fmt.Println("<--- 添加房源信息结果 --->:", ret)
	}
	// 最后返回在首页
	this.TplName = "index.html"
}

args[1]),[]byte(args[2]),[]byte(args[3]),[]byte(args[4]),[]byte(args[5]),[]byte( args[6]),[]byte(args[7])},TransientMap:tempDataMap}
response ,err := this.FabricSetup.client.Execute(request)
if err!=nil{ // Execute the default value return “”; ,err }


return string(response.TransactionID),nil

}


这时我们在 controller 层的 FormHouse() 函数就可以写成如下形式:

```go
// 发布房源信息
func (this *MainController) FormHouse() {
	// 响应 /form_house 请求,将 form_house.html 页面返回给客户端
	this.TplName = "form_house.html"

	rentingID := this.GetString("rentingID")
	if rentingID == "" {
		return
	} else {
		// 获取用户输入
		fczbh := this.GetString("fczbh")
		fzxm := this.GetString("fzxm")
		djrq := this.GetString("djrq")
		zfmj := this.GetString("zfmj")
		fwsjyt := this.GetString("fwsjyt")
		sfdy := this.GetString("sfdy")

		// 组织参数
		var args []string
		args = append(args, "addHouseInfo")
		args = append(args, rentingID)
		args = append(args, fczbh)
		args = append(args, fzxm)
		args = append(args, djrq)
		args = append(args, zfmj)
		args = append(args, fwsjyt)
		args = append(args, sfdy)

		// 调用models 层业务逻辑,把数据记入超级账本
		ret, err := models.App.AddHouseItem(args)
		if err != nil {
			fmt.Println("AddHouseItem err...")
		}

		fmt.Println("<--- 添加房源信息结果 --->:", ret)
	}
	// 最后返回在首页
	this.TplName = "index.html"
}

Now, we have a complete process running through, and we can implement the remaining functions in the same way.

Guess you like

Origin blog.csdn.net/fegus/article/details/124359867
Recommended