×
Community Blog Building a Hyperledger Fabric Network from Scratch

Building a Hyperledger Fabric Network from Scratch

This article describes how to build a Hyperledger Fabric network from scratch on Alibaba Cloud Elastic Compute Service (ECS).

In this article, we will first describe how to build a Hyperledger Fabric network from scratch on Alibaba Cloud Elastic Compute Service (ECS) or Mac. Then, we will look at how to download fabric-sdk-java from IntelliJ IDEA on the local Mac computer and use End2EndIT to perform unit tests on the blockchain deployed on ECS or Mac. Finally, we will look at the whole development process with a real use case.

Install Dependencies for Fabric

Fabric is dependent on cURL (which comes with Linux and Mac terminals by default), Docker, and Go.

Install Docker CE on ECS. For detailed information, see Install Docker on Alibaba Cloud ECS.

yum install epel-release –y
yum clean all
yum list
yum install docker-io –y
systemctl start docker
docker info

Install docker-compose.

sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version

Install Docker on Mac as follows: Run the homebrew command to install Docker (including docker-compose) or directly download and install Docker.

brew install docker

After Docker is installed on Mac, a question mark (?) icon appears on the upper right corner of the Mac desktop.

Install Go on ECS.

yum install go -y

Configure Go environment variables.

export GOPATH=/usr/local/go
export PATH=$PATH:$GOPATH/bin

Install Go on Mac.

brew install go

Configure Go environment variables.

vi /etc/profile
source /etc/profile

Write the following code to the profile:
export GOROOT=/usr/local/Cellar/go/1.10
export GOPATH=/Users/Your user name/go  #All external libraries and code downloaded by Go commands are stored in this directory by default.
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOBIN

Install Fabric

Both ECS and Mac can call scripts, such as Fabric binaries, official sample, and all types of Docker images of Fabric.

curl -sSL https://goo.gl/6wtTN5 | bash -s 1.1.0

Note: If the system does not respond when the preceding command is run, terminate the command, click https://goo.gl/6wtTN5, and paste the web content into a local .sh file.

touch fabric_download.sh
vi fabric_download.sh  #Paste the content.
chmod +x fabric_download.sh
./fabric_download.sh

Git is not installed on ECS by default. Install Git first; otherwise, fabric-samples cannot be checked out.

yum install git

If an exception occurs during installation and you are prompted to choose installation items, modify the following lines in the script and choose to install the Docker images, samples, and binaries as required.

DOCKER=true
SAMPLES=true
BINARIES=true

Note:

  1. Because Docker, on which Fabric is dependent, is larger than 10 GB, you will have to wait for it to install.
  2. Configure the accelerator on MAC as follows: Click the question mark (?) icon on the upper right corner of the Mac desktop, choose Preference > Daemon > Registry mirrors, and add an Alibaba Cloud or DaoCloud personal accelerator address.

After the command is run, you can check whether the tools required by Fabric are stored in the /usr/local/bin directory.

1

View the downloaded Docker images.

docker image ls

2

Official Fabric sample

(1) Official Sample: Build Your First Network (BYFN)

[root@fabric-test-01 first-network]# cd /usr/local/fabric-samples/first-network
[root@fabric-test-01 first-network]# ./byfn.sh up

If you directly run the ./byfn.sh up command in the cloud ECS environment, the system will break down. For detailed information, see Official Fabric issue.

Hyperledger officially suggests that - GODEBUG=netdns=go be added. However, test results indicate that this method does not work and the peer may still fail to join the channel. This problem still persists even if the timeout duration is prolonged.

[root@fabric-test-01 first-network]# ./byfn down
[root@fabric-test-01 first-network]# vi docker-compose-cli.yaml
[root@fabric-test-01 first-network]# ./byfn up -t 10000 #Set the timeout duration to 10s.
Add - GODEBUG=netdns=go.

3

4

According to our cause analysis, modify the /etc/resolv.conf file by commenting out the following line: options timeout:2 attempts:3 rotate single-request-reopen.

[root@fabric-test-01 first-network]# ./byfn.sh down
[root@fabric-test-01 first-network]# vi /etc/resolv.conf
[root@fabric-test-01 first-network]# ./byfn.sh up -t 10000

(2) BYFN Startup Process

Step 1

Call the byfn.sh script and networkUp method as follows:

  1. Verify the version.
  2. Use the cryptogen tool to generate public and private keys and certificates.
  3. Use the configtxgen tool to generate a genesis block, a channel configuration block, and two organization anchor points.
  4. Call the scripts/script.sh script to perform subsequent operations.

