Hyperledger Fabric Network: Up and Running

In the previous article, you found out about Hyperledger Fabric and now, we will dive deep into the technical part. Be prepared for a long article. 😊 From our experience, it was really hard to find all the needed information for the blockchain network, so we decided to put it all together with explaining to you almost every line of our code.

We already explained what the blockchain network is and now we will learn

…what do we need to make one?

The first important thing to do is to stick with recommended versions because of conflicts which can cause a lot of errors. And if you got it anyway — check your versions again!

And don’t worry — ‘’Anything that can go wrong will go wrong”.

So, before we begin with installation, you may wish to check if you already have some of the prerequisites on the platform on which you’ll be developing blockchain applications and/or operating Hyperledger Fabric.

Installation of prerequisites

For Crypto-Kajmak application, we used Ubuntu 16.04 LTS Virtual Machine so our prerequisites are:

  • cURL
  • Docker version 17.9.x or greater
  • Docker Compose version 1.22.x or greater
  • Node.js and npm package manager
  • Go version 1.9.x
  • Python version 2.7.x

Let’s start!

What is cURL?

cURL is a tool to transfer data from or to a server, using one of the supported protocols (for example FTP, HTTP, HTTPS, IMAP and other).

Install it by using the following command:

$ sudo apt install curl

Now you can check version using the command with capital V:

$ curl -V

The output should look like this:

What is docker?

Docker is a tool designed to make it easier to create, deploy, and run applications by using containers. A container is a standard unit of software that packages up the code and all its dependencies so the application runs quickly and reliably from one computing environment to another. A Docker container image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings.

$ sudo apt install docker.io

Run this command to download the required version of Docker Compose:

$ curl -L https://github.com/docker/compose/releases/download/1.23.1-rc1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

Now, when you check your version with the following command:

$ docker — version && docker-compose — version

your output should look like this:

What is Node.js?

It is an open-source, cross-platform JavaScript run-time environment that executes JavaScript code outside of a browser. It is asynchronous and event-driven I/O.

What is npm?

NPM is short for Node.js package manager.

www.npmjs.com hosts thousands of free packages to download and use.

A package in Node.js contains all the files you need for a module or JavaScript libraries which you can include in your project.

You can visit this page to install Node.js or use nvm (Node Version Manager) to change it whenever you want:

$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash$ export NVM_DIR=”${XDG_CONFIG_HOME/:-$HOME/.}nvm”
[ -s “$NVM_DIR/nvm.sh” ] && \. “$NVM_DIR/nvm.sh”

On Linux, after running the install script, if you get nvm: command not found or see no feedback from your terminal after you type:

$ command -v nvm

simply close your current terminal, open a new terminal, and try verifying again.

Now you can install node version simply like this:

$ nvm install 8.13.0
Installation of node v8.13.0
Example of changing the version of node

Last but not least — Golang

Go is a programming language that we will use to write chaincode.

Use the following command to check the version of Golang:

$ go version

To install it visit https://golang.org/dl/ and make note of the latest stable release (v1.8 or later):

$ sudo curl -O https://storage.googleapis.com/golang/go1.9.2.darwin-amd64.pkg

Or follow these steps to install compatible and required version:

  1. If you already have installed Golang use this to delete it:
$ sudo apt-get purge golang

2. Now install needed version

$ curl https://dl.google.com/go/go1.9.linux-amd64.tar.gz > go1.9.linux-amd64.tar.gz$ sudo tar -C /usr/local -xzf go1.9.linux-amd64.tar.gz
Checking the version of go

Writing configuration files

Now, the environment is prepared and we are ready to write files necessary to bring up our blockchain network. Files that we need are:

  • crypto-config.yaml
  • configtx.yaml
  • generate.sh
  • docker-compose.yaml
  • start.sh
Folders structure for blockchain network in CryptoKajmak application

Let’s create this folder structure. First, open the terminal and navigate to the folder where you want to create an application.

$ mkdir crypto-kajmak
$ cd crypto-kajmak
$ mkdir kajmak-network
$ cd kajmak-network

From the link download bin folder where tools for creating certificates and artifacts are located.

Each of the configuration file mentioned below is created within kajmak-network folder.

