From caf5c33db22eb52473c516331c3c9292c0f568dd Mon Sep 17 00:00:00 2001 From: Keith Smith Date: Wed, 6 Sep 2017 18:47:36 -0400 Subject: [PATCH] [FAB-6050] Adding fabric-ca sample This sample uses fabric-ca to run an end-to-end test similar to the BYFN sample. However, instead of using cryptogen, it uses fabric-ca. All private keys are generated dynamically in the container in which they are used. This sample also demonstrates how to use abac (Attribute-Based Access Control) to make access decisions. See chaincode/abac/abac.go. Change-Id: I5eddc9e35908e409ac07266c3183ce89a5a6cd82 Signed-off-by: Keith Smith --- chaincode/abac/abac.go | 206 +++++++++++++ fabric-ca/.env | 1 + fabric-ca/.gitignore | 2 + fabric-ca/README.md | 88 ++++++ fabric-ca/build-images.sh | 54 ++++ fabric-ca/makeDocker.sh | 261 +++++++++++++++++ fabric-ca/scripts/env.sh | 322 +++++++++++++++++++++ fabric-ca/scripts/run-fabric.sh | 191 ++++++++++++ fabric-ca/scripts/setup-fabric.sh | 288 ++++++++++++++++++ fabric-ca/scripts/start-intermediate-ca.sh | 24 ++ fabric-ca/scripts/start-orderer.sh | 37 +++ fabric-ca/scripts/start-peer.sh | 32 ++ fabric-ca/scripts/start-root-ca.sh | 17 ++ fabric-ca/start.sh | 67 +++++ fabric-ca/stop.sh | 14 + 15 files changed, 1604 insertions(+) create mode 100644 chaincode/abac/abac.go create mode 100644 fabric-ca/.env create mode 100644 fabric-ca/.gitignore create mode 100755 fabric-ca/README.md create mode 100755 fabric-ca/build-images.sh create mode 100755 fabric-ca/makeDocker.sh create mode 100755 fabric-ca/scripts/env.sh create mode 100755 fabric-ca/scripts/run-fabric.sh create mode 100755 fabric-ca/scripts/setup-fabric.sh create mode 100755 fabric-ca/scripts/start-intermediate-ca.sh create mode 100755 fabric-ca/scripts/start-orderer.sh create mode 100755 fabric-ca/scripts/start-peer.sh create mode 100755 fabric-ca/scripts/start-root-ca.sh create mode 100755 fabric-ca/start.sh create mode 100755 fabric-ca/stop.sh diff --git a/chaincode/abac/abac.go b/chaincode/abac/abac.go new file mode 100644 index 0000000000..c43448e481 --- /dev/null +++ b/chaincode/abac/abac.go @@ -0,0 +1,206 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + "strconv" + + "github.com/hyperledger/fabric/core/chaincode/shim" + "github.com/hyperledger/fabric/core/chaincode/lib/cid" + pb "github.com/hyperledger/fabric/protos/peer" +) + +// SimpleChaincode example simple Chaincode implementation +type SimpleChaincode struct { +} + +// Init initializes the chaincode +func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { + + fmt.Println("abac Init") + + // + // Demonstrate the use of Attribute-Based Access Control (ABAC) by checking + // to see if the caller has the "abac.init" attribute with a value of true; + // if not, return an error. + // + err := cid.AssertAttributeValue(stub, "abac.init", "true") + if err != nil { + return shim.Error(err.Error()) + } + + _, args := stub.GetFunctionAndParameters() + var A, B string // Entities + var Aval, Bval int // Asset holdings + + if len(args) != 4 { + return shim.Error("Incorrect number of arguments. Expecting 4") + } + + // Initialize the chaincode + A = args[0] + Aval, err = strconv.Atoi(args[1]) + if err != nil { + return shim.Error("Expecting integer value for asset holding") + } + B = args[2] + Bval, err = strconv.Atoi(args[3]) + if err != nil { + return shim.Error("Expecting integer value for asset holding") + } + fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) + + // Write the state to the ledger + err = stub.PutState(A, []byte(strconv.Itoa(Aval))) + if err != nil { + return shim.Error(err.Error()) + } + + err = stub.PutState(B, []byte(strconv.Itoa(Bval))) + if err != nil { + return shim.Error(err.Error()) + } + + return shim.Success(nil) +} + +func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { + fmt.Println("abac Invoke") + function, args := stub.GetFunctionAndParameters() + if function == "invoke" { + // Make payment of X units from A to B + return t.invoke(stub, args) + } else if function == "delete" { + // Deletes an entity from its state + return t.delete(stub, args) + } else if function == "query" { + // the old "Query" is now implemtned in invoke + return t.query(stub, args) + } + + return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"") +} + +// Transaction makes payment of X units from A to B +func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response { + var A, B string // Entities + var Aval, Bval int // Asset holdings + var X int // Transaction value + var err error + + if len(args) != 3 { + return shim.Error("Incorrect number of arguments. Expecting 3") + } + + A = args[0] + B = args[1] + + // Get the state from the ledger + // TODO: will be nice to have a GetAllState call to ledger + Avalbytes, err := stub.GetState(A) + if err != nil { + return shim.Error("Failed to get state") + } + if Avalbytes == nil { + return shim.Error("Entity not found") + } + Aval, _ = strconv.Atoi(string(Avalbytes)) + + Bvalbytes, err := stub.GetState(B) + if err != nil { + return shim.Error("Failed to get state") + } + if Bvalbytes == nil { + return shim.Error("Entity not found") + } + Bval, _ = strconv.Atoi(string(Bvalbytes)) + + // Perform the execution + X, err = strconv.Atoi(args[2]) + if err != nil { + return shim.Error("Invalid transaction amount, expecting a integer value") + } + Aval = Aval - X + Bval = Bval + X + fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) + + // Write the state back to the ledger + err = stub.PutState(A, []byte(strconv.Itoa(Aval))) + if err != nil { + return shim.Error(err.Error()) + } + + err = stub.PutState(B, []byte(strconv.Itoa(Bval))) + if err != nil { + return shim.Error(err.Error()) + } + + return shim.Success(nil) +} + +// Deletes an entity from state +func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response { + if len(args) != 1 { + return shim.Error("Incorrect number of arguments. Expecting 1") + } + + A := args[0] + + // Delete the key from the state in ledger + err := stub.DelState(A) + if err != nil { + return shim.Error("Failed to delete state") + } + + return shim.Success(nil) +} + +// query callback representing the query of a chaincode +func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response { + var A string // Entities + var err error + + if len(args) != 1 { + return shim.Error("Incorrect number of arguments. Expecting name of the person to query") + } + + A = args[0] + + // Get the state from the ledger + Avalbytes, err := stub.GetState(A) + if err != nil { + jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}" + return shim.Error(jsonResp) + } + + if Avalbytes == nil { + jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}" + return shim.Error(jsonResp) + } + + jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}" + fmt.Printf("Query Response:%s\n", jsonResp) + return shim.Success(Avalbytes) +} + +func main() { + err := shim.Start(new(SimpleChaincode)) + if err != nil { + fmt.Printf("Error starting Simple chaincode: %s", err) + } +} diff --git a/fabric-ca/.env b/fabric-ca/.env new file mode 100644 index 0000000000..4fd2ee0d73 --- /dev/null +++ b/fabric-ca/.env @@ -0,0 +1 @@ +COMPOSE_PROJECT_NAME=net diff --git a/fabric-ca/.gitignore b/fabric-ca/.gitignore new file mode 100644 index 0000000000..a5a77b4574 --- /dev/null +++ b/fabric-ca/.gitignore @@ -0,0 +1,2 @@ +docker-compose.yml +data diff --git a/fabric-ca/README.md b/fabric-ca/README.md new file mode 100755 index 0000000000..8e9fd3865c --- /dev/null +++ b/fabric-ca/README.md @@ -0,0 +1,88 @@ +# 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. The following images are required to run this sample: +*hyperledger/fabric-ca-orderer*, *hyperledger/fabric-ca-peer*, and *hyperledger/fabric-ca-tools*. +These images are new in the v1.1.0 release of the *github.com/hyperledger/fabric-ca*. +In order to run this sample prior to the v1.1.0 release, you must build these +images manually as follows: +a) pull the master branch of the *github.com/hyperledger/fabric* and + *github.com/hyperledger/fabric-ca* repositories; +b) make sure these repositories are on your GOPATH; +c) run the *build-images.sh* script provided with this sample. + +2. 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. + +3. 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/run-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/tree/release/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. + +Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License diff --git a/fabric-ca/build-images.sh b/fabric-ca/build-images.sh new file mode 100755 index 0000000000..e49bf4e5e9 --- /dev/null +++ b/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-ca/makeDocker.sh b/fabric-ca/makeDocker.sh new file mode 100755 index 0000000000..83244097ea --- /dev/null +++ b/fabric-ca/makeDocker.sh @@ -0,0 +1,261 @@ +#!/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. +# + +SDIR=$(dirname "$0") +source $SDIR/scripts/env.sh + +function main { + { + writeHeader + writeRootFabricCA + if $USE_INTERMEDIATE_CA; then + writeIntermediateFabricCA + fi + writeSetupFabric + writeStartFabric + writeRunFabric + } > $SDIR/docker-compose.yml + log "Created docker-compose.yml" +} + +# 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 + image: hyperledger/fabric-ca-tools + 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 + 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 + 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 + image: hyperledger/fabric-ca-orderer + 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_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 + image: hyperledger/fabric-ca-peer + environment: + - FABRIC_CA_CLIENT_HOME=$MYHOME + - FABRIC_CA_CLIENT_TLS_CERTFILES=$CA_CHAINFILE + - ENROLLMENT_URL=https://$PEER_NAME_PASS@$CA_HOST:7054 + - 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_PROFILE_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_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-ca/scripts/env.sh b/fabric-ca/scripts/env.sh new file mode 100755 index 0000000000..e86f661170 --- /dev/null +++ b/fabric-ca/scripts/env.sh @@ -0,0 +1,322 @@ +#!/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 + +# 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 + +# 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 $USE_INTERMEDIATE_CA; 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=[$INT_CA_CHAINFILE] +} + +# 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_PROFILE_ENABLED=true + export CORE_PEER_TLS_CERT_FILE=$TLSDIR/server.crt + export CORE_PEER_TLS_KEY_FILE=$TLSDIR/server.key + export CORE_PEER_TLS_ROOTCERT_FILE=$INT_CA_CHAINFILE + # 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 +} + +# 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" 10 $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" 10 $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 +} + +# 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" 10 $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" $1 $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 "" +} + +# 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-ca/scripts/run-fabric.sh b/fabric-ca/scripts/run-fabric.sh new file mode 100755 index 0000000000..76c9860fc2 --- /dev/null +++ b/fabric-ca/scripts/run-fabric.sh @@ -0,0 +1,191 @@ +#!/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 5 seconds for the orderer and peers to start + awaitSetup 10 + sleep 5 + + 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 + ORDERER_PORT_ARGS="-o $ORDERER_HOST:7050 --tls true --cafile $CA_CHAINFILE" + + # 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_PORT_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_PORT_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_PORT_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 + + 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_PORT_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 +} + +chaincodeQuery () { + if [ $# -ne 1 ]; then + fatalr "Usage: chaincodeQuery " + fi + set +e + logr "Querying chaincode in channel '$CHANNEL_NAME' on 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 + 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 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 +} + +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 + fi +} + +function logr { + log $* + log $* >> $RUN_SUMPATH +} + +function fatalr { + logr "FATAL: $*" + exit 1 +} + +main diff --git a/fabric-ca/scripts/setup-fabric.sh b/fabric-ca/scripts/setup-fabric.sh new file mode 100755 index 0000000000..3837a87d7e --- /dev/null +++ b/fabric-ca/scripts/setup-fabric.sh @@ -0,0 +1,288 @@ +#!/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 { + sleep 1 + log "Beginning building channel artifacts ..." + registerIdentities + getCACerts + makeConfigTxYaml + generateChannelArtifacts + log "Finished building channel artifacts" + touch /$SETUP_SUCCESS_FILE +} + +# Enroll as the CA admin +function enrollCAAdmin { + dowait "$CA_NAME to start" 10 $CA_LOGFILE $CA_CHAINFILE + 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 + COUNT=$((COUNT+1)) + done + log "Registering admin identity with $CA_NAME" + # The admin identity has the "hf.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.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 + COUNT=$((COUNT+1)) + done + log "Registering admin identity with $CA_NAME" + # The admin identity has the "hf.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.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 "################################################################################ +# +# 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 + + 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: +" + + } > /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-ca/scripts/start-intermediate-ca.sh b/fabric-ca/scripts/start-intermediate-ca.sh new file mode 100755 index 0000000000..d69ec5c163 --- /dev/null +++ b/fabric-ca/scripts/start-intermediate-ca.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +source $(dirname "$0")/env.sh +initOrgVars $ORG + +set -e + +dowait "Root CA certificate file to be created" 10 $ROOT_CA_CERTFILE $ROOT_CA_LOGFILE + +sleep 2 + +# 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 + +# Start the intermediate CA +fabric-ca-server start diff --git a/fabric-ca/scripts/start-orderer.sh b/fabric-ca/scripts/start-orderer.sh new file mode 100755 index 0000000000..be12d53883 --- /dev/null +++ b/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 10 + +# 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" 10 $SETUP_LOGFILE $ORDERER_GENERAL_GENESISFILE + +# Start the orderer +env | grep ORDERER +orderer diff --git a/fabric-ca/scripts/start-peer.sh b/fabric-ca/scripts/start-peer.sh new file mode 100755 index 0000000000..addfd2eef4 --- /dev/null +++ b/fabric-ca/scripts/start-peer.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Copyright IBM Corp. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +set -e + +source $(dirname "$0")/env.sh + +awaitSetup 10 + +# Enroll the peer to get a TLS cert +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 + +# 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-ca/scripts/start-root-ca.sh b/fabric-ca/scripts/start-root-ca.sh new file mode 100755 index 0000000000..27874ddf60 --- /dev/null +++ b/fabric-ca/scripts/start-root-ca.sh @@ -0,0 +1,17 @@ +#!/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 + +# Start the root CA +fabric-ca-server start diff --git a/fabric-ca/start.sh b/fabric-ca/start.sh new file mode 100755 index 0000000000..5574b343b0 --- /dev/null +++ b/fabric-ca/start.sh @@ -0,0 +1,67 @@ +#!/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. +# + +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" 10 $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" 15 ${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-ca/stop.sh b/fabric-ca/stop.sh new file mode 100755 index 0000000000..c350e46faf --- /dev/null +++ b/fabric-ca/stop.sh @@ -0,0 +1,14 @@ +#!/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 +log "Docker containers have been stopped"