Fabric supports two types of public and private keys and certificates, namely, a TLS certificate for ensuring the communication security between nodes and a user certificate for user logon and permission control. These certificates should have been issued by the CA, but we use the test environment where the CA node is not enabled. Fabric has provided us with the cryptogen tool.

[root@fabric-test-01 first-network]# cryptogen generate --config=./crypto-config.yaml
org1.example.com
org2.example.com

The cryptogen tool generates a crypto-config folder in the current directory based on the crypto-config.yaml file. The folder structure is shown as follows.

[root@fabric-test-01 first-network]# yum install tree
[root@fabric-test-01 first-network]# tree -L 4 crypto-config
crypto-config
├── ordererOrganizations
│   └── example.com
│       ├── ca
│       │   ├── 16cdf940fba55b97f3cdf71eb44ed39bc08ff7149757ac34d7596707c97b18b8_sk
│       │   └── ca.example.com-cert.pem
│       ├── msp
│       │   ├── admincerts
│       │   ├── cacerts
│       │   └── tlscacerts
│       ├── orderers
│       │   └── orderer.example.com
│       ├── tlsca
│       │   ├── b38e560de1aa8138923ad0d2797b8b8f16ed082b8a14381190a0dea1829e749a_sk
│       │   └── tlsca.example.com-cert.pem
│       └── users
│           └── Admin@example.com
└── peerOrganizations
    ├── org1.example.com
    │   ├── ca
    │   │   ├── 0f7e3c534db8811228222fc7b16d2105e59b1002981745ca75d9c16a64e32287_sk
    │   │   └── ca.org1.example.com-cert.pem
    │   ├── msp
    │   │   ├── admincerts
    │   │   ├── cacerts
    │   │   ├── config.yaml
    │   │   └── tlscacerts
    │   ├── peers
    │   │   ├── peer0.org1.example.com
    │   │   └── peer1.org1.example.com
    │   ├── tlsca
    │   │   ├── 4d4a78f0c71d4a1247867f7d9e389cde56869dec283eb2555fd0825441fba0c3_sk
    │   │   └── tlsca.org1.example.com-cert.pem
    │   └── users
    │       ├── Admin@org1.example.com
    │       └── User1@org1.example.com
    └── org2.example.com
        ├── ca
        │   ├── 83b3d4ce5fb3877c76817da2a50329ddb4f4c4bc9f5cdbc9b7cf0fa44aa6fa17_sk
        │   └── ca.org2.example.com-cert.pem
        ├── msp
        │   ├── admincerts
        │   ├── cacerts
        │   ├── config.yaml
        │   └── tlscacerts
        ├── peers
        │   ├── peer0.org2.example.com
        │   └── peer1.org2.example.com
        ├── tlsca
        │   ├── 12e7338b1321c4660fbebf42dac0adc38ec6fd6e1b972bd6c13b27f5798cf95f_sk
        │   └── tlsca.org2.example.com-cert.pem
        └── users
            ├── Admin@org2.example.com
            └── User1@org2.example.com

39 directories, 14 files

The crypto-config.yaml file is parsed as follows.

# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

# ---------------------------------------------------------------------------
# "OrdererOrgs" - Definition of organizations managing orderer nodes
# ---------------------------------------------------------------------------
OrdererOrgs:  #Orderer organization
  # ---------------------------------------------------------------------------
  # Orderer
  # ---------------------------------------------------------------------------
  - Name: Orderer  #Organization name
    Domain: example.com #Organization's domain name
    # ---------------------------------------------------------------------------
    # "Specs" - See PeerOrgs below for complete description
    # ---------------------------------------------------------------------------
    Specs:
      - Hostname: orderer #Generate an additional orderer.example.com.
                          #The function of Spec is to personalize a domain name and exempt the domain name from being affected by the following template generation rules.
