diff --git a/fabric/v1.3/fabric-ca/.env b/fabric/v1.3/fabric-ca/.env
new file mode 100644
index 0000000..4fd2ee0
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/.env
@@ -0,0 +1 @@
+COMPOSE_PROJECT_NAME=net
diff --git a/fabric/v1.3/fabric-ca/.gitignore b/fabric/v1.3/fabric-ca/.gitignore
new file mode 100644
index 0000000..d21ef6d
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/.gitignore
@@ -0,0 +1,5 @@
+docker-compose.yml
+fabric-ca-orderer.dockerfile
+fabric-ca-peer.dockerfile
+fabric-ca-tools.dockerfile
+data
diff --git a/fabric/v1.3/fabric-ca/README.md b/fabric/v1.3/fabric-ca/README.md
new file mode 100755
index 0000000..568a198
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/README.md
@@ -0,0 +1,102 @@
+# Hyperledger Fabric CA sample
+
+The Hyperledger Fabric CA sample demonstrates the following:
+
+* How to use the Hyperledger Fabric CA client and server to generate all crypto
+ material rather than using cryptogen. The cryptogen tool is not intended for
+ a production environment because it generates all private keys in one location
+ which must then be copied to the appropriate host or container. This sample
+ demonstrates how to generate crypto material for orderers, peers,
+ administrators, and end users so that private keys never leave the host or
+ container in which they are generated.
+
+* How to use Attribute-Based Access Control (ABAC). See
+ fabric-samples/chaincode/abac/abac.go and note the use of the *github.com/hyperledger/fabric/core/chaincode/lib/cid* package to extract
+ attributes from the invoker's identity. Only identities with the *abac.init*
+ attribute value of *true* can successfully call the *Init* function to
+ instantiate the chaincode.
+
+## Running this sample
+
+1. To run this sample, simply run the *start.sh* script. You may do this
+multiple times in a row as needed since the *start.sh* script cleans up before
+starting each time. This sample can be run with the latest released version,
+an older released version, or from locally built docker images as follows:
+
+ a. By default, the sample is run with the latest released version of Fabric
+ and Fabric CA.
+
+ b. Older versions of Fabric and Fabric CA can be used by setting the
+ `FABRIC_TAG` environment variable. For example, `export FABRIC_TAG=1.3.0`
+ will run the sample with 1.3.0 version of Fabric and Fabric CA.
+
+ c. The sample can also be run with locally built Fabric and Fabric CA
+ docker images. Fabric and Fabric CA repositories must be cloned with following
+ commands:
+
+ `git clone https://github.com/hyperledger/fabric.git`
+ `git clone https://github.com/hyperledger/fabric-ca.git`
+
+ Then execute the `make docker-all` command from the fabric-ca repository. This will
+ build the necessary images based on the local source code. Before executing the
+ *start.sh* script, set the `FABRIC_TAG` environment variable to 'local' as follows:
+ `export FABRIC_TAG=local`.
+
+2. To stop the containers which are started by the *start.sh* script, you may run the *stop.sh* script.
+
+## Understanding this sample
+
+There are some variables at the top of *fabric-samples/fabric-ca/scripts/env.sh*
+script which define the names and topology of this sample. You may modify these
+as described in the comments of the script in order to customize this sample.
+By default, there are three organizations. The orderer organization is *org0*,
+and two peer organizations are *org1* and *org2*.
+
+The *start.sh* script first builds the *docker-compose.yml* file (by invoking the
+*makeDocker.sh* script) and then starts the docker containers.
+The *data* directory is a volume mount for all containers.
+This volume mount is not be needed in a real scenario, but it is used by this
+sample for the following reasons:
+ a) so that all containers can write their logs to a common directory
+ (i.e. *the *data/logs* directory) to make debugging easier;
+ b) to synchronize the sequence in which containers start as described below
+ (for example, an intermediate CA in an *ica* container must wait for the
+ corresponding root CA in a *rca* container to write its certificate to
+ the *data* directory);
+ c) to access bootstrap certificates required by clients to connect over TLS.
+
+The containers defined in the *docker-compose.yml* file are started in the
+following sequence.
+
+1. The *rca* (root CA) containers start first, one for each organization.
+An *rca* container runs the fabric-ca-server for the root CA of an
+organization. The root CA certificate is written to the *data* directory
+and is used when an intermediate CA must connect to it over TLS.
+
+2. The *ica* (Intermediate CA) containers start next. An *ica* container
+runs the fabric-ca-server for the intermediate CA of an organization.
+Each of these containers enrolls with a corresponding root CA.
+The intermediate CA certificate is also written to the *data* directory.
+
+3. The *setup* container registers identities with the intermediate CAs,
+generates the genesis block, and other artifacts needed to setup the
+blockchain network. This is performed by the
+*fabric-samples/fabric-ca/scripts/setup-fabric.sh* script. Note that the
+admin identity is registered with **abac.init=true:ecert**
+(see the *registerPeerIdentities* function of this script). This causes
+the admin's enrollment certificate (ECert) to have an attribute named "abac.init"
+with a value of "true". Note further that the chaincode used by this sample
+requires this attribute be included in the certificate of the identity that
+invokes its Init function. See the chaincode at *fabric-samples/chaincode/abac/abac.go*).
+For more information on Attribute-Based Access Control (ABAC), see
+https://github.com/hyperledger/fabric/blob/master/core/chaincode/lib/cid/README.md.
+
+4. The orderer and peer containers are started. The naming of these containers
+is straight-forward as is their log files in the *data/logs* directory.
+
+5. The *run* container is started which runs the actual test case. It creates
+a channel, peers join the channel, chaincode is installed and instantiated,
+and the chaincode is queried and invoked. See the *main* function of the
+*fabric-samples/fabric-ca/scripts/run-fabric.sh* script for more details.
+
+
This work is licensed under a Creative Commons Attribution 4.0 International License
diff --git a/fabric/v1.3/fabric-ca/build-images.sh b/fabric/v1.3/fabric-ca/build-images.sh
new file mode 100755
index 0000000..e49bf4e
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/build-images.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+#
+# This script builds the images required to run this sample.
+#
+
+function assertOnMasterBranch {
+ if [ "`git rev-parse --abbrev-ref HEAD`" != "master" ]; then
+ fatal "You must switch to the master branch in `pwd`"
+ fi
+}
+
+set -e
+
+SDIR=$(dirname "$0")
+source $SDIR/scripts/env.sh
+
+# Delete docker containers
+dockerContainers=$(docker ps -a | awk '$2~/hyperledger/ {print $1}')
+if [ "$dockerContainers" != "" ]; then
+ log "Deleting existing docker containers ..."
+ docker rm -f $dockerContainers > /dev/null
+fi
+
+# Remove chaincode docker images
+chaincodeImages=`docker images | grep "^dev-peer" | awk '{print $3}'`
+if [ "$chaincodeImages" != "" ]; then
+ log "Removing chaincode docker images ..."
+ docker rmi $chaincodeImages > /dev/null
+fi
+
+# Perform docker clean for fabric-ca
+log "Cleaning fabric-ca docker images ..."
+cd $GOPATH/src/github.com/hyperledger/fabric-ca
+assertOnMasterBranch
+make docker-clean
+
+# Perform docker clean for fabric and rebuild
+log "Cleaning and rebuilding fabric docker images ..."
+cd $GOPATH/src/github.com/hyperledger/fabric
+assertOnMasterBranch
+make docker-clean docker
+
+# Perform docker clean for fabric and rebuild against latest fabric images just built
+log "Rebuilding fabric-ca docker images ..."
+cd $GOPATH/src/github.com/hyperledger/fabric-ca
+FABRIC_TAG=latest make docker
+
+log "Setup completed successfully. You may run the tests multiple times by running start.sh."
diff --git a/fabric/v1.3/fabric-ca/desktopConfig.yaml b/fabric/v1.3/fabric-ca/desktopConfig.yaml
new file mode 100644
index 0000000..4eed0cd
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/desktopConfig.yaml
@@ -0,0 +1,10 @@
+peerGrpcUrl: grpcs://localhost:7051
+peerEventUrl: grpcs://localhost:7053
+ordererUrl: grpcs://localhost:7050
+mspId: org1MSP
+certificate: data/orgs/org1/admin/msp/signcerts/cert.pem
+privateKey: data/orgs/org1/admin/msp/keystore/6a65f3b294f455b1dc41bfec77da60ed86570cb5a342a21f052d3930d0d4f7c0_sk
+peerTlsCaCert: data/org1-ca-chain.pem
+ordererTlsCaCert: data/org0-ca-chain.pem
+peerSslTarget: peer1-org1
+ordererSslTarget: orderer1-org0
diff --git a/fabric/v1.3/fabric-ca/makeDocker.sh b/fabric/v1.3/fabric-ca/makeDocker.sh
new file mode 100755
index 0000000..168503f
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/makeDocker.sh
@@ -0,0 +1,316 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+#
+# This script builds the docker compose file needed to run this sample.
+#
+
+# IMPORTANT: The following default FABRIC_TAG value should be updated for each
+# release after the fabric-orderer and fabric-peer images have been published
+# for the release.
+export FABRIC_TAG=${FABRIC_TAG:-1.3.0}
+
+export FABRIC_CA_TAG=${FABRIC_CA_TAG:-${FABRIC_TAG}}
+export NS=${NS:-hyperledger}
+export MARCH=$(echo "$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')-$(uname -m | sed 's/x86_64/amd64/g')" | awk '{print tolower($0)}')
+CA_BINARY_FILE=hyperledger-fabric-ca-${MARCH}-${FABRIC_CA_TAG}.tar.gz
+URL=https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric-ca/hyperledger-fabric-ca/${MARCH}-${FABRIC_CA_TAG}/${CA_BINARY_FILE}
+
+SDIR=$(dirname "$0")
+source $SDIR/scripts/env.sh
+
+function main {
+ {
+ createDockerFiles
+ writeHeader
+ writeRootFabricCA
+ if $USE_INTERMEDIATE_CA; then
+ writeIntermediateFabricCA
+ fi
+ writeSetupFabric
+ writeStartFabric
+ writeRunFabric
+ } > $SDIR/docker-compose.yml
+ log "Created docker-compose.yml"
+}
+
+# Create various dockerfiles used by this sample
+function createDockerFiles {
+ if [ "$FABRIC_TAG" = "local" ]; then
+ ORDERER_BUILD="image: hyperledger/fabric-ca-orderer"
+ PEER_BUILD="image: hyperledger/fabric-ca-peer"
+ TOOLS_BUILD="image: hyperledger/fabric-ca-tools"
+ else
+ createDockerFile orderer
+ ORDERER_BUILD="build:
+ context: .
+ dockerfile: fabric-ca-orderer.dockerfile"
+ createDockerFile peer
+ PEER_BUILD="build:
+ context: .
+ dockerfile: fabric-ca-peer.dockerfile"
+ createDockerFile tools
+ TOOLS_BUILD="build:
+ context: .
+ dockerfile: fabric-ca-tools.dockerfile"
+ fi
+}
+
+# createDockerFile
+function createDockerFile {
+ {
+ echo "FROM ${NS}/fabric-${1}:${FABRIC_TAG}"
+ echo 'RUN apt-get update && apt-get install -y netcat jq && apt-get install -y curl && rm -rf /var/cache/apt'
+ echo "RUN curl -o /tmp/fabric-ca-client.tar.gz $URL && tar -xzvf /tmp/fabric-ca-client.tar.gz -C /tmp && cp /tmp/bin/fabric-ca-client /usr/local/bin"
+ echo 'RUN chmod +x /usr/local/bin/fabric-ca-client'
+ echo 'ARG FABRIC_CA_DYNAMIC_LINK=false'
+ # libraries needed when image is built dynamically
+ echo 'RUN if [ "\$FABRIC_CA_DYNAMIC_LINK" = "true" ]; then apt-get install -y libltdl-dev; fi'
+ } > $SDIR/fabric-ca-${1}.dockerfile
+}
+
+# Write services for the root fabric CA servers
+function writeRootFabricCA {
+ for ORG in $ORGS; do
+ initOrgVars $ORG
+ writeRootCA
+ done
+}
+
+# Write services for the intermediate fabric CA servers
+function writeIntermediateFabricCA {
+ for ORG in $ORGS; do
+ initOrgVars $ORG
+ writeIntermediateCA
+ done
+}
+
+# Write a service to setup the fabric artifacts (e.g. genesis block, etc)
+function writeSetupFabric {
+ echo " setup:
+ container_name: setup
+ $TOOLS_BUILD
+ command: /bin/bash -c '/scripts/setup-fabric.sh 2>&1 | tee /$SETUP_LOGFILE; sleep 99999'
+ volumes:
+ - ./scripts:/scripts
+ - ./$DATA:/$DATA
+ networks:
+ - $NETWORK
+ depends_on:"
+ for ORG in $ORGS; do
+ initOrgVars $ORG
+ echo " - $CA_NAME"
+ done
+ echo ""
+}
+
+# Write services for fabric orderer and peer containers
+function writeStartFabric {
+ for ORG in $ORDERER_ORGS; do
+ COUNT=1
+ while [[ "$COUNT" -le $NUM_ORDERERS ]]; do
+ initOrdererVars $ORG $COUNT
+ writeOrderer
+ COUNT=$((COUNT+1))
+ done
+ done
+ for ORG in $PEER_ORGS; do
+ COUNT=1
+ while [[ "$COUNT" -le $NUM_PEERS ]]; do
+ initPeerVars $ORG $COUNT
+ writePeer
+ COUNT=$((COUNT+1))
+ done
+ done
+}
+
+# Write a service to run a fabric test including creating a channel,
+# installing chaincode, invoking and querying
+function writeRunFabric {
+ # Set samples directory relative to this script
+ SAMPLES_DIR=$(dirname $(cd ${SDIR} && pwd))
+ # Set fabric directory relative to GOPATH
+ FABRIC_DIR=${GOPATH}/src/github.com/hyperledger/fabric
+ echo " run:
+ container_name: run
+ image: hyperledger/fabric-ca-tools
+ environment:
+ - GOPATH=/opt/gopath
+ command: /bin/bash -c 'sleep 3;/scripts/run-fabric.sh 2>&1 | tee /$RUN_LOGFILE; sleep 99999'
+ volumes:
+ - ./scripts:/scripts
+ - ./$DATA:/$DATA
+ - ${SAMPLES_DIR}:/opt/gopath/src/github.com/hyperledger/fabric-samples
+ - ${FABRIC_DIR}:/opt/gopath/src/github.com/hyperledger/fabric
+ networks:
+ - $NETWORK
+ depends_on:"
+ for ORG in $ORDERER_ORGS; do
+ COUNT=1
+ while [[ "$COUNT" -le $NUM_ORDERERS ]]; do
+ initOrdererVars $ORG $COUNT
+ echo " - $ORDERER_NAME"
+ COUNT=$((COUNT+1))
+ done
+ done
+ for ORG in $PEER_ORGS; do
+ COUNT=1
+ while [[ "$COUNT" -le $NUM_PEERS ]]; do
+ initPeerVars $ORG $COUNT
+ echo " - $PEER_NAME"
+ COUNT=$((COUNT+1))
+ done
+ done
+}
+
+function writeRootCA {
+ echo " $ROOT_CA_NAME:
+ container_name: $ROOT_CA_NAME
+ image: hyperledger/fabric-ca
+ command: /bin/bash -c '/scripts/start-root-ca.sh 2>&1 | tee /$ROOT_CA_LOGFILE'
+ environment:
+ - FABRIC_CA_SERVER_HOME=/etc/hyperledger/fabric-ca
+ - FABRIC_CA_SERVER_TLS_ENABLED=true
+ - FABRIC_CA_SERVER_CSR_CN=$ROOT_CA_NAME
+ - FABRIC_CA_SERVER_CSR_HOSTS=$ROOT_CA_HOST
+ - FABRIC_CA_SERVER_DEBUG=true
+ - BOOTSTRAP_USER_PASS=$ROOT_CA_ADMIN_USER_PASS
+ - TARGET_CERTFILE=$ROOT_CA_CERTFILE
+ - FABRIC_ORGS="$ORGS"
+ volumes:
+ - ./scripts:/scripts
+ - ./$DATA:/$DATA
+ networks:
+ - $NETWORK
+"
+}
+
+function writeIntermediateCA {
+ echo " $INT_CA_NAME:
+ container_name: $INT_CA_NAME
+ image: hyperledger/fabric-ca
+ command: /bin/bash -c '/scripts/start-intermediate-ca.sh $ORG 2>&1 | tee /$INT_CA_LOGFILE'
+ environment:
+ - FABRIC_CA_SERVER_HOME=/etc/hyperledger/fabric-ca
+ - FABRIC_CA_SERVER_CA_NAME=$INT_CA_NAME
+ - FABRIC_CA_SERVER_INTERMEDIATE_TLS_CERTFILES=$ROOT_CA_CERTFILE
+ - FABRIC_CA_SERVER_CSR_HOSTS=$INT_CA_HOST
+ - FABRIC_CA_SERVER_TLS_ENABLED=true
+ - FABRIC_CA_SERVER_DEBUG=true
+ - BOOTSTRAP_USER_PASS=$INT_CA_ADMIN_USER_PASS
+ - PARENT_URL=https://$ROOT_CA_ADMIN_USER_PASS@$ROOT_CA_HOST:7054
+ - TARGET_CHAINFILE=$INT_CA_CHAINFILE
+ - ORG=$ORG
+ - FABRIC_ORGS="$ORGS"
+ volumes:
+ - ./scripts:/scripts
+ - ./$DATA:/$DATA
+ networks:
+ - $NETWORK
+ depends_on:
+ - $ROOT_CA_NAME
+"
+}
+
+function writeOrderer {
+ MYHOME=/etc/hyperledger/orderer
+ echo " $ORDERER_NAME:
+ container_name: $ORDERER_NAME
+ $ORDERER_BUILD
+ environment:
+ - FABRIC_CA_CLIENT_HOME=$MYHOME
+ - FABRIC_CA_CLIENT_TLS_CERTFILES=$CA_CHAINFILE
+ - ENROLLMENT_URL=https://$ORDERER_NAME_PASS@$CA_HOST:7054
+ - ORDERER_HOME=$MYHOME
+ - ORDERER_HOST=$ORDERER_HOST
+ - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
+ - ORDERER_GENERAL_GENESISMETHOD=file
+ - ORDERER_GENERAL_GENESISFILE=$GENESIS_BLOCK_FILE
+ - ORDERER_GENERAL_LOCALMSPID=$ORG_MSP_ID
+ - ORDERER_GENERAL_LOCALMSPDIR=$MYHOME/msp
+ - ORDERER_GENERAL_TLS_ENABLED=true
+ - ORDERER_GENERAL_TLS_PRIVATEKEY=$MYHOME/tls/server.key
+ - ORDERER_GENERAL_TLS_CERTIFICATE=$MYHOME/tls/server.crt
+ - ORDERER_GENERAL_TLS_ROOTCAS=[$CA_CHAINFILE]
+ - ORDERER_GENERAL_TLS_CLIENTAUTHREQUIRED=true
+ - ORDERER_GENERAL_TLS_CLIENTROOTCAS=[$CA_CHAINFILE]
+ - ORDERER_GENERAL_LOGLEVEL=debug
+ - ORDERER_DEBUG_BROADCASTTRACEDIR=$LOGDIR
+ - ORG=$ORG
+ - ORG_ADMIN_CERT=$ORG_ADMIN_CERT
+ command: /bin/bash -c '/scripts/start-orderer.sh 2>&1 | tee /$ORDERER_LOGFILE'
+ volumes:
+ - ./scripts:/scripts
+ - ./$DATA:/$DATA
+ networks:
+ - $NETWORK
+ depends_on:
+ - setup
+"
+}
+
+function writePeer {
+ MYHOME=/opt/gopath/src/github.com/hyperledger/fabric/peer
+ echo " $PEER_NAME:
+ container_name: $PEER_NAME
+ $PEER_BUILD
+ environment:
+ - FABRIC_CA_CLIENT_HOME=$MYHOME
+ - FABRIC_CA_CLIENT_TLS_CERTFILES=$CA_CHAINFILE
+ - ENROLLMENT_URL=https://$PEER_NAME_PASS@$CA_HOST:7054
+ - PEER_NAME=$PEER_NAME
+ - PEER_HOME=$MYHOME
+ - PEER_HOST=$PEER_HOST
+ - PEER_NAME_PASS=$PEER_NAME_PASS
+ - CORE_PEER_ID=$PEER_HOST
+ - CORE_PEER_ADDRESS=$PEER_HOST:7051
+ - CORE_PEER_LOCALMSPID=$ORG_MSP_ID
+ - CORE_PEER_MSPCONFIGPATH=$MYHOME/msp
+ - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
+ - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=net_${NETWORK}
+ - CORE_LOGGING_LEVEL=DEBUG
+ - CORE_PEER_TLS_ENABLED=true
+ - CORE_PEER_TLS_CERT_FILE=$MYHOME/tls/server.crt
+ - CORE_PEER_TLS_KEY_FILE=$MYHOME/tls/server.key
+ - CORE_PEER_TLS_ROOTCERT_FILE=$CA_CHAINFILE
+ - CORE_PEER_TLS_CLIENTAUTHREQUIRED=true
+ - CORE_PEER_TLS_CLIENTROOTCAS_FILES=$CA_CHAINFILE
+ - CORE_PEER_TLS_CLIENTCERT_FILE=/$DATA/tls/$PEER_NAME-client.crt
+ - CORE_PEER_TLS_CLIENTKEY_FILE=/$DATA/tls/$PEER_NAME-client.key
+ - CORE_PEER_GOSSIP_USELEADERELECTION=true
+ - CORE_PEER_GOSSIP_ORGLEADER=false
+ - CORE_PEER_GOSSIP_EXTERNALENDPOINT=$PEER_HOST:7051
+ - CORE_PEER_GOSSIP_SKIPHANDSHAKE=true
+ - ORG=$ORG
+ - ORG_ADMIN_CERT=$ORG_ADMIN_CERT"
+ if [ $NUM -gt 1 ]; then
+ echo " - CORE_PEER_GOSSIP_BOOTSTRAP=peer1-${ORG}:7051"
+ fi
+ echo " working_dir: $MYHOME
+ command: /bin/bash -c '/scripts/start-peer.sh 2>&1 | tee /$PEER_LOGFILE'
+ volumes:
+ - ./scripts:/scripts
+ - ./$DATA:/$DATA
+ - /var/run:/host/var/run
+ networks:
+ - $NETWORK
+ depends_on:
+ - setup
+"
+}
+
+function writeHeader {
+ echo "version: '2'
+
+networks:
+ $NETWORK:
+
+services:
+"
+}
+
+main
diff --git a/fabric/v1.3/fabric-ca/scripts/env.sh b/fabric/v1.3/fabric-ca/scripts/env.sh
new file mode 100755
index 0000000..ca0ecfa
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/scripts/env.sh
@@ -0,0 +1,402 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+#
+# The following variables describe the topology and may be modified to provide
+# different organization names or the number of peers in each peer organization.
+#
+
+# Name of the docker-compose network
+NETWORK=fabric-ca
+
+# Names of the orderer organizations
+ORDERER_ORGS="org0"
+
+# Names of the peer organizations
+PEER_ORGS="org1 org2"
+
+# Number of peers in each peer organization
+NUM_PEERS=2
+
+#
+# The remainder of this file contains variables which typically would not be changed.
+#
+
+# All org names
+ORGS="$ORDERER_ORGS $PEER_ORGS"
+
+# Set to true to populate the "admincerts" folder of MSPs
+ADMINCERTS=true
+
+# Number of orderer nodes
+NUM_ORDERERS=1
+
+# The volume mount to share data between containers
+DATA=data
+
+# The path to the genesis block
+GENESIS_BLOCK_FILE=/$DATA/genesis.block
+
+# The path to a channel transaction
+CHANNEL_TX_FILE=/$DATA/channel.tx
+
+# Name of test channel
+CHANNEL_NAME=mychannel
+
+# Query timeout in seconds
+QUERY_TIMEOUT=15
+
+# Setup timeout in seconds (for setup container to complete)
+SETUP_TIMEOUT=120
+
+# Log directory
+LOGDIR=$DATA/logs
+LOGPATH=/$LOGDIR
+
+# Name of a the file to create when setup is successful
+SETUP_SUCCESS_FILE=${LOGDIR}/setup.successful
+# The setup container's log file
+SETUP_LOGFILE=${LOGDIR}/setup.log
+
+# The run container's log file
+RUN_LOGFILE=${LOGDIR}/run.log
+# The run container's summary log file
+RUN_SUMFILE=${LOGDIR}/run.sum
+RUN_SUMPATH=/${RUN_SUMFILE}
+# Run success and failure files
+RUN_SUCCESS_FILE=${LOGDIR}/run.success
+RUN_FAIL_FILE=${LOGDIR}/run.fail
+
+# Affiliation is not used to limit users in this sample, so just put
+# all identities in the same affiliation.
+export FABRIC_CA_CLIENT_ID_AFFILIATION=org1
+
+# Set to true to enable use of intermediate CAs
+USE_INTERMEDIATE_CA=true
+
+
+# Config block file path
+CONFIG_BLOCK_FILE=/tmp/config_block.pb
+
+# Update config block payload file path
+CONFIG_UPDATE_ENVELOPE_FILE=/tmp/config_update_as_envelope.pb
+
+# initOrgVars
+function initOrgVars {
+ if [ $# -ne 1 ]; then
+ echo "Usage: initOrgVars "
+ exit 1
+ fi
+ ORG=$1
+ ORG_CONTAINER_NAME=${ORG//./-}
+ ROOT_CA_HOST=rca-${ORG}
+ ROOT_CA_NAME=rca-${ORG}
+ ROOT_CA_LOGFILE=$LOGDIR/${ROOT_CA_NAME}.log
+ INT_CA_HOST=ica-${ORG}
+ INT_CA_NAME=ica-${ORG}
+ INT_CA_LOGFILE=$LOGDIR/${INT_CA_NAME}.log
+
+ # Root CA admin identity
+ ROOT_CA_ADMIN_USER=rca-${ORG}-admin
+ ROOT_CA_ADMIN_PASS=${ROOT_CA_ADMIN_USER}pw
+ ROOT_CA_ADMIN_USER_PASS=${ROOT_CA_ADMIN_USER}:${ROOT_CA_ADMIN_PASS}
+ # Root CA intermediate identity to bootstrap the intermediate CA
+ ROOT_CA_INT_USER=ica-${ORG}
+ ROOT_CA_INT_PASS=${ROOT_CA_INT_USER}pw
+ ROOT_CA_INT_USER_PASS=${ROOT_CA_INT_USER}:${ROOT_CA_INT_PASS}
+ # Intermediate CA admin identity
+ INT_CA_ADMIN_USER=ica-${ORG}-admin
+ INT_CA_ADMIN_PASS=${INT_CA_ADMIN_USER}pw
+ INT_CA_ADMIN_USER_PASS=${INT_CA_ADMIN_USER}:${INT_CA_ADMIN_PASS}
+ # Admin identity for the org
+ ADMIN_NAME=admin-${ORG}
+ ADMIN_PASS=${ADMIN_NAME}pw
+ # Typical user identity for the org
+ USER_NAME=user-${ORG}
+ USER_PASS=${USER_NAME}pw
+
+ ROOT_CA_CERTFILE=/${DATA}/${ORG}-ca-cert.pem
+ INT_CA_CHAINFILE=/${DATA}/${ORG}-ca-chain.pem
+ ANCHOR_TX_FILE=/${DATA}/orgs/${ORG}/anchors.tx
+ ORG_MSP_ID=${ORG}MSP
+ ORG_MSP_DIR=/${DATA}/orgs/${ORG}/msp
+ ORG_ADMIN_CERT=${ORG_MSP_DIR}/admincerts/cert.pem
+ ORG_ADMIN_HOME=/${DATA}/orgs/$ORG/admin
+
+ if test "$USE_INTERMEDIATE_CA" = "true"; then
+ CA_NAME=$INT_CA_NAME
+ CA_HOST=$INT_CA_HOST
+ CA_CHAINFILE=$INT_CA_CHAINFILE
+ CA_ADMIN_USER_PASS=$INT_CA_ADMIN_USER_PASS
+ CA_LOGFILE=$INT_CA_LOGFILE
+ else
+ CA_NAME=$ROOT_CA_NAME
+ CA_HOST=$ROOT_CA_HOST
+ CA_CHAINFILE=$ROOT_CA_CERTFILE
+ CA_ADMIN_USER_PASS=$ROOT_CA_ADMIN_USER_PASS
+ CA_LOGFILE=$ROOT_CA_LOGFILE
+ fi
+}
+
+# initOrdererVars
+function initOrdererVars {
+ if [ $# -ne 2 ]; then
+ echo "Usage: initOrdererVars "
+ exit 1
+ fi
+ initOrgVars $1
+ NUM=$2
+ ORDERER_HOST=orderer${NUM}-${ORG}
+ ORDERER_NAME=orderer${NUM}-${ORG}
+ ORDERER_PASS=${ORDERER_NAME}pw
+ ORDERER_NAME_PASS=${ORDERER_NAME}:${ORDERER_PASS}
+ ORDERER_LOGFILE=$LOGDIR/${ORDERER_NAME}.log
+ MYHOME=/etc/hyperledger/orderer
+
+ export FABRIC_CA_CLIENT=$MYHOME
+ export ORDERER_GENERAL_LOGLEVEL=debug
+ export ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
+ export ORDERER_GENERAL_GENESISMETHOD=file
+ export ORDERER_GENERAL_GENESISFILE=$GENESIS_BLOCK_FILE
+ export ORDERER_GENERAL_LOCALMSPID=$ORG_MSP_ID
+ export ORDERER_GENERAL_LOCALMSPDIR=$MYHOME/msp
+ # enabled TLS
+ export ORDERER_GENERAL_TLS_ENABLED=true
+ TLSDIR=$MYHOME/tls
+ export ORDERER_GENERAL_TLS_PRIVATEKEY=$TLSDIR/server.key
+ export ORDERER_GENERAL_TLS_CERTIFICATE=$TLSDIR/server.crt
+ export ORDERER_GENERAL_TLS_ROOTCAS=[$CA_CHAINFILE]
+}
+
+function genClientTLSCert {
+ if [ $# -ne 3 ]; then
+ echo "Usage: genClientTLSCert : $*"
+ exit 1
+ fi
+
+ HOST_NAME=$1
+ CERT_FILE=$2
+ KEY_FILE=$3
+
+ # Get a client cert
+ fabric-ca-client enroll -d --enrollment.profile tls -u $ENROLLMENT_URL -M /tmp/tls --csr.hosts $HOST_NAME
+
+ mkdir /$DATA/tls || true
+ cp /tmp/tls/signcerts/* $CERT_FILE
+ cp /tmp/tls/keystore/* $KEY_FILE
+ rm -rf /tmp/tls
+}
+
+# initPeerVars
+function initPeerVars {
+ if [ $# -ne 2 ]; then
+ echo "Usage: initPeerVars : $*"
+ exit 1
+ fi
+ initOrgVars $1
+ NUM=$2
+ PEER_HOST=peer${NUM}-${ORG}
+ PEER_NAME=peer${NUM}-${ORG}
+ PEER_PASS=${PEER_NAME}pw
+ PEER_NAME_PASS=${PEER_NAME}:${PEER_PASS}
+ PEER_LOGFILE=$LOGDIR/${PEER_NAME}.log
+ MYHOME=/opt/gopath/src/github.com/hyperledger/fabric/peer
+ TLSDIR=$MYHOME/tls
+
+ export FABRIC_CA_CLIENT=$MYHOME
+ export CORE_PEER_ID=$PEER_HOST
+ export CORE_PEER_ADDRESS=$PEER_HOST:7051
+ export CORE_PEER_LOCALMSPID=$ORG_MSP_ID
+ export 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/
+ #export CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_${NETWORK}
+ export CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=net_${NETWORK}
+ # export CORE_LOGGING_LEVEL=ERROR
+ export CORE_LOGGING_LEVEL=DEBUG
+ export CORE_PEER_TLS_ENABLED=true
+ export CORE_PEER_TLS_CLIENTAUTHREQUIRED=true
+ export CORE_PEER_TLS_ROOTCERT_FILE=$CA_CHAINFILE
+ export CORE_PEER_TLS_CLIENTCERT_FILE=/$DATA/tls/$PEER_NAME-cli-client.crt
+ export CORE_PEER_TLS_CLIENTKEY_FILE=/$DATA/tls/$PEER_NAME-cli-client.key
+ export CORE_PEER_PROFILE_ENABLED=true
+ # gossip variables
+ export CORE_PEER_GOSSIP_USELEADERELECTION=true
+ export CORE_PEER_GOSSIP_ORGLEADER=false
+ export CORE_PEER_GOSSIP_EXTERNALENDPOINT=$PEER_HOST:7051
+ if [ $NUM -gt 1 ]; then
+ # Point the non-anchor peers to the anchor peer, which is always the 1st peer
+ export CORE_PEER_GOSSIP_BOOTSTRAP=peer1-${ORG}:7051
+ fi
+ export ORDERER_CONN_ARGS="$ORDERER_PORT_ARGS --keyfile $CORE_PEER_TLS_CLIENTKEY_FILE --certfile $CORE_PEER_TLS_CLIENTCERT_FILE"
+}
+
+# Switch to the current org's admin identity. Enroll if not previously enrolled.
+function switchToAdminIdentity {
+ if [ ! -d $ORG_ADMIN_HOME ]; then
+ dowait "$CA_NAME to start" 60 $CA_LOGFILE $CA_CHAINFILE
+ log "Enrolling admin '$ADMIN_NAME' with $CA_HOST ..."
+ export FABRIC_CA_CLIENT_HOME=$ORG_ADMIN_HOME
+ export FABRIC_CA_CLIENT_TLS_CERTFILES=$CA_CHAINFILE
+ fabric-ca-client enroll -d -u https://$ADMIN_NAME:$ADMIN_PASS@$CA_HOST:7054
+ # If admincerts are required in the MSP, copy the cert there now and to my local MSP also
+ if [ $ADMINCERTS ]; then
+ mkdir -p $(dirname "${ORG_ADMIN_CERT}")
+ cp $ORG_ADMIN_HOME/msp/signcerts/* $ORG_ADMIN_CERT
+ mkdir $ORG_ADMIN_HOME/msp/admincerts
+ cp $ORG_ADMIN_HOME/msp/signcerts/* $ORG_ADMIN_HOME/msp/admincerts
+ fi
+ fi
+ export CORE_PEER_MSPCONFIGPATH=$ORG_ADMIN_HOME/msp
+}
+
+# Switch to the current org's user identity. Enroll if not previously enrolled.
+function switchToUserIdentity {
+ export FABRIC_CA_CLIENT_HOME=/etc/hyperledger/fabric/orgs/$ORG/user
+ export CORE_PEER_MSPCONFIGPATH=$FABRIC_CA_CLIENT_HOME/msp
+ if [ ! -d $FABRIC_CA_CLIENT_HOME ]; then
+ dowait "$CA_NAME to start" 60 $CA_LOGFILE $CA_CHAINFILE
+ log "Enrolling user for organization $ORG with home directory $FABRIC_CA_CLIENT_HOME ..."
+ export FABRIC_CA_CLIENT_TLS_CERTFILES=$CA_CHAINFILE
+ fabric-ca-client enroll -d -u https://$USER_NAME:$USER_PASS@$CA_HOST:7054
+ # Set up admincerts directory if required
+ if [ $ADMINCERTS ]; then
+ ACDIR=$CORE_PEER_MSPCONFIGPATH/admincerts
+ mkdir -p $ACDIR
+ cp $ORG_ADMIN_HOME/msp/signcerts/* $ACDIR
+ fi
+ fi
+}
+
+# Revokes the fabric user
+function revokeFabricUserAndGenerateCRL {
+ switchToAdminIdentity
+ export FABRIC_CA_CLIENT_HOME=$ORG_ADMIN_HOME
+ logr "Revoking the user '$USER_NAME' of the organization '$ORG' with Fabric CA Client home directory set to $FABRIC_CA_CLIENT_HOME and generating CRL ..."
+ export FABRIC_CA_CLIENT_TLS_CERTFILES=$CA_CHAINFILE
+ fabric-ca-client revoke -d --revoke.name $USER_NAME --gencrl
+}
+
+# Generates a CRL that contains serial numbers of all revoked enrollment certificates.
+# The generated CRL is placed in the crls folder of the admin's MSP
+function generateCRL {
+ switchToAdminIdentity
+ export FABRIC_CA_CLIENT_HOME=$ORG_ADMIN_HOME
+ logr "Generating CRL for the organization '$ORG' with Fabric CA Client home directory set to $FABRIC_CA_CLIENT_HOME ..."
+ export FABRIC_CA_CLIENT_TLS_CERTFILES=$CA_CHAINFILE
+ fabric-ca-client gencrl -d
+}
+
+# Copy the org's admin cert into some target MSP directory
+# This is only required if ADMINCERTS is enabled.
+function copyAdminCert {
+ if [ $# -ne 1 ]; then
+ fatal "Usage: copyAdminCert "
+ fi
+ if $ADMINCERTS; then
+ dstDir=$1/admincerts
+ mkdir -p $dstDir
+ dowait "$ORG administator to enroll" 60 $SETUP_LOGFILE $ORG_ADMIN_CERT
+ cp $ORG_ADMIN_CERT $dstDir
+ fi
+}
+
+# Create the TLS directories of the MSP folder if they don't exist.
+# The fabric-ca-client should do this.
+function finishMSPSetup {
+ if [ $# -ne 1 ]; then
+ fatal "Usage: finishMSPSetup "
+ fi
+ if [ ! -d $1/tlscacerts ]; then
+ mkdir $1/tlscacerts
+ cp $1/cacerts/* $1/tlscacerts
+ if [ -d $1/intermediatecerts ]; then
+ mkdir $1/tlsintermediatecerts
+ cp $1/intermediatecerts/* $1/tlsintermediatecerts
+ fi
+ fi
+}
+
+function awaitSetup {
+ dowait "the 'setup' container to finish registering identities, creating the genesis block and other artifacts" $SETUP_TIMEOUT $SETUP_LOGFILE /$SETUP_SUCCESS_FILE
+}
+
+# Wait for one or more files to exist
+# Usage: dowait [ ...]
+function dowait {
+ if [ $# -lt 4 ]; then
+ fatal "Usage: dowait: $*"
+ fi
+ local what=$1
+ local secs=$2
+ local logFile=$3
+ shift 3
+ local logit=true
+ local starttime=$(date +%s)
+ for file in $*; do
+ until [ -f $file ]; do
+ if [ "$logit" = true ]; then
+ log -n "Waiting for $what ..."
+ logit=false
+ fi
+ sleep 1
+ if [ "$(($(date +%s)-starttime))" -gt "$secs" ]; then
+ echo ""
+ fatal "Failed waiting for $what ($file not found); see $logFile"
+ fi
+ echo -n "."
+ done
+ done
+ echo ""
+}
+
+# Wait for a process to begin to listen on a particular host and port
+# Usage: waitPort
+function waitPort {
+ set +e
+ local what=$1
+ local secs=$2
+ local logFile=$3
+ local host=$4
+ local port=$5
+ nc -z $host $port > /dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ log -n "Waiting for $what ..."
+ local starttime=$(date +%s)
+ while true; do
+ sleep 1
+ nc -z $host $port > /dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ break
+ fi
+ if [ "$(($(date +%s)-starttime))" -gt "$secs" ]; then
+ fatal "Failed waiting for $what; see $logFile"
+ fi
+ echo -n "."
+ done
+ echo ""
+ fi
+ set -e
+}
+
+
+# log a message
+function log {
+ if [ "$1" = "-n" ]; then
+ shift
+ echo -n "##### `date '+%Y-%m-%d %H:%M:%S'` $*"
+ else
+ echo "##### `date '+%Y-%m-%d %H:%M:%S'` $*"
+ fi
+}
+
+# fatal a message
+function fatal {
+ log "FATAL: $*"
+ exit 1
+}
diff --git a/fabric/v1.3/fabric-ca/scripts/run-fabric.sh b/fabric/v1.3/fabric-ca/scripts/run-fabric.sh
new file mode 100755
index 0000000..c812f96
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/scripts/run-fabric.sh
@@ -0,0 +1,291 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+set -e
+
+source $(dirname "$0")/env.sh
+
+function main {
+
+ done=false
+
+ # Wait for setup to complete and then wait another 10 seconds for the orderer and peers to start
+ awaitSetup
+ sleep 10
+
+ trap finish EXIT
+
+ mkdir -p $LOGPATH
+ logr "The docker 'run' container has started"
+
+ # Set ORDERER_PORT_ARGS to the args needed to communicate with the 1st orderer
+ IFS=', ' read -r -a OORGS <<< "$ORDERER_ORGS"
+ initOrdererVars ${OORGS[0]} 1
+ export ORDERER_PORT_ARGS="-o $ORDERER_HOST:7050 --tls --cafile $CA_CHAINFILE --clientauth"
+
+ # Convert PEER_ORGS to an array named PORGS
+ IFS=', ' read -r -a PORGS <<< "$PEER_ORGS"
+
+ # Create the channel
+ createChannel
+
+ # All peers join the channel
+ for ORG in $PEER_ORGS; do
+ local COUNT=1
+ while [[ "$COUNT" -le $NUM_PEERS ]]; do
+ initPeerVars $ORG $COUNT
+ joinChannel
+ COUNT=$((COUNT+1))
+ done
+ done
+
+ # Update the anchor peers
+ for ORG in $PEER_ORGS; do
+ initPeerVars $ORG 1
+ switchToAdminIdentity
+ logr "Updating anchor peers for $PEER_HOST ..."
+ peer channel update -c $CHANNEL_NAME -f $ANCHOR_TX_FILE $ORDERER_CONN_ARGS
+ done
+
+ # Install chaincode on the 1st peer in each org
+ for ORG in $PEER_ORGS; do
+ initPeerVars $ORG 1
+ installChaincode
+ done
+
+ # Instantiate chaincode on the 1st peer of the 2nd org
+ makePolicy
+ initPeerVars ${PORGS[1]} 1
+ switchToAdminIdentity
+ logr "Instantiating chaincode on $PEER_HOST ..."
+ peer chaincode instantiate -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "$POLICY" $ORDERER_CONN_ARGS
+
+ # Query chaincode from the 1st peer of the 1st org
+ initPeerVars ${PORGS[0]} 1
+ switchToUserIdentity
+ chaincodeQuery 100
+
+ # Invoke chaincode on the 1st peer of the 1st org
+ initPeerVars ${PORGS[0]} 1
+ switchToUserIdentity
+ logr "Sending invoke transaction to $PEER_HOST ..."
+ peer chaincode invoke -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' $ORDERER_CONN_ARGS
+
+ ## Install chaincode on 2nd peer of 2nd org
+ initPeerVars ${PORGS[1]} 2
+ installChaincode
+
+ # Query chaincode on 2nd peer of 2nd org
+ sleep 10
+ initPeerVars ${PORGS[1]} 2
+ switchToUserIdentity
+ chaincodeQuery 90
+
+ initPeerVars ${PORGS[0]} 1
+ switchToUserIdentity
+
+ # Revoke the user and generate CRL using admin's credentials
+ revokeFabricUserAndGenerateCRL
+
+ # Fetch config block
+ fetchConfigBlock
+
+ # Create config update envelope with CRL and update the config block of the channel
+ createConfigUpdatePayloadWithCRL
+ updateConfigBlock
+
+ # querying the chaincode should fail as the user is revoked
+ switchToUserIdentity
+ queryAsRevokedUser
+ if [ "$?" -ne 0 ]; then
+ logr "The revoked user $USER_NAME should have failed to query the chaincode in the channel '$CHANNEL_NAME'"
+ exit 1
+ fi
+ logr "Congratulations! The tests ran successfully."
+
+ done=true
+}
+
+# Enroll as a peer admin and create the channel
+function createChannel {
+ initPeerVars ${PORGS[0]} 1
+ switchToAdminIdentity
+ logr "Creating channel '$CHANNEL_NAME' on $ORDERER_HOST ..."
+ peer channel create --logging-level=DEBUG -c $CHANNEL_NAME -f $CHANNEL_TX_FILE $ORDERER_CONN_ARGS
+}
+
+# Enroll as a fabric admin and join the channel
+function joinChannel {
+ switchToAdminIdentity
+ set +e
+ local COUNT=1
+ MAX_RETRY=10
+ while true; do
+ logr "Peer $PEER_HOST is attempting to join channel '$CHANNEL_NAME' (attempt #${COUNT}) ..."
+ peer channel join -b $CHANNEL_NAME.block
+ if [ $? -eq 0 ]; then
+ set -e
+ logr "Peer $PEER_HOST successfully joined channel '$CHANNEL_NAME'"
+ return
+ fi
+ if [ $COUNT -gt $MAX_RETRY ]; then
+ fatalr "Peer $PEER_HOST failed to join channel '$CHANNEL_NAME' in $MAX_RETRY retries"
+ fi
+ COUNT=$((COUNT+1))
+ sleep 1
+ done
+}
+
+function chaincodeQuery {
+ if [ $# -ne 1 ]; then
+ fatalr "Usage: chaincodeQuery "
+ fi
+ set +e
+ logr "Querying chaincode in the channel '$CHANNEL_NAME' on the peer '$PEER_HOST' ..."
+ local rc=1
+ local starttime=$(date +%s)
+ # Continue to poll until we get a successful response or reach QUERY_TIMEOUT
+ while test "$(($(date +%s)-starttime))" -lt "$QUERY_TIMEOUT"; do
+ sleep 1
+ peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' >& log.txt
+ VALUE=$(cat log.txt | awk '/Query Result/ {print $NF}')
+ if [ $? -eq 0 -a "$VALUE" = "$1" ]; then
+ logr "Query of channel '$CHANNEL_NAME' on peer '$PEER_HOST' was successful"
+ set -e
+ return 0
+ else
+ # removed the string "Query Result" from peer chaincode query command result, as a result, have to support both options until the change is merged.
+ VALUE=$(cat log.txt | egrep '^[0-9]+$')
+ if [ $? -eq 0 -a "$VALUE" = "$1" ]; then
+ logr "Query of channel '$CHANNEL_NAME' on peer '$PEER_HOST' was successful"
+ set -e
+ return 0
+ fi
+ fi
+ echo -n "."
+ done
+ cat log.txt
+ cat log.txt >> $RUN_SUMFILE
+ fatalr "Failed to query channel '$CHANNEL_NAME' on peer '$PEER_HOST'; expected value was $1 and found $VALUE"
+}
+
+function queryAsRevokedUser {
+ set +e
+ logr "Querying the chaincode in the channel '$CHANNEL_NAME' on the peer '$PEER_HOST' as revoked user '$USER_NAME' ..."
+ local starttime=$(date +%s)
+ # Continue to poll until we get an expected response or reach QUERY_TIMEOUT
+ while test "$(($(date +%s)-starttime))" -lt "$QUERY_TIMEOUT"; do
+ sleep 1
+ peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' >& log.txt
+ if [ $? -ne 0 ]; then
+ err=$(cat log.txt | grep "access denied")
+ if [ "$err" != "" ]; then
+ logr "Expected error occurred when the revoked user '$USER_NAME' queried the chaincode in the channel '$CHANNEL_NAME'"
+ set -e
+ return 0
+ fi
+ fi
+ echo -n "."
+ done
+ set -e
+ cat log.txt
+ cat log.txt >> $RUN_SUMFILE
+ return 1
+}
+
+function makePolicy {
+ POLICY="OR("
+ local COUNT=0
+ for ORG in $PEER_ORGS; do
+ if [ $COUNT -ne 0 ]; then
+ POLICY="${POLICY},"
+ fi
+ initOrgVars $ORG
+ POLICY="${POLICY}'${ORG_MSP_ID}.member'"
+ COUNT=$((COUNT+1))
+ done
+ POLICY="${POLICY})"
+ log "policy: $POLICY"
+}
+
+function installChaincode {
+ switchToAdminIdentity
+ logr "Installing chaincode on $PEER_HOST ..."
+ peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric-samples/chaincode/abac/go
+}
+
+function fetchConfigBlock {
+ logr "Fetching the configuration block of the channel '$CHANNEL_NAME'"
+ peer channel fetch config $CONFIG_BLOCK_FILE -c $CHANNEL_NAME $ORDERER_CONN_ARGS
+}
+
+function updateConfigBlock {
+ logr "Updating the configuration block of the channel '$CHANNEL_NAME'"
+ peer channel update -f $CONFIG_UPDATE_ENVELOPE_FILE -c $CHANNEL_NAME $ORDERER_CONN_ARGS
+}
+
+function createConfigUpdatePayloadWithCRL {
+ logr "Creating config update payload with the generated CRL for the organization '$ORG'"
+ # Start the configtxlator
+ configtxlator start &
+ configtxlator_pid=$!
+ log "configtxlator_pid:$configtxlator_pid"
+ logr "Sleeping 5 seconds for configtxlator to start..."
+ sleep 5
+
+ pushd /tmp
+
+ CTLURL=http://127.0.0.1:7059
+ # Convert the config block protobuf to JSON
+ curl -X POST --data-binary @$CONFIG_BLOCK_FILE $CTLURL/protolator/decode/common.Block > config_block.json
+ # Extract the config from the config block
+ jq .data.data[0].payload.data.config config_block.json > config.json
+
+ # Update crl in the config json
+ CRL=$(cat $CORE_PEER_MSPCONFIGPATH/crls/crl*.pem | base64 | tr -d '\n')
+ cat config.json | jq --arg org "$ORG" --arg crl "$CRL" '.channel_group.groups.Application.groups[$org].values.MSP.value.config.revocation_list = [$crl]' > updated_config.json
+
+ # Create the config diff protobuf
+ curl -X POST --data-binary @config.json $CTLURL/protolator/encode/common.Config > config.pb
+ curl -X POST --data-binary @updated_config.json $CTLURL/protolator/encode/common.Config > updated_config.pb
+ curl -X POST -F original=@config.pb -F updated=@updated_config.pb $CTLURL/configtxlator/compute/update-from-configs -F channel=$CHANNEL_NAME > config_update.pb
+
+ # Convert the config diff protobuf to JSON
+ curl -X POST --data-binary @config_update.pb $CTLURL/protolator/decode/common.ConfigUpdate > config_update.json
+
+ # Create envelope protobuf container config diff to be used in the "peer channel update" command to update the channel configuration block
+ echo '{"payload":{"header":{"channel_header":{"channel_id":"'"${CHANNEL_NAME}"'", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' > config_update_as_envelope.json
+ curl -X POST --data-binary @config_update_as_envelope.json $CTLURL/protolator/encode/common.Envelope > $CONFIG_UPDATE_ENVELOPE_FILE
+
+ # Stop configtxlator
+ kill $configtxlator_pid
+
+ popd
+}
+
+function finish {
+ if [ "$done" = true ]; then
+ logr "See $RUN_LOGFILE for more details"
+ touch /$RUN_SUCCESS_FILE
+ else
+ logr "Tests did not complete successfully; see $RUN_LOGFILE for more details"
+ touch /$RUN_FAIL_FILE
+ exit 1
+ fi
+}
+
+function logr {
+ log $*
+ log $* >> $RUN_SUMPATH
+}
+
+function fatalr {
+ logr "FATAL: $*"
+ exit 1
+}
+
+main
diff --git a/fabric/v1.3/fabric-ca/scripts/setup-fabric.sh b/fabric/v1.3/fabric-ca/scripts/setup-fabric.sh
new file mode 100755
index 0000000..48c3c57
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/scripts/setup-fabric.sh
@@ -0,0 +1,287 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+#
+# This script does the following:
+# 1) registers orderer and peer identities with intermediate fabric-ca-servers
+# 2) Builds the channel artifacts (e.g. genesis block, etc)
+#
+
+function main {
+ log "Beginning building channel artifacts ..."
+ registerIdentities
+ getCACerts
+ makeConfigTxYaml
+ generateChannelArtifacts
+ log "Finished building channel artifacts"
+ touch /$SETUP_SUCCESS_FILE
+}
+
+# Enroll the CA administrator
+function enrollCAAdmin {
+ waitPort "$CA_NAME to start" 90 $CA_LOGFILE $CA_HOST 7054
+ log "Enrolling with $CA_NAME as bootstrap identity ..."
+ export FABRIC_CA_CLIENT_HOME=$HOME/cas/$CA_NAME
+ export FABRIC_CA_CLIENT_TLS_CERTFILES=$CA_CHAINFILE
+ fabric-ca-client enroll -d -u https://$CA_ADMIN_USER_PASS@$CA_HOST:7054
+}
+
+function registerIdentities {
+ log "Registering identities ..."
+ registerOrdererIdentities
+ registerPeerIdentities
+}
+
+# Register any identities associated with the orderer
+function registerOrdererIdentities {
+ for ORG in $ORDERER_ORGS; do
+ initOrgVars $ORG
+ enrollCAAdmin
+ local COUNT=1
+ while [[ "$COUNT" -le $NUM_ORDERERS ]]; do
+ initOrdererVars $ORG $COUNT
+ log "Registering $ORDERER_NAME with $CA_NAME"
+ fabric-ca-client register -d --id.name $ORDERER_NAME --id.secret $ORDERER_PASS --id.type orderer
+ COUNT=$((COUNT+1))
+ done
+ log "Registering admin identity with $CA_NAME"
+ # The admin identity has the "admin" attribute which is added to ECert by default
+ fabric-ca-client register -d --id.name $ADMIN_NAME --id.secret $ADMIN_PASS --id.attrs "admin=true:ecert"
+ done
+}
+
+# Register any identities associated with a peer
+function registerPeerIdentities {
+ for ORG in $PEER_ORGS; do
+ initOrgVars $ORG
+ enrollCAAdmin
+ local COUNT=1
+ while [[ "$COUNT" -le $NUM_PEERS ]]; do
+ initPeerVars $ORG $COUNT
+ log "Registering $PEER_NAME with $CA_NAME"
+ fabric-ca-client register -d --id.name $PEER_NAME --id.secret $PEER_PASS --id.type peer
+ COUNT=$((COUNT+1))
+ done
+ log "Registering admin identity with $CA_NAME"
+ # The admin identity has the "admin" attribute which is added to ECert by default
+ fabric-ca-client register -d --id.name $ADMIN_NAME --id.secret $ADMIN_PASS --id.attrs "hf.Registrar.Roles=client,hf.Registrar.Attributes=*,hf.Revoker=true,hf.GenCRL=true,admin=true:ecert,abac.init=true:ecert"
+ log "Registering user identity with $CA_NAME"
+ fabric-ca-client register -d --id.name $USER_NAME --id.secret $USER_PASS
+ done
+}
+
+function getCACerts {
+ log "Getting CA certificates ..."
+ for ORG in $ORGS; do
+ initOrgVars $ORG
+ log "Getting CA certs for organization $ORG and storing in $ORG_MSP_DIR"
+ export FABRIC_CA_CLIENT_TLS_CERTFILES=$CA_CHAINFILE
+ fabric-ca-client getcacert -d -u https://$CA_HOST:7054 -M $ORG_MSP_DIR
+ finishMSPSetup $ORG_MSP_DIR
+ # If ADMINCERTS is true, we need to enroll the admin now to populate the admincerts directory
+ if [ $ADMINCERTS ]; then
+ switchToAdminIdentity
+ fi
+ done
+}
+
+# printOrg
+function printOrg {
+ echo "
+ - &$ORG_CONTAINER_NAME
+
+ Name: $ORG
+
+ # ID to load the MSP definition as
+ ID: $ORG_MSP_ID
+
+ # MSPDir is the filesystem path which contains the MSP configuration
+ MSPDir: $ORG_MSP_DIR"
+}
+
+# printOrdererOrg
+function printOrdererOrg {
+ initOrgVars $1
+ printOrg
+}
+
+# printPeerOrg
+function printPeerOrg {
+ initPeerVars $1 $2
+ printOrg
+ echo "
+ AnchorPeers:
+ # AnchorPeers defines the location of peers which can be used
+ # for cross org gossip communication. Note, this value is only
+ # encoded in the genesis block in the Application section context
+ - Host: $PEER_HOST
+ Port: 7051"
+}
+
+function makeConfigTxYaml {
+ {
+ echo "
+################################################################################
+#
+# Section: Organizations
+#
+# - This section defines the different organizational identities which will
+# be referenced later in the configuration.
+#
+################################################################################
+Organizations:"
+
+ for ORG in $ORDERER_ORGS; do
+ printOrdererOrg $ORG
+ done
+
+ for ORG in $PEER_ORGS; do
+ printPeerOrg $ORG 1
+ done
+
+ echo "
+################################################################################
+#
+# SECTION: Application
+#
+# This section defines the values to encode into a config transaction or
+# genesis block for application related parameters
+#
+################################################################################
+Application: &ApplicationDefaults
+
+ # Organizations is the list of orgs which are defined as participants on
+ # the application side of the network
+ Organizations:
+"
+ echo "
+################################################################################
+#
+# Profile
+#
+# - Different configuration profiles may be encoded here to be specified
+# as parameters to the configtxgen tool
+#
+################################################################################
+Profiles:
+
+ OrgsOrdererGenesis:
+ Orderer:
+ # Orderer Type: The orderer implementation to start
+ # Available types are \"solo\" and \"kafka\"
+ OrdererType: solo
+ Addresses:"
+
+ for ORG in $ORDERER_ORGS; do
+ local COUNT=1
+ while [[ "$COUNT" -le $NUM_ORDERERS ]]; do
+ initOrdererVars $ORG $COUNT
+ echo " - $ORDERER_HOST:7050"
+ COUNT=$((COUNT+1))
+ done
+ done
+
+ echo "
+ # Batch Timeout: The amount of time to wait before creating a batch
+ BatchTimeout: 2s
+
+ # Batch Size: Controls the number of messages batched into a block
+ BatchSize:
+
+ # Max Message Count: The maximum number of messages to permit in a batch
+ MaxMessageCount: 10
+
+ # Absolute Max Bytes: The absolute maximum number of bytes allowed for
+ # the serialized messages in a batch.
+ AbsoluteMaxBytes: 99 MB
+
+ # Preferred Max Bytes: The preferred maximum number of bytes allowed for
+ # the serialized messages in a batch. A message larger than the preferred
+ # max bytes will result in a batch larger than preferred max bytes.
+ PreferredMaxBytes: 512 KB
+
+ Kafka:
+ # Brokers: A list of Kafka brokers to which the orderer connects
+ # NOTE: Use IP:port notation
+ Brokers:
+ - 127.0.0.1:9092
+
+ # Organizations is the list of orgs which are defined as participants on
+ # the orderer side of the network
+ Organizations:"
+
+ for ORG in $ORDERER_ORGS; do
+ initOrgVars $ORG
+ echo " - *${ORG_CONTAINER_NAME}"
+ done
+
+ echo "
+ Consortiums:
+
+ SampleConsortium:
+
+ Organizations:"
+
+ for ORG in $PEER_ORGS; do
+ initOrgVars $ORG
+ echo " - *${ORG_CONTAINER_NAME}"
+ done
+
+ echo "
+ OrgsChannel:
+ Consortium: SampleConsortium
+ Application:
+ <<: *ApplicationDefaults
+ Organizations:"
+
+ for ORG in $PEER_ORGS; do
+ initOrgVars $ORG
+ echo " - *${ORG_CONTAINER_NAME}"
+ done
+
+ } > /etc/hyperledger/fabric/configtx.yaml
+ # Copy it to the data directory to make debugging easier
+ cp /etc/hyperledger/fabric/configtx.yaml /$DATA
+}
+
+function generateChannelArtifacts() {
+ which configtxgen
+ if [ "$?" -ne 0 ]; then
+ fatal "configtxgen tool not found. exiting"
+ fi
+
+ log "Generating orderer genesis block at $GENESIS_BLOCK_FILE"
+ # Note: For some unknown reason (at least for now) the block file can't be
+ # named orderer.genesis.block or the orderer will fail to launch!
+ configtxgen -profile OrgsOrdererGenesis -outputBlock $GENESIS_BLOCK_FILE
+ if [ "$?" -ne 0 ]; then
+ fatal "Failed to generate orderer genesis block"
+ fi
+
+ log "Generating channel configuration transaction at $CHANNEL_TX_FILE"
+ configtxgen -profile OrgsChannel -outputCreateChannelTx $CHANNEL_TX_FILE -channelID $CHANNEL_NAME
+ if [ "$?" -ne 0 ]; then
+ fatal "Failed to generate channel configuration transaction"
+ fi
+
+ for ORG in $PEER_ORGS; do
+ initOrgVars $ORG
+ log "Generating anchor peer update transaction for $ORG at $ANCHOR_TX_FILE"
+ configtxgen -profile OrgsChannel -outputAnchorPeersUpdate $ANCHOR_TX_FILE \
+ -channelID $CHANNEL_NAME -asOrg $ORG
+ if [ "$?" -ne 0 ]; then
+ fatal "Failed to generate anchor peer update for $ORG"
+ fi
+ done
+}
+
+set -e
+
+SDIR=$(dirname "$0")
+source $SDIR/env.sh
+
+main
diff --git a/fabric/v1.3/fabric-ca/scripts/start-intermediate-ca.sh b/fabric/v1.3/fabric-ca/scripts/start-intermediate-ca.sh
new file mode 100755
index 0000000..01bf14b
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/scripts/start-intermediate-ca.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+source $(dirname "$0")/env.sh
+initOrgVars $ORG
+
+set -e
+
+# Wait for the root CA to start
+waitPort "root CA to start" 60 $ROOT_CA_LOGFILE $ROOT_CA_HOST 7054
+
+# Initialize the intermediate CA
+fabric-ca-server init -b $BOOTSTRAP_USER_PASS -u $PARENT_URL
+
+# Copy the intermediate CA's certificate chain to the data directory to be used by others
+cp $FABRIC_CA_SERVER_HOME/ca-chain.pem $TARGET_CHAINFILE
+
+# Add the custom orgs
+for o in $FABRIC_ORGS; do
+ aff=$aff"\n $o: []"
+done
+aff="${aff#\\n }"
+sed -i "/affiliations:/a \\ $aff" \
+ $FABRIC_CA_SERVER_HOME/fabric-ca-server-config.yaml
+
+# Start the intermediate CA
+fabric-ca-server start
diff --git a/fabric/v1.3/fabric-ca/scripts/start-orderer.sh b/fabric/v1.3/fabric-ca/scripts/start-orderer.sh
new file mode 100755
index 0000000..c354dda
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/scripts/start-orderer.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+set -e
+
+source $(dirname "$0")/env.sh
+
+# Wait for setup to complete sucessfully
+awaitSetup
+
+# Enroll to get orderer's TLS cert (using the "tls" profile)
+fabric-ca-client enroll -d --enrollment.profile tls -u $ENROLLMENT_URL -M /tmp/tls --csr.hosts $ORDERER_HOST
+
+# Copy the TLS key and cert to the appropriate place
+TLSDIR=$ORDERER_HOME/tls
+mkdir -p $TLSDIR
+cp /tmp/tls/keystore/* $ORDERER_GENERAL_TLS_PRIVATEKEY
+cp /tmp/tls/signcerts/* $ORDERER_GENERAL_TLS_CERTIFICATE
+rm -rf /tmp/tls
+
+# Enroll again to get the orderer's enrollment certificate (default profile)
+fabric-ca-client enroll -d -u $ENROLLMENT_URL -M $ORDERER_GENERAL_LOCALMSPDIR
+
+# Finish setting up the local MSP for the orderer
+finishMSPSetup $ORDERER_GENERAL_LOCALMSPDIR
+copyAdminCert $ORDERER_GENERAL_LOCALMSPDIR
+
+# Wait for the genesis block to be created
+dowait "genesis block to be created" 60 $SETUP_LOGFILE $ORDERER_GENERAL_GENESISFILE
+
+# Start the orderer
+env | grep ORDERER
+orderer
diff --git a/fabric/v1.3/fabric-ca/scripts/start-peer.sh b/fabric/v1.3/fabric-ca/scripts/start-peer.sh
new file mode 100755
index 0000000..9dd7438
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/scripts/start-peer.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+set -e
+
+source $(dirname "$0")/env.sh
+
+awaitSetup
+
+# Although a peer may use the same TLS key and certificate file for both inbound and outbound TLS,
+# we generate a different key and certificate for inbound and outbound TLS simply to show that it is permissible
+
+# Generate server TLS cert and key pair for the peer
+fabric-ca-client enroll -d --enrollment.profile tls -u $ENROLLMENT_URL -M /tmp/tls --csr.hosts $PEER_HOST
+
+# Copy the TLS key and cert to the appropriate place
+TLSDIR=$PEER_HOME/tls
+mkdir -p $TLSDIR
+cp /tmp/tls/signcerts/* $CORE_PEER_TLS_CERT_FILE
+cp /tmp/tls/keystore/* $CORE_PEER_TLS_KEY_FILE
+rm -rf /tmp/tls
+
+# Generate client TLS cert and key pair for the peer
+genClientTLSCert $PEER_NAME $CORE_PEER_TLS_CLIENTCERT_FILE $CORE_PEER_TLS_CLIENTKEY_FILE
+
+# Generate client TLS cert and key pair for the peer CLI
+genClientTLSCert $PEER_NAME /$DATA/tls/$PEER_NAME-cli-client.crt /$DATA/tls/$PEER_NAME-cli-client.key
+
+# Enroll the peer to get an enrollment certificate and set up the core's local MSP directory
+fabric-ca-client enroll -d -u $ENROLLMENT_URL -M $CORE_PEER_MSPCONFIGPATH
+finishMSPSetup $CORE_PEER_MSPCONFIGPATH
+copyAdminCert $CORE_PEER_MSPCONFIGPATH
+
+# Start the peer
+log "Starting peer '$CORE_PEER_ID' with MSP at '$CORE_PEER_MSPCONFIGPATH'"
+env | grep CORE
+peer node start
diff --git a/fabric/v1.3/fabric-ca/scripts/start-root-ca.sh b/fabric/v1.3/fabric-ca/scripts/start-root-ca.sh
new file mode 100755
index 0000000..f1fa244
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/scripts/start-root-ca.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+set -e
+
+# Initialize the root CA
+fabric-ca-server init -b $BOOTSTRAP_USER_PASS
+
+# Copy the root CA's signing certificate to the data directory to be used by others
+cp $FABRIC_CA_SERVER_HOME/ca-cert.pem $TARGET_CERTFILE
+
+# Add the custom orgs
+for o in $FABRIC_ORGS; do
+ aff=$aff"\n $o: []"
+done
+aff="${aff#\\n }"
+sed -i "/affiliations:/a \\ $aff" \
+ $FABRIC_CA_SERVER_HOME/fabric-ca-server-config.yaml
+
+# Start the root CA
+fabric-ca-server start
diff --git a/fabric/v1.3/fabric-ca/start.sh b/fabric/v1.3/fabric-ca/start.sh
new file mode 100755
index 0000000..13e994c
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/start.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+#
+# This script does everything required to run the fabric CA sample.
+#
+# By default, this test is run with the latest released docker images.
+#
+# To run against a specific fabric/fabric-ca version:
+# export FABRIC_TAG=1.3.0
+#
+# To run with locally built images:
+# export FABRIC_TAG=local
+
+set -e
+
+SDIR=$(dirname "$0")
+source ${SDIR}/scripts/env.sh
+
+cd ${SDIR}
+
+# Delete docker containers
+dockerContainers=$(docker ps -a | awk '$2~/hyperledger/ {print $1}')
+if [ "$dockerContainers" != "" ]; then
+ log "Deleting existing docker containers ..."
+ docker rm -f $dockerContainers > /dev/null
+fi
+
+# Remove chaincode docker images
+chaincodeImages=`docker images | grep "^dev-peer" | awk '{print $3}'`
+if [ "$chaincodeImages" != "" ]; then
+ log "Removing chaincode docker images ..."
+ docker rmi -f $chaincodeImages > /dev/null
+fi
+
+# Start with a clean data directory
+DDIR=${SDIR}/${DATA}
+if [ -d ${DDIR} ]; then
+ log "Cleaning up the data directory from previous run at $DDIR"
+ rm -rf ${SDIR}/data
+fi
+mkdir -p ${DDIR}/logs
+
+# Create the docker-compose file
+${SDIR}/makeDocker.sh
+
+# Create the docker containers
+log "Creating docker containers ..."
+docker-compose up -d
+
+# Wait for the setup container to complete
+dowait "the 'setup' container to finish registering identities, creating the genesis block and other artifacts" 90 $SDIR/$SETUP_LOGFILE $SDIR/$SETUP_SUCCESS_FILE
+
+# Wait for the run container to start and then tails it's summary log
+dowait "the docker 'run' container to start" 60 ${SDIR}/${SETUP_LOGFILE} ${SDIR}/${RUN_SUMFILE}
+tail -f ${SDIR}/${RUN_SUMFILE}&
+TAIL_PID=$!
+
+# Wait for the run container to complete
+while true; do
+ if [ -f ${SDIR}/${RUN_SUCCESS_FILE} ]; then
+ kill -9 $TAIL_PID
+ exit 0
+ elif [ -f ${SDIR}/${RUN_FAIL_FILE} ]; then
+ kill -9 $TAIL_PID
+ exit 1
+ else
+ sleep 1
+ fi
+done
diff --git a/fabric/v1.3/fabric-ca/stop.sh b/fabric/v1.3/fabric-ca/stop.sh
new file mode 100755
index 0000000..8f85406
--- /dev/null
+++ b/fabric/v1.3/fabric-ca/stop.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright IBM Corp. All Rights Reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+set -e
+SDIR=$(dirname "$0")
+source $SDIR/scripts/env.sh
+
+log "Stopping docker containers ..."
+docker-compose down
+# Stop chaincode containers and images as well
+docker rm -f $(docker ps -aq --filter name=dev-peer)
+docker rmi $(docker images | awk '$1 ~ /dev-peer/ { print $3 }')
+log "Docker containers have been stopped"