diff --git a/first-network/byfn.sh b/first-network/byfn.sh index 01287c6391..161a5dc615 100755 --- a/first-network/byfn.sh +++ b/first-network/byfn.sh @@ -34,13 +34,14 @@ export FABRIC_CFG_PATH=${PWD} # Print the usage message function printHelp () { echo "Usage: " - echo " byfn.sh up|down|restart|generate [-c ] [-t ] [-d ] [-f ] [-s ] [-i ]" + echo " byfn.sh up|down|restart|generate|upgrade [-c ] [-t ] [-d ] [-f ] [-s ] [-i ]" echo " byfn.sh -h|--help (print this message)" echo " - one of 'up', 'down', 'restart' or 'generate'" echo " - 'up' - bring up the network with docker-compose up" echo " - 'down' - clear the network with docker-compose down" echo " - 'restart' - restart the network" echo " - 'generate' - generate required certificates and genesis block" + echo " - 'upgrade' - upgrade the network from v1.0.x to v1.1" echo " -c - channel name to use (defaults to \"mychannel\")" echo " -t - CLI timeout duration in seconds (defaults to 10)" echo " -d - delay duration in seconds (defaults to 3)" @@ -58,6 +59,7 @@ function printHelp () { echo " byfn.sh up -c mychannel -s couchdb -i 1.1.0-alpha" echo " byfn.sh up -l node" echo " byfn.sh down -c mychannel" + echo " byfn.sh upgrade -c mychannel" echo echo "Taking all defaults:" echo " byfn.sh generate" @@ -141,6 +143,65 @@ function networkUp () { fi } +# Upgrade the network from v1.0.x to v1.1 +# Stop the orderer and peers, backup the ledger from orderer and peers, cleanup chaincode containers and images +# and relaunch the orderer and peers with latest tag +function upgradeNetwork () { + if [ ! -d ledgers ]; then + echo "ERROR !!!! There is no persisted ledgers directory, did you start your network with -p?" + exit 1 + fi + + LEDGERS_BACKUP=./ledgers-backup + + # create ledger-backup directory + mkdir -p $LEDGERS_BACKUP + + export IMAGE_TAG=$IMAGETAG + if [ "${IF_COUCHDB}" == "couchdb" ]; then + COMPOSE_FILES="-f $COMPOSE_FILE -f $COMPOSE_FILE_PERSIST -f $COMPOSE_FILE_COUCH" + else + COMPOSE_FILES="-f $COMPOSE_FILE -f $COMPOSE_FILE_PERSIST" + fi + + # removing the cli container + docker-compose $COMPOSE_FILES stop cli + docker-compose $COMPOSE_FILES up -d --no-deps cli + + echo "Upgrading orderer" + docker-compose $COMPOSE_FILES stop orderer.example.com + docker cp -a orderer.example.com:/var/hyperledger/production/orderer $LEDGERS_BACKUP/orderer.example.com + docker-compose $COMPOSE_FILES up -d --no-deps orderer.example.com + + for PEER in peer0.org1.example.com peer1.org1.example.com peer0.org2.example.com peer1.org2.example.com; do + echo "Upgrading peer $PEER" + + # Stop the peer and backup its ledger + docker-compose $COMPOSE_FILES stop $PEER + docker cp -a $PEER:/var/hyperledger/production $LEDGERS_BACKUP/$PEER/ + + # Remove any old containers and images for this peer + CC_CONTAINERS=$(docker ps | grep dev-$PEER | awk '{print $1}') + if [ -n "$CC_CONTAINERS" ] ; then + docker rm -f $CC_CONTAINERS + fi + CC_IMAGES=$(docker images | grep dev-$PEER | awk '{print $1}') + if [ -n "$CC_IMAGES" ] ; then + docker rmi -f $CC_IMAGES + fi + + # Start the peer again + docker-compose $COMPOSE_FILES up -d --no-deps $PEER + done + + docker exec cli scripts/upgrade_to_v11.sh $CHANNEL_NAME $CLI_DELAY $LANGUAGE $CLI_TIMEOUT + if [ $? -ne 0 ]; then + echo "ERROR !!!! Test failed" + exit 1 + fi +} + + # Tear down running network function networkDown () { docker-compose -f $COMPOSE_FILE down @@ -149,6 +210,7 @@ function networkDown () { if [ "$MODE" != "restart" ]; then #Delete any persisted ledgers docker run -v $PWD:/tmp/first-network --rm hyperledger/fabric-tools:$IMAGETAG rm -Rf /tmp/first-network/ledgers + docker run -v $PWD:/tmp/first-network --rm hyperledger/fabric-tools:$IMAGETAG rm -Rf /tmp/first-network/ledgers-backup #Cleanup the chaincode containers clearContainers #Cleanup images @@ -356,6 +418,8 @@ elif [ "$MODE" == "restart" ]; then EXPMODE="Restarting" elif [ "$MODE" == "generate" ]; then EXPMODE="Generating certs and genesis block for" +elif [ "$MODE" == "upgrade" ]; then + EXPMODE="Upgrading the network" else printHelp exit 1 @@ -409,6 +473,8 @@ elif [ "${MODE}" == "generate" ]; then ## Generate Artifacts elif [ "${MODE}" == "restart" ]; then ## Restart the network networkDown networkUp +elif [ "${MODE}" == "upgrade" ]; then ## Upgrade the network from v1.0.x to v1.1 + upgradeNetwork else printHelp exit 1 diff --git a/first-network/scripts/capabilities.json b/first-network/scripts/capabilities.json new file mode 100644 index 0000000000..f6120bbf5d --- /dev/null +++ b/first-network/scripts/capabilities.json @@ -0,0 +1,9 @@ +{ + "mod_policy": "Admins", + "value": { + "capabilities": { + "V1_1": {} + } + }, + "version": "0" +} diff --git a/first-network/scripts/step1org3.sh b/first-network/scripts/step1org3.sh index 1f16216750..1e468d3c4d 100755 --- a/first-network/scripts/step1org3.sh +++ b/first-network/scripts/step1org3.sh @@ -29,6 +29,9 @@ if [ "$LANGUAGE" = "node" ]; then CC_SRC_PATH="/opt/gopath/src/github.com/chaincode/chaincode_example02/node/" fi +# import utils +. scripts/utils.sh + echo echo "========= Creating config transaction to add org3 to network =========== " echo @@ -36,34 +39,14 @@ echo echo "Installing jq" apt-get -y update && apt-get -y install jq -echo "Fetching the most recent configuration block for the channel" -peer channel fetch config config_block.pb -o orderer.example.com:7050 -c ${CHANNEL_NAME} --tls --cafile ${ORDERER_CA} - -echo "Creating config transaction adding org3 to the network" -# translate channel configuration block into JSON format -configtxlator proto_decode --input config_block.pb --type common.Block --output config_block.json +# Fetch the config for the channel, writing it to config.json +fetchChannelConfig ${CHANNEL_NAME} config.json -# strip away all of the encapsulating wrappers -jq .data.data[0].payload.data.config config_block.json > config.json - -# append new org to the configuration +# Modify the configuration to append the new org jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"Org3MSP":.[1]}}}}}' config.json ./channel-artifacts/org3.json > modified_config.json -# translate json config files back to protobuf -configtxlator proto_encode --input config.json --type common.Config --output config.pb -configtxlator proto_encode --input modified_config.json --type common.Config --output modified_config.pb - -# get delta between old and new configs -configtxlator compute_update --channel_id ${CHANNEL_NAME} --original config.pb --updated modified_config.pb --output org3_update.pb - -# translate protobuf delta to json -configtxlator proto_decode --input org3_update.pb --type common.ConfigUpdate --output org3_update.json - -# wrap delta in an envelope message -echo '{"payload":{"header":{"channel_header":{"channel_id":"'${CHANNEL_NAME}'", "type":2}},"data":{"config_update":'$(cat org3_update.json)'}}}' | jq . > org3_update_in_envelope.json - -# translate json back to protobuf -configtxlator proto_encode --input org3_update_in_envelope.json --type common.Envelope --output org3_update_in_envelope.pb +# Compute a config update, based on the differences between config.json and modified_config.json, write it as a transaction to org3_update_in_envelope.pb +createConfigUpdate ${CHANNEL_NAME} config.json modified_config.json org3_update_in_envelope.pb echo echo "========= Config transaction to add org3 to network created ===== " @@ -71,15 +54,12 @@ echo echo "Signing config transaction" echo -peer channel signconfigtx -f org3_update_in_envelope.pb +signConfigtxAsPeerOrg 1 org3_update_in_envelope.pb echo echo "========= Submitting transaction from a different peer (peer0.org2) which also signs it ========= " echo -export CORE_PEER_LOCALMSPID="Org2MSP" -export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp -export CORE_PEER_ADDRESS=peer0.org2.example.com:7051 +setGlobals 0 2 peer channel update -f org3_update_in_envelope.pb -c ${CHANNEL_NAME} -o orderer.example.com:7050 --tls --cafile ${ORDERER_CA} echo diff --git a/first-network/scripts/upgrade_to_v11.sh b/first-network/scripts/upgrade_to_v11.sh new file mode 100755 index 0000000000..8ce98a675e --- /dev/null +++ b/first-network/scripts/upgrade_to_v11.sh @@ -0,0 +1,168 @@ +#!/bin/bash + +echo +echo " ____ _____ _ ____ _____ " +echo "/ ___| |_ _| / \ | _ \ |_ _|" +echo "\___ \ | | / _ \ | |_) | | | " +echo " ___) | | | / ___ \ | _ < | | " +echo "|____/ |_| /_/ \_\ |_| \_\ |_| " +echo +echo "Upgrade your first network (BYFN) from v1.0.x to v1.1 end-to-end test" +echo +CHANNEL_NAME="$1" +DELAY="$2" +LANGUAGE="$3" +TIMEOUT="$4" +: ${CHANNEL_NAME:="mychannel"} +: ${DELAY:="5"} +: ${LANGUAGE:="golang"} +: ${TIMEOUT:="10"} +LANGUAGE=`echo "$LANGUAGE" | tr [:upper:] [:lower:]` +COUNTER=1 +MAX_RETRY=5 +ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem + +CC_SRC_PATH="github.com/chaincode/chaincode_example02/go/" +if [ "$LANGUAGE" = "node" ]; then + CC_SRC_PATH="/opt/gopath/src/github.com/chaincode/chaincode_example02/node/" +fi + +echo "Channel name : "$CHANNEL_NAME + +# import utils +. scripts/utils.sh + +# addCapabilityToChannel +# This function pulls the current channel config, modifies it with capabilities +# for the specified group, computes the config update, signs, and submits it. +addCapabilityToChannel() { + CH_NAME=$1 + GROUP=$2 + + setOrdererGlobals + + # Get the current channel config, decode and write it to config.json + fetchChannelConfig $CH_NAME config.json + + # Modify the correct section of the config based on capabilities group + if [ $GROUP == "orderer" ]; then + jq -s '.[0] * {"channel_group":{"groups":{"Orderer": {"values": {"Capabilities": .[1]}}}}}' config.json ./scripts/capabilities.json > modified_config.json + elif [ $GROUP == "channel" ]; then + jq -s '.[0] * {"channel_group":{"values": {"Capabilities": .[1]}}}' config.json ./scripts/capabilities.json > modified_config.json + elif [ $GROUP == "application" ]; then + jq -s '.[0] * {"channel_group":{"groups":{"Application": {"values": {"Capabilities": .[1]}}}}}' config.json ./scripts/capabilities.json > modified_config.json + fi + + # Create a config updated for this channel based on the differences between config.json and modified_config.json + # write the output to config_update_in_envelope.pb + createConfigUpdate "$CH_NAME" config.json modified_config.json config_update_in_envelope.pb + + # Sign, and set the correct identity for submission. + if [ $CH_NAME != "testchainid" ] ; then + if [ $GROUP == "orderer" ]; then + # Modifying the orderer group requires only the Orderer admin to sign. + # Prepare to sign the update as the OrdererOrg.Admin + setOrdererGlobals + elif [ $GROUP == "channel" ]; then + # Modifying the channel group requires a majority of application admins and the orderer admin to sign. + # Sign with PeerOrg1.Admin + signConfigtxAsPeerOrg 1 config_update_in_envelope.pb + # Sign with PeerOrg2.Admin + signConfigtxAsPeerOrg 2 config_update_in_envelope.pb + # Prepare to sign the update as the OrdererOrg.Admin + setOrdererGlobals + elif [ $GROUP == "application" ]; then + # Modifying the application group requires a majority of application admins to sign. + # Sign with PeerOrg1.Admin + signConfigtxAsPeerOrg 1 config_update_in_envelope.pb + # Prepare to sign the update as the PeerOrg2.Admin + setGlobals 0 2 + fi + else + # For the orderer system channel, only the orderer admin needs sign + # which will be attached during the update + setOrdererGlobals + fi + + if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then + peer channel update -f config_update_in_envelope.pb -c $CH_NAME -o orderer.example.com:7050 --cafile $ORDERER_CA + else + peer channel update -f config_update_in_envelope.pb -c $CH_NAME -o orderer.example.com:7050 --tls true --cafile $ORDERER_CA + fi + res=$? + verifyResult $res "Config update for \"$GROUP\" on \"$CH_NAME\" failed" + echo "===================== Config update for \"$GROUP\" on \"$CH_NAME\" is completed ===================== " + +} + +echo "Installing jq" +apt-get update +apt-get install -y jq + +sleep $DELAY + +#Config update for /Channel/Orderer on testchainid +echo "Config update for /Channel/Orderer on testchainid" +addCapabilityToChannel testchainid orderer + +sleep $DELAY + +#Config update for /Channel on testchainid +echo "Config update for /Channel on testchainid" +addCapabilityToChannel testchainid channel + +sleep $DELAY + +#Config update for /Channel/Orderer +echo "Config update for /Channel/Orderer on \"$CHANNEL_NAME\"" +addCapabilityToChannel $CHANNEL_NAME orderer + +sleep $DELAY + +#Config update for /Channel/Application +echo "Config update for /Channel/Application on \"$CHANNEL_NAME\"" +addCapabilityToChannel $CHANNEL_NAME application + +sleep $DELAY + +#Config update for /Channel +echo "Config update for /Channel on \"$CHANNEL_NAME\"" +addCapabilityToChannel $CHANNEL_NAME channel + +#Query on chaincode on Peer0/Org1 +echo "Querying chaincode on org1/peer0..." +chaincodeQuery 0 1 90 + +#Invoke on chaincode on Peer0/Org1 +echo "Sending invoke transaction on org1/peer0..." +chaincodeInvoke 0 1 + +sleep $DELAY + +#Query on chaincode on Peer0/Org1 +echo "Querying chaincode on org1/peer0..." +chaincodeQuery 0 1 80 + +##Invoke on chaincode on Peer0/Org2 +echo "Sending invoke transaction on org2/peer0..." +chaincodeInvoke 0 2 + +sleep $DELAY + +#Query on chaincode on Peer0/Org2 +echo "Querying chaincode on org2/peer0..." +chaincodeQuery 0 2 70 + +echo +echo "===================== All GOOD, End-2-End UPGRADE Scenario execution completed ===================== " +echo + +echo +echo " _____ _ _ ____ _____ ____ _____ " +echo "| ____| | \ | | | _ \ | ____| |___ \ | ____|" +echo "| _| | \| | | | | | _____ | _| __) | | _| " +echo "| |___ | |\ | | |_| | |_____| | |___ / __/ | |___ " +echo "|_____| |_| \_| |____/ |_____| |_____| |_____|" +echo + +exit 0 diff --git a/first-network/scripts/utils.sh b/first-network/scripts/utils.sh index dd481c0e78..5a7005adb5 100755 --- a/first-network/scripts/utils.sh +++ b/first-network/scripts/utils.sh @@ -17,6 +17,13 @@ verifyResult () { fi } +# Set OrdererOrg.Admin globals +setOrdererGlobals() { + CORE_PEER_LOCALMSPID="OrdererMSP" + CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem + CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/users/Admin@example.com/msp +} + setGlobals () { PEER=$1 ORG=$2 @@ -171,6 +178,50 @@ chaincodeQuery () { fi } +# fetchChannelConfig +# Writes the current channel config for a given channel to a JSON file +fetchChannelConfig() { + CHANNEL=$1 + OUTPUT=$2 + + setOrdererGlobals + + echo "Fetching the most recent configuration block for the channel" + if [ -z "$CORE_PEER_TLS_ENABLED" -o "$CORE_PEER_TLS_ENABLED" = "false" ]; then + peer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL --cafile $ORDERER_CA + else + peer channel fetch config config_block.pb -o orderer.example.com:7050 -c $CHANNEL --tls --cafile $ORDERER_CA + fi + + echo "Decoding config block to JSON and isolating config to ${OUTPUT}" + configtxlator proto_decode --input config_block.pb --type common.Block | jq .data.data[0].payload.data.config > "${OUTPUT}" +} + +# signConfigtxAsPeerOrg +# Set the peerOrg admin of an org and signing the config update +signConfigtxAsPeerOrg() { + PEERORG=$1 + TX=$2 + setGlobals 0 $PEERORG + peer channel signconfigtx -f "${TX}" +} + +# createConfigUpdate +# Takes an original and modified config, and produces the config update tx which transitions between the two +createConfigUpdate() { + CHANNEL=$1 + ORIGINAL=$2 + MODIFIED=$3 + OUTPUT=$4 + + configtxlator proto_encode --input "${ORIGINAL}" --type common.Config > original_config.pb + configtxlator proto_encode --input "${MODIFIED}" --type common.Config > modified_config.pb + configtxlator compute_update --channel_id "${CHANNEL}" --original original_config.pb --updated modified_config.pb > config_update.pb + configtxlator proto_decode --input config_update.pb --type common.ConfigUpdate > config_update.json + echo '{"payload":{"header":{"channel_header":{"channel_id":"'$CHANNEL'", "type":2}},"data":{"config_update":'$(cat config_update.json)'}}}' | jq . > config_update_in_envelope.json + configtxlator proto_encode --input config_update_in_envelope.json --type common.Envelope > "${OUTPUT}" +} + chaincodeInvoke () { PEER=$1 ORG=$2