# ---------------------------------------------------------------------------
# "PeerOrgs" - Definition of organizations managing peer nodes
# ---------------------------------------------------------------------------
PeerOrgs: #Network node organization
  # ---------------------------------------------------------------------------
  # Org1
  # ---------------------------------------------------------------------------
  - Name: Org1 #Name of Org1
    Domain: org1.example.com #Domain name of Org1
    EnableNodeOUs: true #It is supported by Node.js, and is commented out in the Java SDK by default.
    # ---------------------------------------------------------------------------
    # "Specs"
    # ---------------------------------------------------------------------------
    # Uncomment this section to enable the explicit definition of hosts in your
    # configuration.  Most users will want to use Template, below
    #
    # Specs is an array of Spec entries.  Each Spec entry consists of two fields:
    #   - Hostname:   (Required) The desired hostname, sans the domain.
    #   - CommonName: (Optional) Specifies the template or explicit override for
    #                 the CN.  By default, this is the template:
    #
    #                              "{{.Hostname}}.{{.Domain}}"
    #
    #                 which obtains its values from the Spec.Hostname and
    #                 Org.Domain, respectively.
    # ---------------------------------------------------------------------------
    # Specs:
    #   - Hostname: foo # implicitly "foo.org1.example.com"
    #     CommonName: foo27.org5.example.com # overrides Hostname-based FQDN set above
    #   - Hostname: bar
    #   - Hostname: baz
    # ---------------------------------------------------------------------------
    # "Template"
    # ---------------------------------------------------------------------------
    # Allows for the definition of 1 or more hosts that are created sequentially
    # from a template. By default, this looks like "peer%d" from 0 to Count-1.
    # You may override the number of nodes (Count), the starting index (Start)
    # or the template used to construct the name (Hostname).
    #
    # Note: Template and Specs are not mutually exclusive.  You may define both
    # sections and the aggregate nodes will be created for you.  Take care with
    # name collisions
    # ---------------------------------------------------------------------------
    Template: #Generate two sets of public and private keys as well as certificates based on the preceding template. The default generation rule is based on peers 0 to 9.organization domain name.
      Count: 2 #Generate the public and private keys as well as certificates for the peer0.org1.example.com and peer1.org1.example.com nodes.
      # Start: 5
      # Hostname: {{.Prefix}}{{.Index}} # default
    # ---------------------------------------------------------------------------
    # "Users"
    # ---------------------------------------------------------------------------
    # Count: The number of user accounts _in addition_ to Admin
    # ---------------------------------------------------------------------------
    Users: #In addition to the admin account, a User1 account is generated.
      Count: 1
  # ---------------------------------------------------------------------------
  # Org2: See "Org1" for full specification
  # ---------------------------------------------------------------------------
  - Name: Org2
    Domain: org2.example.com
    EnableNodeOUs: true
    Template:
      Count: 2
    Users:
      Count: 1

The configtxgen tool generates four components based on the configtx.yaml file. Before this operation, specify the path to the configtx.yaml file for the configtxgen tool. We need to set an environment variable as the current directory, and the generated files will be stored in the channel-artifacts folder of this directory.

  1. Generate a genesis.block file for the orderer node.
  2. Generate a channel configuration transaction file channel.tx.
  3. Generate an anchor peer for Org1 of the channel.
  4. Generate an anchor peer for Org2 of the channel.

Anchor peer: specifies a peer that can be discovered by all other peers in the same channel. Each member of the channel has an anchor peer (or multiple anchor peers to prevent single-point failures). Peers belonging to different members are allowed to discover all existing peers in the same channel.

[root@fabric-test-01 first-network]# export FABRIC_CFG_PATH=$PWD
[root@fabric-test-01 first-network]# configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block
[root@fabric-test-01 first-network]# configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel
[root@fabric-test-01 first-network]# configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
[root@fabric-test-01 first-network]# configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP

The configtx.yaml file contains TwoOrgsOrdererGenesis (the orderer consensus configuration for two organizations) and TwoOrgsChannel (the channel configuration for two organizations). The consensus algorithm can be set to Solo or Kafka for the orderer. The default values of the consensus time block size, timeout duration, and other parameters can be used. The configurations of peer nodes include the configurations of the MSP and anchor nodes. If more organizations or channels are required, you can modify the configurations based on the template.

Step 2

Enable each container of Fabric. The docker-compose tool uses the docker-compose-cli.yaml file in the current directory to initialize the environment. It then generates four fabric-peer transaction node containers, one fabric-order orderer node container, and one fabric-tools Cli container. The Cli container is used to replace the SDK client. Because the Java SDK was not started, a client tool needs to be created to connect to the Fabric network for testing.