Let’s write and explain each of those files.

.env

In this file, we define COMPOSE_PROJECT_NAME environment variable which we use in docker-compose file for the network name.

The content of this file is:

COMPOSE_PROJECT_NAME=net

crypto-config.yaml

The first thing we need to do is to define the network topology. We will do that in a configuration file called crypto-config.yaml. There we list all organizations in our network and all peers and users that belong to the particular organization. It’s important to know that every organization always has one admin user by default. We will use this file later to generate digital certificates for all actors in our network.

Let’s recall our network structure from the previous article to clarify what we want to achieve.

There are three organizations in our network: org1, org2 and org3. Every organization has two peers and zero users (for now). But, as we already said, there is always one default admin user per organization. Also, the orderer node belongs to the orderer organization.

The content of the crypto-config.yaml configuration file (indentation is important):

OrdererOrgs:
- Name: Orderer
Domain: example.com
Specs:
- Hostname: orderer
PeerOrgs:
#Org1
- Name: Org1
Domain: org1.example.com
CA:
Hostname: ca
Template:
Count: 2
SANS:
- "localhost"
Users:
Count: 0
#Org2
- Name: Org2
Domain: org2.example.com
CA:
Hostname: ca
Template:
Count: 2
SANS:
- "localhost"
Users:
Count: 0
#Org3
- Name: Org3
Domain: org3.example.com
CA:
Hostname: ca
Template:
Count: 2
SANS:
- "localhost"
Users:
Count: 0

Explanation of the crypto-config.yaml file:

OrdererOrgs — definition of organizations managing orderer nodes

PeerOrgs — definition of organizations managing peer nodes

Name — the name of the orderer organization or the peer organization

Domain — the scope controlled by the orderer organization or the peer organization

Specs — an array of Spec entries. Each Spec entry consists of two fields:

  • Hostname: (Required) The desired name of the orderer or peer node.
  • CommonName: (Optional) This entry overrides specified hostname.

For example, if we have:

OrdererOrgs:
- Name: Orderer
Domain: example.com
Specs:
- Hostname: orderer

the name of the orderer node will be orderer.example.com (domain is always added implicitly for Hostname).

On the other hand, if we have:

OrdererOrgs:
- Name: Orderer
Domain: example.com
Specs:
- Hostname: orderer
CommonName: blockchain

the name of the orderer node will be blockchain (without domain) because CommonName blockchain overrides Hostname orderer.example.com

CA — explicit definition of the Certificate Authority(CA) for the organization; the name of the CA is defined with Hostname keyword under CA keyword

Template — together with keyword Count under the keyword Template, defines how many peer nodes are created sequentially for a specific organization. By default, names of the peers look like “peer%d” from 0 to Count-1. For example, for organization Org1, names of the peers will be peer0.org1.example.com and peer1.org1.example.com (domain is added implicitly, remember?)

SANS — (Optional) Specifies one or more Subject Alternative Names to be set in the resulting x509

Users — together with keyword Count under the keyword Template, defines how many user accounts are created for a specific organization in addition to an Admin user

More options are available, but they are beyond the scope of this tutorial.

configtx.yaml

Next, we will create configuration artifacts that we need for initialization of the blockchain network or its components. Artifacts are:

  • orderer genesis block — the orderer block is the genesis block for the ordering service. The genesis block is the configuration block that initializes a blockchain network or channel, and also serves as the first block on a chain. The orderer genesis block contains configuration transactions that hold the initial state of the channel.
  • channel configuration transaction — this file is broadcast to the orderer at channel creation time. It contains a lot of different parameters needed for channel configuration.
  • and two anchor peer transactions— they specify each Org’s Anchor Peer on the channel. Org1 and Org2 are part of the same channel in our case, so that is the reason why we have two anchor peer transactions.

We create artifacts based on the content of the configtx.yaml file that contains the definitions for the CryptoKajmak blockchain network:

##################################################################### SECTION: Profile
####################################################################
Profiles:
TwoOrgsOrdererGenesis:
Orderer:
<<: *OrdererDefaults
Organizations:
- *OrdererOrg
Consortiums:
KajmakConsortium:
Organizations:
- *Org1
- *Org2
TwoOrgsChannel:
Consortium: KajmakConsortium
Application:
<<: *ApplicationDefaults
Organizations:
- *Org1
- *Org2
####################################################################
# SECTION: Organizations
####################################################################
Organizations:
- &OrdererOrg
Name: OrdererMSP
ID: OrdererMSP
MSPDir: crypto-config/ordererOrganizations/example.com/msp
- &Org1
Name: Org1MSP
ID: Org1MSP
MSPDir: crypto-config/peerOrganizations/org1.example.com/msp
AnchorPeers:
- Host: peer0.org1.example.com
Port: 7051
- &Org2
Name: Org2MSP
ID: Org2MSP
MSPDir: crypto-config/peerOrganizations/org2.example.com/msp
AnchorPeers:
- Host: peer0.org2.example.com
Port: 7051
####################################################################
# SECTION: Orderer
####################################################################
Orderer: &OrdererDefaults
OrdererType: solo
Addresses:
- orderer.example.com:7050
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 10
AbsoluteMaxBytes: 98 MB
PreferredMaxBytes: 512 KB
Organizations:
####################################################################
# SECTION: Application
####################################################################
Application: &ApplicationDefaults
Organizations:

SECTION: PROFILE

In this section, you will notice that we have two unique headers. We pass them in as arguments when we create artifacts:

  • for the orderer genesis block, we pass TwoOrgsOrdererGenesis header. We reference information about orderer with * character and that information is defined in Section: Organizations. Here we also define consortium. Our consortium consists of two organizations, Org1 and Org2.
  • for our channel, we pass TwoOrgsChannel header.

SECTION: ORGANIZATIONS

This section defines different organizational identities which we referenced in the Profile section. Here we list all organizations in our network. Most important parts are:

ID: we use it to load the MSP definition for a specific organization

MSPDir: the filesystem path which contains the MSP configuration for a specific organization

Here, we can also define what peer will be anchor peer for every organization.

SECTION: ORDERER

In this section, we define the values to encode into a config transaction or genesis block for orderer related parameters.

With reference *OrdererDefaults we can approach to &OrdererDefaults.

OrdererType — in our case type is solo. It means that we have one orderer peer. Besides solo, you can also use Kafka. Kafka uses more than one peer as the ordering service and if one of them failed, another can be used

Addressestells us what is the address of the orderer node

Explanation of other parameters can be found in fabric samples, but you should not change them unless you know very well what you are doing and what do you want to achieve.

SECTION: APPLICATION

This section defines the values to encode into a config transaction or
genesis block for application related parameters.

generate.sh

We wrote configuration files but we didn’t create anything yet. Where are certificates and artifacts we talked about and how do they look? Hmm.

It’s a perfect time to find out.

To make it easier, instead of writing all commands one by one, we wrote them in bash script called generate.sh. On that way, we can only execute the script and it will do everything else for us because that’s what scripts do — they run a series of commands.

The content of the generate.sh script:

#!/bin/bashexport PATH=$GOPATH/src/github.com/hyperledger/fabric/build/bin:${PWD}/../bin:${PWD}:$PATHexport FABRIC_CFG_PATH=${PWD}CHANNEL_NAME=mychannelrm -fr config/*
rm -fr crypto-config/*
mkdir config
./bin/cryptogen generate --config=./crypto-config.yaml
if [ "$?" -ne 0 ]; then
echo "Failed to generate crypto material..."
exit 1
fi
./bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./config/genesis.block
if [ "$?" -ne 0 ]; then
echo "Failed to generate orderer genesis block..."
exit 1
fi
./bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./config/channel.tx -channelID $CHANNEL_NAME
if [ "$?" -ne 0 ]; then
echo "Failed to generate channel configuration transaction..."
exit 1
fi
./bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./config/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
if [ "$?" -ne 0 ]; then
echo "Failed to generate anchor peer update for Org1MSP..."
exit 1
fi
./bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./config/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP
if [ "$?" -ne 0 ]; then
echo "Failed to generate anchor peer update for Org2MSP..."
exit 1
fi

Explanation of the generate.sh script:

We specify the path to the bin folder where tools for generating certificates, keys and artifacts are located:

export PATH=$GOPATH/src/github.com/hyperledger/fabric/build/bin:${PWD}/../bin:${PWD}:$PATH

FABRIC_CFG_PATH is environment variable. The configtxgen tool needs this variable in order to locate the configtx.yaml. PWD means Present Working Directory:

export FABRIC_CFG_PATH=${PWD}

CHANNEL_NAME is a variable that holds the name of the channel. We use it in commands for creating artifacts:

CHANNEL_NAME=mychannel

We remove config folder and all its content. In config folder, artifacts are located:

rm -fr config/*

We remove crypto-config folder and all its content. In crypto-config folder, certificates and keys are located:

rm -fr crypto-config/*

We create a new config folder where artifacts will be stored since we removed the old one. Folder crypto-config doesn’t need to be explicitly created.

mkdir config

We use cryptogen tool from the bin folder and generate command to create certificates and keys for all organizations and their components. With the flag --config we tell what configuration template to use. Tool cryptogen consumes crypto-config.yaml configuration file. If there is an error, we will be informed:

./bin/cryptogen generate --config=./crypto-config.yaml
if [ "$?" -ne 0 ]; then
echo "Failed to generate crypto material..."
exit 1
fi

We use configtxgen tool from the bin folder to create and inspect channel config related artifacts. The content of the generated artifacts is dictated by the contents of configtx.yaml configuration file. With this tool, we can use different flags:

-profile — The profile from configtx.yaml to use for generation

-outputBlock — The path to write the genesis block to

-outputCreateChannelTx — The path to write a channel configuration transaction to

-channelID — The channel ID to use

-outputAnchorPeersUpdate — Creates an config update to update an anchor peer

-asOrg — Performs the config generation as a particular organization

To create orderer genesis block called genesis.block we use command:

./bin/configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./config/genesis.block
if [ "$?" -ne 0 ]; then
echo "Failed to generate orderer genesis block..."
exit 1
fi

To create a channel configuration transaction called channel.tx we use the command:

./bin/configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./config/channel.tx -channelID $CHANNEL_NAME
if [ "$?" -ne 0 ]; then
echo "Failed to generate channel configuration transaction..."
exit 1
fi

To create anchor peer transaction called Org1MSPanchors.tx for Org1 we use the command:

./bin/configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./config/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP
if [ "$?" -ne 0 ]; then
echo "Failed to generate anchor peer update for Org1MSP..."
exit 1
fi

In the same way, we do that for Org2.

Go inspect just created config and crypto-config folders. Can you recognize artifacts in config folder and certificates and keys for every component in our network in crypto-config folder (little help: certificate files start with -----BEGIN CERTIFICATE----- and private key files start with -----BEGIN PRIVATE KEY----- )?

If you want to learn better, it would be very helpful to execute in terminal previous commands on your own, step by step, to inspect outputs of every command separately.

docker-compose.yaml

This is the configuration file that Docker will use to bring up our blockchain network. Every component of our network is one Docker container defined in this file as a service. Docker-compose manages all containers. Inside every container, there is a file system hierarchy, just like on our computers.

Note: You need to modify environmental variables, in docker-compose.yaml, for CA’s key file every time you generate new certificates and private keys. Replace all --HERE-- in docker-compose.yaml with file that ends with _sk from ca folder that exist for every organization in crypto-config folder. For example, for org1, file we need can be found on location:

cd crypto-kajmak/kajmak-network/crypto-config/peerOrganizations/org1.example.com/ca

Do this for org2 and org3 too.

The content of the docker-compose.yaml:

# Copyright IBM Corp. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
version: '2'
networks:
kajmak:
services:
ca.org1.example.com:
image: hyperledger/fabric-ca
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_NAME=ca-org1
- FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem
- FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/--HERE--
ports:
- "7054:7054"
command: sh -c 'fabric-ca-server start -b admin:adminpw -d'
volumes:
- ./crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config
container_name: ca_peerOrg1
networks:
- kajmak

ca.org2.example.com:
image: hyperledger/fabric-ca
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_NAME=ca-org2
- FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org2.example.com-cert.pem
- FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/--HERE--
ports:
- "8054:7054"
command: sh -c 'fabric-ca-server start -b admin:adminpw -d'
volumes:
- ./crypto-config/peerOrganizations/org2.example.com/ca/:/etc/hyperledger/fabric-ca-server-config
container_name: ca_peerOrg2
networks:
- kajmak

ca.org3.example.com:
image: hyperledger/fabric-ca
environment:
- FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server
- FABRIC_CA_SERVER_CA_NAME=ca-org3
- FABRIC_CA_SERVER_CA_CERTFILE=/etc/hyperledger/fabric-ca-server-config/ca.org3.example.com-cert.pem
- FABRIC_CA_SERVER_CA_KEYFILE=/etc/hyperledger/fabric-ca-server-config/--HERE--
ports:
- "9054:7054"
command: sh -c 'fabric-ca-server start -b admin:adminpw -d'
volumes:
- ./crypto-config/peerOrganizations/org3.example.com/ca/:/etc/hyperledger/fabric-ca-server-config
container_name: ca_peerOrg3
networks:
- kajmak

orderer.example.com:
container_name: orderer.example.com
image: hyperledger/fabric-orderer
environment:
- ORDERER_GENERAL_LOGLEVEL=debug
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_GENESISMETHOD=file
-ORDERER_GENERAL_GENESISFILE=/etc/hyperledger/configtx/genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
- ORDERER_GENERAL_LOCALMSPDIR=/etc/hyperledger/crypto/orderer/msp
working_dir: /opt/gopath/src/github.com/hyperledger/fabric/orderer
command: orderer
ports:
- 7050:7050
volumes:
- ./config/:/etc/hyperledger/configtx
- ./crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/:/etc/hyperledger/crypto/orderer
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/:/etc/hyperledger/crypto/peer0Org1
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/:/etc/hyperledger/crypto/peer1Org1
- ./crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/:/etc/hyperledger/crypto/peer0Org2
- ./crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/:/etc/hyperledger/crypto/peer1Org2
- ./crypto-config/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/:/etc/hyperledger/crypto/peer0Org3
- ./crypto-config/peerOrganizations/org3.example.com/peers/peer1.org3.example.com/:/etc/hyperledger/crypto/peer1Org3
networks:
- kajmak

peer0.org1.example.com:
container_name: peer0.org1.example.com
image: hyperledger/fabric-peer
environment:
- CORE_PEER_ID=peer0.org1.example.com
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_ADDRESS=peer0.org1.example.com:7051
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/peer/
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_kajmak
- CORE_LOGGING_PEER=debug
- CORE_CHAINCODE_LOGGING_LEVEL=DEBUG
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
command: peer node start
ports:
- 7051:7051
- 7053:7053
volumes:
- /var/run/:/host/var/run/
- ./config/:/etc/hyperledger/configtx
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/crypto/peer
- ./crypto-config/peerOrganizations/org1.example.com/users:/etc/hyperledger/crypto/users/org1
- ./crypto-config/peerOrganizations/org2.example.com/users:/etc/hyperledger/crypto/users/org2
depends_on:
- orderer.example.com
networks:
- kajmak
peer1.org1.example.com:
container_name: peer1.org1.example.com
image: hyperledger/fabric-peer
environment:
- CORE_PEER_ID=peer1.org1.example.com
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_ADDRESS=peer1.org1.example.com:7051
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/peer/
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_kajmak
- CORE_LOGGING_PEER=debug
- CORE_CHAINCODE_LOGGING_LEVEL=DEBUG
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
ports:
- 7056:7051
- 7058:7053
volumes:
- /var/run/:/host/var/run/
- ./config/:/etc/hyperledger/configtx
- ./crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/crypto/peer
- ./crypto-config/peerOrganizations/org1.example.com/users:/etc/hyperledger/crypto/users
depends_on:
- orderer.example.com
networks:
- kajmak

peer0.org2.example.com:
container_name: peer0.org2.example.com
image: hyperledger/fabric-peer
environment:
- CORE_PEER_ID=peer0.org2.example.com
- CORE_PEER_LOCALMSPID=Org2MSP
- CORE_PEER_ADDRESS=peer0.org2.example.com:7051
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/peer/
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_kajmak
- CORE_LOGGING_PEER=debug
- CORE_CHAINCODE_LOGGING_LEVEL=DEBUG
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
ports:
- 8051:7051
- 8053:7053
volumes:
- /var/run/:/host/var/run/
- ./config/:/etc/hyperledger/configtx
- ./crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp:/etc/hyperledger/crypto/peer
- ./crypto-config/peerOrganizations/org2.example.com/users:/etc/hyperledger/crypto/users
depends_on:
- orderer.example.com
networks:
- kajmak

peer1.org2.example.com:
container_name: peer1.org2.example.com
image: hyperledger/fabric-peer
environment:
- CORE_PEER_ID=peer1.org2.example.com
- CORE_PEER_LOCALMSPID=Org2MSP
- CORE_PEER_ADDRESS=peer1.org2.example.com:7051
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/peer/
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_kajmak
- CORE_LOGGING_PEER=debug
- CORE_CHAINCODE_LOGGING_LEVEL=DEBUG
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
ports:
- 8056:7051
- 8058:7053
volumes:
- /var/run/:/host/var/run/
- ./config/:/etc/hyperledger/configtx
- ./crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/msp:/etc/hyperledger/crypto/peer
- ./crypto-config/peerOrganizations/org2.example.com/users:/etc/hyperledger/crypto/users
depends_on:
- orderer.example.com
networks:
- kajmak

peer0.org3.example.com:
container_name: peer0.org3.example.com
image: hyperledger/fabric-peer
environment:
- CORE_PEER_ID=peer0.org3.example.com
- CORE_PEER_LOCALMSPID=Org3MSP
- CORE_PEER_ADDRESS=peer0.org3.example.com:7051
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/peer/
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_kajmak
- CORE_LOGGING_PEER=debug
- CORE_CHAINCODE_LOGGING_LEVEL=DEBUG
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
ports:
- 9051:7051
- 9053:7053
volumes:
- /var/run/:/host/var/run/
- ./config/:/etc/hyperledger/configtx
- ./crypto-config/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/msp:/etc/hyperledger/crypto/peer
- ./crypto-config/peerOrganizations/org3.example.com/users:/etc/hyperledger/crypto/users
depends_on:
- orderer.example.com
networks:
- kajmak

peer1.org3.example.com:
container_name: peer1.org3.example.com
image: hyperledger/fabric-peer
environment:
- CORE_PEER_ID=peer1.org3.example.com
- CORE_PEER_LOCALMSPID=Org3MSP
- CORE_PEER_ADDRESS=peer1.org3.example.com:7051
- CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/peer/
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_kajmak
- CORE_LOGGING_PEER=debug
- CORE_CHAINCODE_LOGGING_LEVEL=DEBUG
working_dir: /opt/gopath/src/github.com/hyperledger/fabric
ports:
- 9056:7051
- 9058:7053
volumes:
- /var/run/:/host/var/run/
- ./config/:/etc/hyperledger/configtx
- ./crypto-config/peerOrganizations/org3.example.com/peers/peer1.org3.example.com/msp:/etc/hyperledger/crypto/peer
- ./crypto-config/peerOrganizations/org3.example.com/users:/etc/hyperledger/crypto/users
depends_on:
- orderer.example.com
networks:
- kajmak

Explanation of the docker-compose.yaml:

services: under this section, we list names of all components represented as containers that we will use in our network

Under the name of some specific service we have these sections:

container_name: name of a container

image: docker image that will have its instance in the docker container

environment: list of environment variables used to set configurations of the container. As you can see from the file above, a lot of different variables can be set, but most important ones that every container has are those that configure paths to certificates and private keys that can be found in msp folder (located in crypto-config folder) that every organization has.

working_dir: this sets the working directory for the container

ports: mapping the ports used by the docker internally. The syntax is host:container

command: defines command to use to start an orderer, peer, fabric-ca-server or some other component in the docker depending on what command is used

volumes: mapping directories between the host and the container. It tells where files located on the host are located in the container file system.The syntax is host_filesystem:container_failsystem .

depends_on: defines the dependency between containers. For example, docker will only boot up some specific container after the dependent container gets booted up, otherwise not.

networks: it is common for all services in this file. This basically defines the bridge network to be used by this container.

start.sh

In this script, you will see how to run containers, create a channel and join peers to channel.

#!/bin/bashset -e#delete containers
function dkcl(){
CONTAINER_IDS=$(docker ps -aq)
echo
if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" = " " ]; then
echo "========== No containers available for deletion =========="
else
docker rm -f $CONTAINER_IDS
fi
echo
}
#delete docker images
function dkrm(){
DOCKER_IMAGE_IDS=$(docker images | grep "dev\|none\|test-vp\|peer[0-9]-" | awk '{print $3}')
echo
if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" = " " ]; then
echo "========== No images available for deletion ==========="
else
docker rmi -f $DOCKER_IMAGE_IDS
fi
echo
}
function restartNetwork() {
echo
#teardown the network and clean the containers and intermediate images
docker-compose -f docker-compose.yaml down
dkcl
dkrm
#Cleanup the stores
rm -rf ./fabric-client-kv-org*
#Start the network
docker-compose -f docker-compose.yaml up -d ca.org1.example.com ca.org2.example.com ca.org3.example.com orderer.example.com peer0.org1.example.com peer1.org1.example.com peer0.org2.example.com peer1.org2.example.com peer0.org3.example.com peer1.org3.example.com
echo
}
restartNetworkexport FABRIC_START_TIMEOUT=20
sleep ${FABRIC_START_TIMEOUT}
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/users/org1/Admin@org1.example.com/msp" peer0.org1.example.com peer channel create -o orderer.example.com:7050 -c mychannel -f /etc/hyperledger/configtx/channel.txdocker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_ADDRESS=peer0.org1.example.com:7051" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/users/org1/Admin@org1.example.com/msp" peer0.org1.example.com peer channel join -b mychannel.block
docker exec -e "CORE_PEER_LOCALMSPID=Org1MSP" -e "CORE_PEER_ADDRESS=peer1.org1.example.com:7051" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/users/org1/Admin@org1.example.com/msp" peer0.org1.example.com peer channel join -b mychannel.block
docker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_ADDRESS=peer0.org2.example.com:7051" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/users/org2/Admin@org2.example.com/msp" peer0.org1.example.com peer channel join -b mychannel.blockdocker exec -e "CORE_PEER_LOCALMSPID=Org2MSP" -e "CORE_PEER_ADDRESS=peer1.org2.example.com:7051" -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/crypto/users/org2/Admin@org2.example.com/msp" peer0.org1.example.com peer channel join -b mychannel.block

First, we have to tear down the network, clean the containers and intermediate images in the case that something has left from the previous networks.

For that, we use command docker-compose and with -f we define which file to use (docker-compose.yaml in this case) and when we say down it will tear down everything that is running.

After that, we clean up local stores with command rm -rf ./fabric-client-kv-org*. That are folders where users are stored.

Now we can start the network using, again, command docker-compose and defining with -f file docker-compose.yaml. So we will “up’’ all containers that we mention after that command. With -d flag we specify that containers run in the background.

With export FABRIC_START_TIMEOUT=20 we will give system 20 seconds to start containers. If you put, for example, 10 sec you can get an error that says something like “its impossible to connect to container”. The reason is — not enough time.

So, stick to our recommended 20sec to stay errorless 😊.

In the next few lines, we will create a channel and join peers.

With command exec, we will enter container peer0.org1.example.com which we will use to create a channel. We specify environment variables that peer0 needs to create a channel.

So, we approach users/Admin@org1.example.com/msp because we need keys and IDs of the admin user from org1. Command peer channel create will create a channel. For that we also need to say who is orderer with -o, a name of the channel with -c and location of the channel configuration transaction with -f.

When the channel is created, we again use this container (peer0) to join different peers to this particular channel using the command peer channel join. It is important to forward mychannel.block which refers to the orderer genesis block. We joined peer0 and peer1 from org1 and peer0 and peer1 from org2 to the channel in our case.

Now our containers are running, the channel is created and peers are joined to our channel.

With the following command, you can list all running containers:

$ docker container ls

Now our network is up and running.

It was easy, right?

In the next article, you will learn how to write chaincode and interact with the blockchain network from the outside.

--

--