[root@fabric-test-01 first-network]# docker-compose -f docker-compose-cli.yaml up -d
Creating network "net_byfn" with the default driver
Creating volume "net_orderer.example.com" with default driver
Creating volume "net_peer0.org1.example.com" with default driver
Creating volume "net_peer1.org1.example.com" with default driver
Creating volume "net_peer0.org2.example.com" with default driver
Creating volume "net_peer1.org2.example.com" with default driver
Creating orderer.example.com    ... done
Creating peer1.org1.example.com ... done
Creating peer0.org2.example.com ... done
Creating peer1.org2.example.com ... done
Creating peer0.org1.example.com ... done
Creating cli                    ... done
[root@fabric-test-01 first-network]# docker ps -a
CONTAINER ID        IMAGE                               COMMAND             CREATED             STATUS              PORTS                                              NAMES
2f3af7e64868        hyperledger/fabric-tools:latest     "/bin/bash"         18 seconds ago      Up 17 seconds                                                          cli
dfea53b15138        hyperledger/fabric-peer:latest      "peer node start"   19 seconds ago      Up 17 seconds       0.0.0.0:7051->7051/tcp, 0.0.0.0:7053->7053/tcp     peer0.org1.example.com
3eac41603863        hyperledger/fabric-peer:latest      "peer node start"   19 seconds ago      Up 17 seconds       0.0.0.0:10051->7051/tcp, 0.0.0.0:10053->7053/tcp   peer1.org2.example.com
ea1ef950b845        hyperledger/fabric-peer:latest      "peer node start"   19 seconds ago      Up 17 seconds       0.0.0.0:9051->7051/tcp, 0.0.0.0:9053->7053/tcp     peer0.org2.example.com
ec49121fbdd5        hyperledger/fabric-peer:latest      "peer node start"   19 seconds ago      Up 17 seconds       0.0.0.0:8051->7051/tcp, 0.0.0.0:8053->7053/tcp     peer1.org1.example.com
11ecb9d115f2        hyperledger/fabric-orderer:latest   "orderer"           19 seconds ago      Up 17 seconds       0.0.0.0:7050->7050/tcp                             orderer.example.com

The docker-compose-cli.yaml file inherits all the content parsed from the docker-compose-base.yaml file. The docker-compose-base.yaml file is parsed as follows.

# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

version: '2'

services: #Orderer and transaction nodes provided

  orderer.example.com: #Orderer service
    container_name: orderer.example.com #Container name
    image: hyperledger/fabric-orderer:$IMAGE_TAG #Use the Fabric Docker image generation container.
    environment: #Environment variable configuration
      - ORDERER_GENERAL_LOGLEVEL=INFO #Log level
      - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 #Listening address segment. 0.0.0.0 indicates that requests from all terminals on the network are accepted.
      - ORDERER_GENERAL_GENESISMETHOD=file #Generate a file block.
      - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block
      - ORDERER_GENERAL_LOCALMSPID=OrdererMSP #Member management ID
      - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp #Member management directory in the Docker container
      # enabled TLS
      - ORDERER_GENERAL_TLS_ENABLED=true #Indicates whether TLS is enabled at the network layer. The following lists the paths of files in the Docker container.
      - 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: #Map the directories generated on the local Linux terminal to the directories in the Docker container.
    - ../channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block
    - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp
    - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls
    - orderer.example.com:/var/hyperledger/production/orderer
    ports: #Map the port of the local Linux terminal to the port of the Docker container.
      - 7050:7050

  peer0.org1.example.com: #Transaction node peer0.org1. The following peer1.org1 has basically the same configuration.
    container_name: peer0.org1.example.com #Container name
    Extends:#:#Inherit the peer-base configuration.
      file: peer-base.yaml
      service: peer-base
    Environment:#:#Environment variables
      - CORE_PEER_ID=peer0.org1.example.com #Node network address and listening port
      - CORE_PEER_ADDRESS=peer0.org1.example.com:7051
      - CORE_PEER_GOSSIP_BOOTSTRAP=peer1.org1.example.com:7051 #Gossip protocol configuration
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051
      - CORE_PEER_LOCALMSPID=Org1MSP #Member management name configured
    volumes:
        - /var/run/:/host/var/run/
        - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp
        - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/fabric/tls
        - peer0.org1.example.com:/var/hyperledger/production
    ports:
      - 7051:7051
      - 7053:7053

The peer-base.yaml file is parsed as follows.

# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
#

version: '2'

services:
  peer-base:
    image: hyperledger/fabric-peer:$IMAGE_TAG #Use the fabric-peer image generation container.
    environment: #Environment variables
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      # the following setting starts chaincode containers on the same
      # bridge network as the peers
      # https://docs.docker.com/compose/networking/
      - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_byfn
      - CORE_LOGGING_LEVEL=INFO # Log level
      #- CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_TLS_ENABLED=true #Indicates whether TLS is enabled.
      - CORE_PEER_GOSSIP_USELEADERELECTION=true #Enable leader election.
      - CORE_PEER_GOSSIP_ORGLEADER=false
      - CORE_PEER_PROFILE_ENABLED=true
      - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt #TLS node certificate
      - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key #TLS public key
      - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt #TLS root certificate
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start

Step 3

After containers are created, create a channel, add peer nodes to the channel, and configure an anchor point for each organization. The channel is similar to that used in Netty and can be used to isolate transactions.

Run the docker exec -it cli bash command to enter the Bash of the simulated client Cli container. Then, run the channel creation command to create a channel in the container.

[root@fabric-test-01 first-network]# docker exec -it cli bash
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer#
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/channel.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

Parameter description:

-o: specifies the orderer node orderer.example.com:7050. In the docker-compose-base.yaml file, port 7050 is specified for the orderer node.

-c mychannel -f ./channel-artifacts/channel.tx: specifies the channel name and uses the channel.tx configuration file that has been generated to initialize the channel.

--tls true: indicates that TLS is used for encrypted transmission on the network.

--cafile: specifies the CA certificate path.

Set the environment variables and add peers to the channel.

Note: peer0.org1 is connected to the Cli container by default. This means that peer0.org1 can use the peer channel join -b mychannel.block command directly. Other nodes can connect to the Cli container only if their environment variables have been modified correctly.

root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer channel join -b mychannel.block
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=peer1.org1.example.com:7051 CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt peer channel join -b mychannel.block
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel join -b mychannel.block
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer1.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel join -b mychannel.block

Configure an anchor point for the two organizations respectively: peer0.org1.example.com:7051 and peer0.org2.example.com:7051.

root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=peer0.org1.example.com:7051 CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org1MSPanchors.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org2MSPanchors.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem

Step 4

Install the chain code, and create an instance. The chain code needs to be installed on each peer node. As described in step 3, peer0.org1 is connected to the Cli container by default, and its environment variables do not need to be set.

Install the chain code.

root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp CORE_PEER_ADDRESS=peer1.org1.example.com:7051 CORE_PEER_LOCALMSPID="Org1MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer0.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp CORE_PEER_ADDRESS=peer1.org2.example.com:7051 CORE_PEER_LOCALMSPID="Org2MSP" CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/

Instantiate the chain code.

This operation is used to pack the previously installed chain code on the terminal where the peer is located, and generate the Docker image and container corresponding to the channel. An endorsement strategy can be specified during the instantiation.

The chain code needs to be instantiated only once.

root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -l golang -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P 'OR   ('\''Org1MSP.peer'\'','\''Org2MSP.peer'\'')'

Start another terminal to view the corresponding logs and the started Docker container.

[root@fabric-test-01 ~]# docker logs -f peer0.org1.example.com
[root@fabric-test-01 ~]# docker ps -a

Step 5

Query and verify data.

root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
2018-06-07 09:15:04.862 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-06-07 09:15:04.862 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
Query Result: 100
2018-06-07 09:15:04.866 UTC [main] main -> INFO 003 Exiting.....

Simulate a scenario in which a transfers 10 dollars to b.

root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode invoke -o orderer.example.com:7050  --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -c '{"Args":["invoke","a","b","10"]}'
2018-06-07 09:18:33.697 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-06-07 09:18:33.697 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2018-06-07 09:18:33.704 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 003 Chaincode invoke successful. result: status:200
2018-06-07 09:18:33.704 UTC [main] main -> INFO 004 Exiting.....
root@2f3af7e64868:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
2018-06-07 09:18:47.068 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2018-06-07 09:18:47.068 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
Query Result: 90
2018-06-07 09:18:47.073 UTC [main] main -> INFO 003 Exiting.....
[root@fabric-test-01 ~]# docker  logs --tail 200 dev-peer0.org1.example.com-mycc-1.0
ex02 Init
Aval = 100, Bval = 200
ex02 Invoke
Query Response:{"Name":"a","Amount":"100"}
ex02 Invoke
Aval = 90, Bval = 210
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}
ex02 Invoke
Query Response:{"Name":"a","Amount":"90"}

Obtain fabric-sdk-java

This project requires an IntelliJ IDEA environment and JDK 1.8 or later.

Preparations:

Fabric uses Google Protocol Buffer (Protobuf) as a serialization tool but JSON does not support Protobuf. Therefore, you must install the Protobuf plug-in on IntelliJ IDEA first.

IntelliJ->-> Preferences -> Plugins-> Browse repositories-> Search and Install Protobuf Support

On Mac, use homebrew to install Libtool; otherwise, Mac will report the error "ltdl.h: no such file or directory" when a unit test is started.

brew install libtool

Pull the branch code and switch to version 1.1. The latest version is 1.2, but it has not been released yet and therefore cannot be started.

git clone https://github.com/hyperledger/fabric-sdk-java.git
git checkout --track remotes/origin/release-1.1
maven install

Get Started with Mac and End2EndIT

The Mac terminal functions as both the server and client, on the premise that Fabric has been installed on it by following the instructions in sections 1 and 2.

Server: Run the following two commands to start the Fabric blockchain network.

Client: Open End2endIT.java and run it to conduct a simple blockchain transfer transaction.

cd src/test/fixture/sdkintegration
./fabric.sh up

The fabric.sh script is described as follows.

(1) The script commands are as follows:
./fabric.sh up: used to forcibly reestablish a network, which is enabled by default)
./fabric.sh start: used to start
./fabric.sh stop: used to stop
./fabric.sh clean: used to clear the generated Docker container
In fact, the fabric.sh script is a simple encapsulation of docker-compose commands. Therefore, you can directly use docker-compose instead.
(2) After each use, if you need to run the script again, you must run the clean command to clear the generated Docker container and other temporary files.

Run the following commands to view logs:

docker ps -a
docker logs --tail 200 "Container ID/name"

5

Use Case: Running the fabric-sdk-java Project

Detailed Project Directory

6

7

End2EndIT Code Details

The SDK uses Protobuf as the transport protocol, uses gRPC to communicate with the server, and uses YAML as the configuration file format. If you are not familiar with these concepts, get started by following the links below.

We recommend that you follow the official example of Fabric to perform operations. In the SDK process, the preceding command lines of the simulated Cli client are simply replaced with Java code.

Run the checkConfig() method to check configuration items.

    @Before
    public void checkConfig() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, MalformedURLException, org.hyperledger.fabric_ca.sdk.exception.InvalidArgumentException {
        out("\n\n\nRUNNING: %s.\n", testName);
        resetConfig();//Reset the org.hyperledger.fabric.sdk.helper.Config file, which mainly contains the configuration items required by Fabric.
        configHelper.customizeConfig();//Call the specified variables entered in the command lines to overwrite the configurations in the preceding configuration file.

        testSampleOrgs = testConfig.getIntegrationTestsSampleOrgs();//Obtain the organizations configured in testConfig, which has been created in End2endIT and contains a large number of test configuration items.
        for (SampleOrg sampleOrg : testSampleOrgs) {////Set the CA that issues certificates.
            String caName = sampleOrg.getCAName(); 
            if (caName != null && !caName.isEmpty()) {
                sampleOrg.setCAClient(HFCAClient.createNewInstance(caName, sampleOrg.getCALocation(), sampleOrg.getCAProperties()));
            } else {
                sampleOrg.setCAClient(HFCAClient.createNewInstance(sampleOrg.getCALocation(), sampleOrg.getCAProperties()));
            }
        }
    }
    @Test
    public void setup() throws Exception {
        if (sampleStoreFile.exists()) {
            sampleStoreFile.delete();//Simulate that the database storage uses the HFCSampletest.properties file.
        }
        sampleStore = new SampleStore(sampleStoreFile);//Initialize the storage.
        enrollUsersSetup(sampleStore);// Use CA for initialization.
        runFabricTest(sampleStore);//Core method

    }
    public void runFabricTest(final SampleStore sampleStore) throws Exception {
        ////////////////////////////
        // Setup client

        //Create instance of client.
        HFClient client = HFClient.createNewInstance();//Initialize a link client similar to Cli.

        client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());//Set an encryption algorithm.

        ////////////////////////////
        //Construct and run the channels.
        //Obtain peer Org1.
        SampleOrg sampleOrg = testConfig.getIntegrationTestsSampleOrg("peerOrg1");
        //Construct a channel and add Org1 to this channel.
        Channel fooChannel = constructChannel(FOO_CHANNEL_NAME, client, sampleOrg);
        //Save the channel name to the database (to the preceding method file in this example).
        sampleStore.saveChannel(fooChannel);
        //Install and instantiate the chain code, and perform a query test.
        runChannel(client, fooChannel, true, sampleOrg, 0);

        assertFalse(fooChannel.isShutdown());
        fooChannel.shutdown(true); // Force foo channel to shutdown clean up resources.
        assertTrue(fooChannel.isShutdown());

        assertNull(client.getChannel(FOO_CHANNEL_NAME));
        out("\n");
        //The following code is related to Org2, which is similar to that of Org1.
        sampleOrg = testConfig.getIntegrationTestsSampleOrg("peerOrg2");
        Channel barChannel = constructChannel(BAR_CHANNEL_NAME, client, sampleOrg);
        assertTrue(barChannel.isInitialized());
        /**
         * sampleStore.saveChannel uses {@link Channel#serializeChannel()}
         */
        sampleStore.saveChannel(barChannel);
        assertFalse(barChannel.isShutdown());
        runChannel(client, barChannel, true, sampleOrg, 100); //run a newly constructed bar channel with different b value!
        //let bar channel just shutdown so we have both scenarios.

        out("\nTraverse the blocks for chain %s ", barChannel.getName());
        //Run various queries on block information, including block read and write sets and block height.
        blockWalker(client, barChannel);

        assertFalse(barChannel.isShutdown());
        assertTrue(barChannel.isInitialized());
        out("That's all folks!");

    }

// Construct a channel and add Org1 to this channel.

Channel fooChannel = constructChannel(FOO_CHANNEL_NAME, client, sampleOrg);

// Install and instantiate the chain code, and perform a query test.

runChannel(client, fooChannel, true, sampleOrg, 0);

The two core methods are described as follows in detail:

Constructing a Channel

Set the user context of the operation.

        client.setUserContext(sampleOrg.getPeerAdmin());

Get the admin account of the peer. In End2endIT.enrollUsersSetup, this field is set as shown in the following code. This field is actually the default admin account configured by the cryptogen tool based on the crypto-config.yaml file. The read files are those in the v1.1 folder generated by the cryptogen tool:

src/test/fixture/sdkintegration/e2e-2Orgs/V1.1/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/

SampleUser peerOrgAdmin = sampleStore.getMember(sampleOrgName + "Admin", sampleOrgName, sampleOrg.getMSPID(),
        Util.findFileSk(Paths.get(testConfig.getTestChannelPath(), "crypto-config/peerOrganizations/",
                sampleOrgDomainName, format("/users/Admin@%s/msp/keystore", sampleOrgDomainName)).toFile()),
        Paths.get(testConfig.getTestChannelPath(), "crypto-config/peerOrganizations/", sampleOrgDomainName,
                format("/users/Admin@%s/msp/signcerts/Admin@%s-cert.pem", sampleOrgDomainName, sampleOrgDomainName)).toFile());

sampleOrg.setPeerAdmin(peerOrgAdmin); //A special user that can create channels, join peers and install chaincode

Initialize the orderer node objects.

        Collection<Orderer> orderers = new LinkedList<>();

        for (String orderName : sampleOrg.getOrdererNames()) {

            Properties ordererProperties = testConfig.getOrdererProperties(orderName);

            //example of setting keepAlive to avoid timeouts on inactive http2 connections.
            // Under 5 minutes would require changes to server side to accept faster ping rates.
            ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {5L, TimeUnit.MINUTES});
            ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {8L, TimeUnit.SECONDS});
            ordererProperties.put("grpc.NettyChannelBuilderOption.keepAliveWithoutCalls", new Object[] {true});


            orderers.add(client.newOrderer(orderName, sampleOrg.getOrdererLocation(orderName),
                    ordererProperties));
        }

sampleOrg.getOrdererLocation obtains the URL of the orderer gRPC configured in testConfig. The following code shows the default value.

defaultProperty(INTEGRATIONTESTS_ORG + "peerOrg1.orderer_locations", "orderer.example.com@grpc://" + LOCALHOST + ":7050");

Create a channel. The process is similar to that described in the preceding official sample of Fabric. As mentioned above, the configtxgen tool generates four types of files. The channel configuration file src/test/fixture/sdkintegration/e2e-2Orgs/V1.1/foo.tx generated by the configtxgen tool is used here.

        //Just pick the first orderer in the list to create the channel.

        Orderer anOrderer = orderers.iterator().next();
        orderers.remove(anOrderer);

        ChannelConfiguration channelConfiguration = new ChannelConfiguration(new File(TEST_FIXTURES_PATH + "/sdkintegration/e2e-2Orgs/" + TestConfig.FAB_CONFIG_GEN_VERS + "/" + name + ".tx"));

        //Create channel that has only one signer that is this orgs peer admin. If channel creation policy needed more signature they would need to be added too.
        Channel newChannel = client.newChannel(name, anOrderer, channelConfiguration, client.getChannelConfigurationSignature(channelConfiguration, sampleOrg.getPeerAdmin()));

        out("Created channel %s", name);

Run the client.newPeer method to create a peer node, and then run the joinPeer method to add the peer to the channel.

The joinPeer logic is complex. The peer needs to be signed first, and then the channel join proposal needs to be sent to all peer nodes. Details are not provided here.

       boolean everyother = true; //test with both cases when doing peer eventing.
        for (String peerName : sampleOrg.getPeerNames()) {
            String peerLocation = sampleOrg.getPeerLocation(peerName);

            Properties peerProperties = testConfig.getPeerProperties(peerName); //test properties for peer.. if any.
            if (peerProperties == null) {
                peerProperties = new Properties();
            }


            //Example of setting specific options on grpc's NettyChannelBuilder
            peerProperties.put("grpc.NettyChannelBuilderOption.maxInboundMessageSize", 9000000);

            Peer peer = client.newPeer(peerName, peerLocation, peerProperties);
            if (doPeerEventing && everyother) {
                newChannel.joinPeer(peer, createPeerOptions()); //Default is all roles.
            } else {
                // Set peer to not be all roles but eventing.
                newChannel.joinPeer(peer, createPeerOptions().setPeerRoles(PeerRole.NO_EVENT_SOURCE));
            }
            out("Peer %s joined channel %s", peerName, name);
            everyother = !everyother;
        }

peerLocation obtains the URL of the peer gRPC configured in testConfig. The following code shows the default value.

defaultProperty(INTEGRATIONTESTS_ORG + "peerOrg1.peer_locations", "peer0.org1.example.com@grpc://" + LOCALHOST + ":7051, peer1.org1.example.com@grpc://" + LOCALHOST + ":7056");

Configure a gRPC port as the event listener port for the channel, and initialize the channel.

        for (String eventHubName : sampleOrg.getEventHubNames()) {

            final Properties eventHubProperties = testConfig.getEventHubProperties(eventHubName);

            eventHubProperties.put("grpc.NettyChannelBuilderOption.keepAliveTime", new Object[] {5L, TimeUnit.MINUTES});
            eventHubProperties.put("grpc.NettyChannelBuilderOption.keepAliveTimeout", new Object[] {8L, TimeUnit.SECONDS});


            EventHub eventHub = client.newEventHub(eventHubName, sampleOrg.getEventHubLocation(eventHubName),
                    eventHubProperties);
            newChannel.addEventHub(eventHub);
        }

        newChannel.initialize();

Running the Channel

Install the chain code. The chain code file is example_cc.go in Java. This file is compiled by using the Go language.

            if (installChaincode) {
                //Write a large piece of code to configure information about the chain code to be installed.
                //Send a transaction proposal to install the chain code.
                responses = client.sendInstallProposal(installProposalRequest, peers);
            }

Initialize the chain code instance and set the endorsement strategy.

            ///////////////
            //// Instantiate chaincode.
            InstantiateProposalRequest instantiateProposalRequest = client.newInstantiationProposalRequest();
            instantiateProposalRequest.setProposalWaitTime(testConfig.getProposalWaitTime());
            instantiateProposalRequest.setChaincodeID(chaincodeID);
            instantiateProposalRequest.setChaincodeLanguage(CHAIN_CODE_LANG);
            instantiateProposalRequest.setFcn("init");
            instantiateProposalRequest.setArgs(new String[] {"a", "500", "b", "" + (200 + delta)});
            Map<String, byte[]> tm = new HashMap<>();
            tm.put("HyperLedgerFabric", "InstantiateProposalRequest:JavaSDK".getBytes(UTF_8));
            tm.put("method", "InstantiateProposalRequest".getBytes(UTF_8));
            instantiateProposalRequest.setTransientMap(tm);

            /*
              policy OR(Org1MSP.member, Org2MSP.member) meaning 1 signature from someone in either Org1 or Org2
              See README.md Chaincode endorsement policies section for more details.
            */
            ChaincodeEndorsementPolicy chaincodeEndorsementPolicy = new ChaincodeEndorsementPolicy();
            chaincodeEndorsementPolicy.fromYamlFile(new File(TEST_FIXTURES_PATH + "/sdkintegration/chaincodeendorsementpolicy.yaml"));
            instantiateProposalRequest.setChaincodeEndorsementPolicy(chaincodeEndorsementPolicy);

            out("Sending instantiateProposalRequest to all peers with arguments: a and b set to 100 and %s respectively", "" + (200 + delta));
            successful.clear();
            failed.clear();

            if (isFooChain) {  //Send responses both ways with specifying peers and by using those on the channel.
                responses = channel.sendInstantiationProposal(instantiateProposalRequest, channel.getPeers());
            } else {
                responses = channel.sendInstantiationProposal(instantiateProposalRequest);
            }

The following code is only used for testing.

channel.sendTransaction(successful, createTransactionOptions()
                    .userContext(client.getUserContext())
                    .shuffleOrders(false)
                    .orderers(channel.getOrderers())
                    .nOfEvents(nOfEvents)
            ).thenApply(transactionEvent -> {
Call the move method in the example_cc.go file to transfer some dollars from a to b.
}.thenApply(transactionEvent -> {
Query the account balance of b.
                try {
}).exceptionally(e -> {
//Exception handling
}

Finally, query the block height and other information.

0 0 0
Share on

Alibaba Clouder

1,501 posts | 242 followers

You may also like

Comments