├── apps └── .gitkeep ├── contracts └── .gitkeep ├── .gitignore ├── media ├── app.png ├── chaincode.png ├── github-repo.png ├── running-app.png ├── yeoman-app.png ├── chaincode-app.png ├── ibm-cloud-app.png ├── vscode-initial.png ├── business-network.png ├── rest-api-explorer.png ├── rest-api-updated.png ├── toolchain-created.png ├── toolchain-initial.png ├── ibm-cloud-dashboard.png ├── ibm-cloud-rest-app.png ├── delivery-pipeline-logs.png ├── ibm-cloud-dashboard-2.png ├── toolchain-auth-complete.png ├── toolchain-auth-required.png ├── toolchain-name-specified.png ├── yeoman-business-network.png ├── delivery-pipeline-overview.png └── toolchain-github-specified.png ├── .bluemix ├── config.sh ├── pipeline.yml ├── pipeline-CLOUDANT.sh ├── toolchain.yml ├── pipeline-COMMON.sh ├── pipeline-BUILD.sh ├── metadata.js ├── deploy.json ├── pipeline-BLOCKCHAIN.sh └── pipeline-DEPLOY.sh ├── LICENSE.txt └── README.md /apps/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contracts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /media/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/app.png -------------------------------------------------------------------------------- /media/chaincode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/chaincode.png -------------------------------------------------------------------------------- /media/github-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/github-repo.png -------------------------------------------------------------------------------- /media/running-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/running-app.png -------------------------------------------------------------------------------- /media/yeoman-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/yeoman-app.png -------------------------------------------------------------------------------- /media/chaincode-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/chaincode-app.png -------------------------------------------------------------------------------- /media/ibm-cloud-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/ibm-cloud-app.png -------------------------------------------------------------------------------- /media/vscode-initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/vscode-initial.png -------------------------------------------------------------------------------- /media/business-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/business-network.png -------------------------------------------------------------------------------- /media/rest-api-explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/rest-api-explorer.png -------------------------------------------------------------------------------- /media/rest-api-updated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/rest-api-updated.png -------------------------------------------------------------------------------- /media/toolchain-created.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/toolchain-created.png -------------------------------------------------------------------------------- /media/toolchain-initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/toolchain-initial.png -------------------------------------------------------------------------------- /media/ibm-cloud-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/ibm-cloud-dashboard.png -------------------------------------------------------------------------------- /media/ibm-cloud-rest-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/ibm-cloud-rest-app.png -------------------------------------------------------------------------------- /media/delivery-pipeline-logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/delivery-pipeline-logs.png -------------------------------------------------------------------------------- /media/ibm-cloud-dashboard-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/ibm-cloud-dashboard-2.png -------------------------------------------------------------------------------- /media/toolchain-auth-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/toolchain-auth-complete.png -------------------------------------------------------------------------------- /media/toolchain-auth-required.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/toolchain-auth-required.png -------------------------------------------------------------------------------- /media/toolchain-name-specified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/toolchain-name-specified.png -------------------------------------------------------------------------------- /media/yeoman-business-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/yeoman-business-network.png -------------------------------------------------------------------------------- /media/delivery-pipeline-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/delivery-pipeline-overview.png -------------------------------------------------------------------------------- /media/toolchain-github-specified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/iot-asset-tracker-starter-kit/master/media/toolchain-github-specified.png -------------------------------------------------------------------------------- /.bluemix/config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | # Set this to the name of the sample recognized by the IBM Blockchain Platform. Samples must be registered by the 6 | # IBM team who manage the IBM Blockchain Platform service; you cannot register samples yourself at this time. 7 | # export BLOCKCHAIN_SAMPLE_ID=your-sample 8 | 9 | # Set this to the name of an application under the apps directory. The URL of this application will be linked 10 | # to from the IBM Blockchain Platform. If you want to explicitly set this URL, specify it in BLOCKCHAIN_SAMPLE_URL. 11 | # export BLOCKCHAIN_SAMPLE_APP=your-app 12 | 13 | # Set this to a hardcoded URL of the application you want to be linked to from the IBM Blockchain Platform. 14 | # If this is not set, it will be set to the URL of the application specified in BLOCKCHAIN_SAMPLE_APP. 15 | # export BLOCKCHAIN_SAMPLE_URL= -------------------------------------------------------------------------------- /.bluemix/pipeline.yml: -------------------------------------------------------------------------------- 1 | --- 2 | stages: 3 | - name: BUILD 4 | inputs: 5 | - type: git 6 | branch: master 7 | service: ${REPO} 8 | triggers: 9 | - type: commit 10 | jobs: 11 | - name: Build 12 | type: builder 13 | script: |- 14 | #!/bin/bash 15 | ./.bluemix/pipeline-BUILD.sh 16 | - name: "DEPLOY" 17 | inputs: 18 | - type: job 19 | stage: BUILD 20 | job: Build 21 | triggers: 22 | - type: stage 23 | properties: 24 | - name: BLOCKCHAIN_SERVICE_INSTANCE 25 | value: ${BLOCKCHAIN_SERVICE_INSTANCE} 26 | type: text 27 | - name: CLOUDANT_SERVICE_INSTANCE 28 | value: ${CLOUDANT_SERVICE_INSTANCE} 29 | type: text 30 | jobs: 31 | - name: Deploy 32 | type: deployer 33 | target: 34 | region_id: ${REGION_ID} 35 | organization: ${ORG_NAME} 36 | space: ${SPACE_NAME} 37 | application: ${CF_APP_NAME} 38 | script: |- 39 | #!/bin/bash 40 | ./.bluemix/pipeline-DEPLOY.sh -------------------------------------------------------------------------------- /.bluemix/pipeline-CLOUDANT.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | source .bluemix/pipeline-COMMON.sh 6 | 7 | export CLOUDANT_SERVICE_NAME=cloudantNoSQLDB 8 | export CLOUDANT_SERVICE_PLAN=Lite 9 | export CLOUDANT_SERVICE_KEY=Credentials-1 10 | export CLOUDANT_DATABASE=wallet 11 | 12 | function provision_cloudant { 13 | if ! cf service ${CLOUDANT_SERVICE_INSTANCE} > /dev/null 2>&1 14 | then 15 | cf create-service ${CLOUDANT_SERVICE_NAME} ${CLOUDANT_SERVICE_PLAN} ${CLOUDANT_SERVICE_INSTANCE} 16 | fi 17 | if ! cf service-key ${CLOUDANT_SERVICE_INSTANCE} ${CLOUDANT_SERVICE_KEY} > /dev/null 2>&1 18 | then 19 | cf create-service-key ${CLOUDANT_SERVICE_INSTANCE} ${CLOUDANT_SERVICE_KEY} 20 | fi 21 | cf service-key ${CLOUDANT_SERVICE_INSTANCE} ${CLOUDANT_SERVICE_KEY} | tail -n +2 > cloudant.json 22 | export CLOUDANT_URL=$(jq --raw-output '.url' cloudant.json) 23 | export CLOUDANT_CREDS=$(jq ". + {database: \"${CLOUDANT_DATABASE}\"}" cloudant.json) 24 | } 25 | 26 | function create_cloudant_database { 27 | if ! do_curl ${CLOUDANT_URL}/${CLOUDANT_DATABASE} > /dev/null 2>&1 28 | then 29 | do_curl -X PUT ${CLOUDANT_URL}/${CLOUDANT_DATABASE} 30 | fi 31 | } 32 | 33 | function configure_composer_wallet { 34 | read -d '' NODE_CONFIG << EOF || true 35 | {"composer":{"wallet":{"type":"@ampretia/composer-wallet-cloudant","desc":"Uses cloud wallet","options":${CLOUDANT_CREDS}}}} 36 | EOF 37 | export NODE_CONFIG 38 | } -------------------------------------------------------------------------------- /.bluemix/toolchain.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | template: 3 | name: "Blockchain Starter Kit" 4 | description: "Deploy the IBM Blockchain Starter Kit to IBM Cloud." 5 | required: 6 | - repo 7 | - build 8 | 9 | toolchain: 10 | name: 'blockchain-starter-kit-{{timestamp}}' 11 | 12 | services: 13 | repo: 14 | service_id: githubpublic 15 | parameters: 16 | repo_name: '{{toolchain.name}}' 17 | repo_url: 'https://github.com/sstone1/blockchain-starter-kit' 18 | type: clone 19 | has_issues: true 20 | enable_traceability: true 21 | build: 22 | service_id: pipeline 23 | parameters: 24 | services: 25 | - repo 26 | name: '{{services.repo.parameters.repo_name}}' 27 | ui-pipeline: true 28 | sin: 'test' 29 | configuration: 30 | content: 31 | $text: pipeline.yml 32 | env: 33 | REPO: repo 34 | SPACE_NAME: '{{form.pipeline.parameters.deploy-space}}' 35 | ORG_NAME: '{{form.pipeline.parameters.deploy-organization}}' 36 | REGION_ID: '{{form.pipeline.parameters.deploy-region}}' 37 | BLOCKCHAIN_SERVICE_INSTANCE: '{{form.pipeline.parameters.blockchain-service-instance}}' 38 | CLOUDANT_SERVICE_INSTANCE: '{{form.pipeline.parameters.cloudant-service-instance}}' 39 | execute: true 40 | 41 | form: 42 | pipeline: 43 | schema: 44 | $ref: deploy.json 45 | parameters: 46 | deploy-region: '{{region}}' 47 | deploy-organization: '{{organization}}' 48 | deploy-space: '{{space}}' 49 | blockchain-service-instance: '{{sin}}' 50 | cloudant-service-instance: '{{toolchain.name}}' 51 | -------------------------------------------------------------------------------- /.bluemix/pipeline-COMMON.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | source .bluemix/config.sh 6 | 7 | export COMPOSER_VERSION=0.19.5 8 | 9 | function exit_on_error { 10 | if [ "$?" = "0" ]; then 11 | return 0 12 | fi 13 | 14 | message=${1:-${expected_error_message:-"Unexpected Error"}} 15 | 16 | echo "ERROR: ${message}" 1>&2 17 | 18 | if [ "$BLOCKCHAIN_SAMPLE_ID" != "" -a "$BLOCKCHAIN_URL" != "" ]; then 19 | request=$(jq -c -n \ 20 | --arg app "$BLOCKCHAIN_SAMPLE_APP" \ 21 | --arg url "$BLOCKCHAIN_SAMPLE_URL" \ 22 | --arg msg "$message" \ 23 | '{ 24 | app: $app, 25 | completed_step: -1, 26 | url: $url, 27 | debug_msg: $msg 28 | }') 29 | 30 | do_curl \ 31 | -X PUT \ 32 | -H 'Content-Type: application/json' \ 33 | -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} \ 34 | --data-binary "$request" \ 35 | ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/sample/${BLOCKCHAIN_SAMPLE_ID} 36 | fi 37 | 38 | exit 1 39 | } 40 | 41 | function install_nodejs { 42 | npm config delete prefix 43 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash 44 | export NVM_DIR="$HOME/.nvm" 45 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 46 | nvm install 8 47 | nvm use 8 48 | node -v 49 | npm -v 50 | } 51 | 52 | function install_composer { 53 | npm install -g composer-cli@${COMPOSER_VERSION} @ampretia/composer-wallet-cloudant 54 | } 55 | 56 | function install_jq { 57 | curl -o jq -L https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 58 | chmod +x jq 59 | export PATH=${PATH}:${PWD} 60 | } 61 | 62 | function do_curl { 63 | HTTP_RESPONSE=$(mktemp) 64 | HTTP_STATUS=$(curl -w '%{http_code}' -o ${HTTP_RESPONSE} "$@") 65 | cat ${HTTP_RESPONSE} 66 | rm -f ${HTTP_RESPONSE} 67 | if [[ ${HTTP_STATUS} -ge 200 && ${HTTP_STATUS} -lt 300 ]] 68 | then 69 | return 0 70 | else 71 | return ${HTTP_STATUS} 72 | fi 73 | } 74 | 75 | install_jq 76 | 77 | trap 'exit_on_error' ERR 78 | -------------------------------------------------------------------------------- /.bluemix/pipeline-BUILD.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | source .bluemix/pipeline-COMMON.sh 6 | 7 | export CONTRACTS=$(ls contracts) 8 | export APPS=$(ls apps) 9 | if ls contracts/*/package.json > /dev/null 2>&1 10 | then 11 | export HAS_COMPOSER_CONTRACTS=true 12 | fi 13 | 14 | function test_contracts { 15 | for CONTRACT in ${CONTRACTS} 16 | do 17 | test_contract ${CONTRACT} 18 | done 19 | } 20 | 21 | function test_contract { 22 | CONTRACT=$1 23 | if [ -f contracts/${CONTRACT}/package.json ] 24 | then 25 | test_composer_contract ${CONTRACT} 26 | elif ls contracts/${CONTRACT}/*.go > /dev/null 2>&1 27 | then 28 | test_fabric_contract ${CONTRACT} 29 | else 30 | echo unrecognized contract type ${CONTRACT} 31 | exit 1 32 | fi 33 | } 34 | 35 | function test_composer_contract { 36 | CONTRACT=$1 37 | echo testing composer contract ${CONTRACT} 38 | pushd contracts/${CONTRACT} 39 | npm install 40 | npm test 41 | rm -rf node_modules 42 | popd 43 | } 44 | 45 | function test_fabric_contract { 46 | CONTRACT=$1 47 | echo testing fabric contract ${CONTRACT} 48 | GIT_CC_HASH=$(git ls-tree --abbrev=12 HEAD contracts/${CONTRACT} | awk '{print $3}') 49 | pushd contracts/${CONTRACT} 50 | echo CHAINCODE_ID=${CONTRACT} > version.env 51 | echo CHAINCODE_VERSION=${GIT_CC_HASH} >> version.env 52 | popd 53 | } 54 | 55 | function test_apps { 56 | for APP in ${APPS} 57 | do 58 | test_app ${APP} 59 | done 60 | } 61 | 62 | function test_app { 63 | APP=$1 64 | if [ -f apps/${APP}/package.json ] 65 | then 66 | test_node_app ${APP} 67 | else 68 | echo unrecognized app type ${APP} 69 | exit 1 70 | fi 71 | } 72 | 73 | function test_node_app { 74 | CONTRACT=$1 75 | echo testing node.js app ${CONTRACT} 76 | pushd apps/${CONTRACT} 77 | npm install 78 | # npm test 79 | SCRIPTS="prepublish prepare prepublishOnly prepack" 80 | for SCRIPT in ${SCRIPTS} 81 | do 82 | if jq -e ".scripts.${SCRIPT}" package.json > /dev/null 2>&1 83 | then 84 | npm run ${SCRIPT} 85 | fi 86 | done 87 | rm -rf node_modules 88 | popd 89 | } 90 | 91 | install_nodejs 92 | test_contracts 93 | test_apps 94 | -------------------------------------------------------------------------------- /.bluemix/metadata.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Replace "your-sample" with the name of the sample recognized by the IBM Blockchain Platform. 3 | // Samples must be registered by the IBM team who manage the IBM Blockchain Platform service; 4 | // you cannot register samples yourself at this time. 5 | 'your-sample': { 6 | 'completed_steps': [ 7 | // Step 1 - install prerequisites, provision blockchain network and Cloudant wallet. 8 | { 9 | // name of step, can be anything. appears in toast when there is an error 10 | 'step': 'started', 11 | 12 | // helps pace the progress bar (seconds) 13 | 'next_step_expected_duration_s': 5 * 60, 14 | 15 | // ui will give up on sample and log an error after timeout 16 | 'next_step_timeout_s': 8 * 60 17 | }, 18 | // Step 2 - deploy contracts (Fabric chaincodes, Composer business networks). 19 | { 20 | 'step': 'deploy_contracts', 21 | 'next_step_expected_duration_s': 2 * 60, 22 | 'next_step_timeout_s': 5 * 60 23 | }, 24 | // Step 3 - deploy REST servers. 25 | { 26 | 'step': 'deploy_rest_servers', 27 | 'next_step_expected_duration_s': 2 * 60, 28 | 'next_step_timeout_s': 5 * 60 29 | }, 30 | // Step 4 - deploy applications. 31 | { 32 | 'step': 'deploy_apps', 33 | 'next_step_expected_duration_s': 2 * 60, 34 | 'next_step_timeout_s': 5 * 60 35 | }, 36 | // Step 5 - gather the URLs for the REST servers. 37 | { 38 | 'step': 'gather_rest_server_urls', 39 | 'next_step_expected_duration_s': 1 * 60, 40 | 'next_step_timeout_s': 2 * 60 41 | }, 42 | // Step 6 - gather the URLs for the applications. 43 | { 44 | 'step': 'gather_app_urls', 45 | 'next_step_expected_duration_s': 1 * 60, 46 | 'next_step_timeout_s': 2 * 60 47 | }, 48 | // Step 7 - start the REST servers. 49 | { 50 | 'step': 'start_rest_servers', 51 | 'next_step_expected_duration_s': 2 * 60, 52 | 'next_step_timeout_s': 5 * 60 53 | }, 54 | // Step 8 - start the applications. 55 | { 56 | 'step': 'start_apps', 57 | 'next_step_expected_duration_s': 2 * 60, 58 | 'next_step_timeout_s': 5 * 60 59 | }, 60 | // Last step - complete! 61 | { 62 | 'step': 'sample_up', 63 | } 64 | ] 65 | } 66 | }; -------------------------------------------------------------------------------- /.bluemix/deploy.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "Blockchain Starter Kit", 4 | "description": "", 5 | "longDescription": "The Delivery Pipeline automates the continuous deployment of the IBM Blockchain Starter Kit to IBM Cloud.", 6 | "type": "object", 7 | "properties": { 8 | "deploy-region": { 9 | "description": "The bluemix region", 10 | "type": "string", 11 | "pattern": "^(ibm:yp:us-south|ibm:ys1:us-south)$" 12 | }, 13 | "deploy-organization": { 14 | "description": "The bluemix org", 15 | "type": "string" 16 | }, 17 | "deploy-space": { 18 | "description": "The bluemix space", 19 | "type": "string" 20 | }, 21 | "blockchain-service-instance": { 22 | "description": "The Blockchain service. This service will be created if it does not already exist.", 23 | "type": "string" 24 | }, 25 | "cloudant-service-instance": { 26 | "description": "The Cloudant NoSQL DB that will be used to store Business Network Cards. This service will be created if it does not already exist.", 27 | "type": "string" 28 | } 29 | }, 30 | "required": [ 31 | "deploy-region", 32 | "deploy-organization", 33 | "deploy-space", 34 | "blockchain-service-instance", 35 | "cloudant-service-instance" 36 | ], 37 | "form": [{ 38 | "type": "validator", 39 | "url": "/devops/setup/bm-helper/helper.html" 40 | }, 41 | { 42 | "type": "table", 43 | "columnCount": 3, 44 | "widths": ["33%", "33%", "33%"], 45 | "items": [ 46 | { 47 | "type": "label", 48 | "title": "**Region:** (only US South is supported)" 49 | }, 50 | { 51 | "type": "label", 52 | "title": "**Organization:**" 53 | }, 54 | { 55 | "type": "label", 56 | "title": "**Space:**" 57 | }, 58 | { 59 | "type": "select", 60 | "key": "deploy-region", 61 | "readonly": true 62 | }, 63 | { 64 | "type": "select", 65 | "key": "deploy-organization", 66 | "readonly": false 67 | }, 68 | { 69 | "type": "select", 70 | "key": "deploy-space", 71 | "readonly": false 72 | } 73 | ] 74 | }, 75 | { 76 | "type": "text", 77 | "title": "Blockchain service name", 78 | "key": "blockchain-service-instance", 79 | "readonly": false 80 | }, 81 | { 82 | "type": "text", 83 | "title": "Cloudant NoSQL DB service name", 84 | "key": "cloudant-service-instance", 85 | "readonly": false 86 | } 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /.bluemix/pipeline-BLOCKCHAIN.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | source .bluemix/pipeline-COMMON.sh 6 | 7 | export BLOCKCHAIN_SERVICE_NAME=ibm-blockchain-5-prod 8 | export BLOCKCHAIN_SERVICE_PLAN=ibm-blockchain-plan-v1-starter-prod 9 | export BLOCKCHAIN_SERVICE_KEY=Credentials-1 10 | export BLOCKCHAIN_NETWORK_CARD=admin@blockchain-network 11 | 12 | function provision_blockchain { 13 | if ! cf service ${BLOCKCHAIN_SERVICE_INSTANCE} > /dev/null 2>&1 14 | then 15 | cf create-service ${BLOCKCHAIN_SERVICE_NAME} ${BLOCKCHAIN_SERVICE_PLAN} ${BLOCKCHAIN_SERVICE_INSTANCE} 16 | fi 17 | if ! cf service-key ${BLOCKCHAIN_SERVICE_INSTANCE} ${BLOCKCHAIN_SERVICE_KEY} > /dev/null 2>&1 18 | then 19 | cf create-service-key ${BLOCKCHAIN_SERVICE_INSTANCE} ${BLOCKCHAIN_SERVICE_KEY} 20 | fi 21 | cf service-key ${BLOCKCHAIN_SERVICE_INSTANCE} ${BLOCKCHAIN_SERVICE_KEY} | tail -n +2 > blockchain.json 22 | export BLOCKCHAIN_NETWORK_ID=$(jq --raw-output '.org1."network_id"' blockchain.json) 23 | export BLOCKCHAIN_KEY=$(jq --raw-output '.org1.key' blockchain.json) 24 | export BLOCKCHAIN_SECRET=$(jq --raw-output '.org1.secret' blockchain.json) 25 | export BLOCKCHAIN_URL=$(jq --raw-output '.org1.url' blockchain.json) 26 | } 27 | 28 | function get_blockchain_connection_profile_inner { 29 | do_curl \ 30 | -H 'Content-Type: application/json' \ 31 | -H 'Accept: application/json' \ 32 | -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} \ 33 | ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/connection_profile > blockchain-connection-profile.json 34 | } 35 | 36 | function get_blockchain_connection_profile { 37 | get_blockchain_connection_profile_inner 38 | while ! jq -e ".channels.defaultchannel" blockchain-connection-profile.json 39 | do 40 | sleep 10 41 | get_blockchain_connection_profile_inner 42 | done 43 | } 44 | 45 | function wait_for_peer_to_start { 46 | PEER=$1 47 | PEER_STATUS="not running" 48 | while [[ "$PEER_STATUS" != "running" ]] 49 | do 50 | sleep 10 51 | STATUS=$(do_curl -H 'Accept: application/json' -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/nodes/status) 52 | PEER_STATUS=$(echo ${STATUS} | jq --raw-output ".[\"${PEER}\"].status") 53 | done 54 | } 55 | 56 | function start_blockchain_peer { 57 | PEER=$1 58 | do_curl \ 59 | -X POST \ 60 | -H 'Accept: application/json' \ 61 | -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} \ 62 | ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/nodes/${PEER}/start 63 | wait_for_peer_to_start ${PEER} 64 | } 65 | 66 | function wait_for_peer_to_stop { 67 | PEER=$1 68 | PEER_STATUS="running" 69 | while [[ "$PEER_STATUS" = "running" ]] 70 | do 71 | sleep 10 72 | STATUS=$(do_curl -H 'Accept: application/json' -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/nodes/status) 73 | PEER_STATUS=$(echo ${STATUS} | jq --raw-output ".[\"${PEER}\"].status") 74 | done 75 | } 76 | 77 | function stop_blockchain_peer { 78 | PEER=$1 79 | do_curl \ 80 | -X POST \ 81 | -H 'Accept: application/json' \ 82 | -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} \ 83 | ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/nodes/${PEER}/stop 84 | wait_for_peer_to_stop ${PEER} 85 | } 86 | 87 | function restart_blockchain_peer { 88 | PEER=$1 89 | stop_blockchain_peer ${PEER} 90 | start_blockchain_peer ${PEER} 91 | } 92 | 93 | function request_admin_cert { 94 | composer card create -f ca.card -p blockchain-connection-profile.json -u ${BLOCKCHAIN_NETWORK_ENROLL_ID} -s ${BLOCKCHAIN_NETWORK_ENROLL_SECRET} 95 | composer card import -f ca.card -c ca 96 | rm -f ca.card 97 | composer identity request -c ca -d ./credentials 98 | composer card delete -c ca 99 | } 100 | 101 | function upload_admin_cert { 102 | MSPID=$(jq --raw-output 'limit(1; .organizations[].mspid)' blockchain-connection-profile.json) 103 | cat << EOF > request.json 104 | { 105 | "msp_id": "${MSPID}", 106 | "adminCertName": "${IDS_PROJECT_NAME}", 107 | "adminCertificate": "$(cat ./credentials/${BLOCKCHAIN_NETWORK_ENROLL_ID}-pub.pem | tr '\n' '~' | sed 's/~/\\r\\n/g')" 108 | } 109 | EOF 110 | do_curl \ 111 | -X POST \ 112 | -H 'Content-Type: application/json' \ 113 | -H 'Accept: application/json' \ 114 | -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} \ 115 | --data-binary @request.json \ 116 | ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/certificates 117 | rm -f request.json 118 | } 119 | 120 | function sync_channel_certs { 121 | CHANNEL=$1 122 | do_curl \ 123 | -X POST \ 124 | -H 'Accept: application/json' \ 125 | -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} \ 126 | ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/channels/${CHANNEL}/sync 127 | } 128 | 129 | function create_blockchain_network_card { 130 | get_blockchain_connection_profile 131 | export BLOCKCHAIN_NETWORK_ENROLL_ID=$(jq --raw-output 'limit(1;.certificateAuthorities[].registrar[0].enrollId)' blockchain-connection-profile.json) 132 | export BLOCKCHAIN_NETWORK_ENROLL_SECRET=$(jq --raw-output 'limit(1;.certificateAuthorities[].registrar[0].enrollSecret)' blockchain-connection-profile.json) 133 | export BLOCKCHAIN_NETWORK_CARD=${BLOCKCHAIN_NETWORK_ENROLL_ID}@blockchain-network 134 | if ! composer card list -c ${BLOCKCHAIN_NETWORK_CARD} > /dev/null 2>&1 135 | then 136 | request_admin_cert 137 | upload_admin_cert 138 | PEER=$(jq --raw-output 'limit(1; .organizations[].peers[0])' blockchain-connection-profile.json) 139 | restart_blockchain_peer ${PEER} 140 | CHANNEL=defaultchannel 141 | sync_channel_certs ${CHANNEL} 142 | composer card create -f adminCard.card -p blockchain-connection-profile.json -u ${BLOCKCHAIN_NETWORK_ENROLL_ID} -c ./credentials/${BLOCKCHAIN_NETWORK_ENROLL_ID}-pub.pem -k ./credentials/${BLOCKCHAIN_NETWORK_ENROLL_ID}-priv.pem -r PeerAdmin -r ChannelAdmin 143 | composer card import -f adminCard.card -c ${BLOCKCHAIN_NETWORK_CARD} 144 | rm -f adminCard.card 145 | fi 146 | } 147 | 148 | function update_blockchain_deploy_status { 149 | COMPLETED_STEP=$1 150 | if [[ "${BLOCKCHAIN_SAMPLE_ID}" = "" ]] 151 | then 152 | echo trying to update blockchain deploy status but no sample id specified 153 | return 0 154 | fi 155 | echo updating blockchain deploy status to ${COMPLETED_STEP} at $(date) 156 | cat << EOF > request.json 157 | { 158 | "app": "${BLOCKCHAIN_SAMPLE_APP}", 159 | "completed_step": "${COMPLETED_STEP}", 160 | "url": "${BLOCKCHAIN_SAMPLE_URL}" 161 | } 162 | EOF 163 | do_curl \ 164 | -X PUT \ 165 | -H 'Content-Type: application/json' \ 166 | -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} \ 167 | --data-binary @request.json \ 168 | ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/sample/${BLOCKCHAIN_SAMPLE_ID} 169 | rm -f request.json 170 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /.bluemix/pipeline-DEPLOY.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | source .bluemix/pipeline-COMMON.sh 6 | source .bluemix/pipeline-CLOUDANT.sh 7 | source .bluemix/pipeline-BLOCKCHAIN.sh 8 | 9 | export CONTRACTS=$(ls contracts) 10 | export APPS=$(ls apps) 11 | if ls contracts/*/package.json > /dev/null 2>&1 12 | then 13 | export HAS_COMPOSER_CONTRACTS=true 14 | fi 15 | export REST_SERVER_URLS={} 16 | 17 | function deploy_contracts { 18 | for CONTRACT in ${CONTRACTS} 19 | do 20 | deploy_contract ${CONTRACT} 21 | done 22 | } 23 | 24 | function deploy_contract { 25 | CONTRACT=$1 26 | if [ -f contracts/${CONTRACT}/package.json ] 27 | then 28 | deploy_composer_contract ${CONTRACT} 29 | elif ls contracts/${CONTRACT}/*.go > /dev/null 2>&1 30 | then 31 | deploy_fabric_contract ${CONTRACT} 32 | else 33 | echo unrecognized contract type ${CONTRACT} 34 | exit 1 35 | fi 36 | } 37 | 38 | function deploy_composer_contract { 39 | CONTRACT=$1 40 | echo deploying composer contract ${CONTRACT} 41 | pushd contracts/${CONTRACT} 42 | BUSINESS_NETWORK_NAME=$(jq --raw-output '.name' package.json) 43 | BUSINESS_NETWORK_VERSION=$(jq --raw-output '.version' package.json) 44 | BUSINESS_NETWORK_ARCHIVES=$(ls dist/*.bna) 45 | BUSINESS_NETWORK_CARD=admin@${BUSINESS_NETWORK_NAME} 46 | for BUSINESS_NETWORK_ARCHIVE in ${BUSINESS_NETWORK_ARCHIVES} 47 | do 48 | if ! OUTPUT=$(composer network install -c ${BLOCKCHAIN_NETWORK_CARD} -a ${BUSINESS_NETWORK_ARCHIVES} 2>&1) 49 | then 50 | if [[ "${OUTPUT}" != *"already installed"* ]] 51 | then 52 | echo failed to install composer contract ${CONTRACT} 53 | exit 1 54 | fi 55 | fi 56 | while ! OUTPUT=$(composer network start -c ${BLOCKCHAIN_NETWORK_CARD} -n ${BUSINESS_NETWORK_NAME} -V ${BUSINESS_NETWORK_VERSION} -A ${BLOCKCHAIN_NETWORK_ENROLL_ID} -S ${BLOCKCHAIN_NETWORK_ENROLL_SECRET} -f adminCard.card 2>&1) 57 | do 58 | if [[ "${OUTPUT}" = *"REQUEST_TIMEOUT"* ]] 59 | then 60 | sleep 30 61 | elif [[ "${OUTPUT}" = *"premature execution"* ]] 62 | then 63 | sleep 30 64 | elif [[ "${OUTPUT}" = *"chaincode exists"* ]] 65 | then 66 | BUSINESS_NETWORK_UPGRADE=true 67 | break 68 | else 69 | echo failed to start composer contract ${CONTRACT} 70 | exit 1 71 | fi 72 | done 73 | if [[ "${BUSINESS_NETWORK_UPGRADE}" = "true" ]] 74 | then 75 | while ! OUTPUT=$(composer network upgrade -c ${BLOCKCHAIN_NETWORK_CARD} -n ${BUSINESS_NETWORK_NAME} -V ${BUSINESS_NETWORK_VERSION} 2>&1) 76 | do 77 | if [[ "${OUTPUT}" = *"REQUEST_TIMEOUT"* ]] 78 | then 79 | sleep 30 80 | elif [[ "${OUTPUT}" = *"premature execution"* ]] 81 | then 82 | sleep 30 83 | elif [[ "${OUTPUT}" = *"version already exists for chaincode"* ]] 84 | then 85 | break 86 | else 87 | echo failed to upgrade composer contract ${CONTRACT} 88 | exit 1 89 | fi 90 | done 91 | else 92 | if composer card list -c ${BUSINESS_NETWORK_CARD} > /dev/null 2>&1 93 | then 94 | composer card delete -c ${BUSINESS_NETWORK_CARD} 95 | fi 96 | composer card import -f adminCard.card -c ${BUSINESS_NETWORK_CARD} 97 | fi 98 | composer network ping -c ${BUSINESS_NETWORK_CARD} 99 | done 100 | popd 101 | } 102 | 103 | function deploy_fabric_contract { 104 | CONTRACT=$1 105 | echo deploying fabric contract ${CONTRACT} 106 | pushd contracts/${CONTRACT} 107 | source version.env 108 | CHAINCODE_FILES=$(find . -name "*.go") 109 | CHAINCODE_FILE_OPTS="" 110 | CHANNEL=defaultchannel 111 | for CHAINCODE_FILE in ${CHAINCODE_FILES} 112 | do 113 | CHAINCODE_FILE_OPTS="${CHAINCODE_FILE_OPTS} -F files[]=@${CHAINCODE_FILE}" 114 | done 115 | if ! OUTPUT=$(do_curl -X POST -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} ${CHAINCODE_FILE_OPTS} -F chaincode_id=${CHAINCODE_ID} -F chaincode_version=${CHAINCODE_VERSION} ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/chaincode/install) 116 | then 117 | if [[ "${OUTPUT}" != *"chaincode code"*"exists"* ]] 118 | then 119 | echo failed to install fabric contract ${CONTRACT} 120 | exit 1 121 | fi 122 | fi 123 | cat << EOF > request.json 124 | { 125 | "chaincode_id": "${CHAINCODE_ID}", 126 | "chaincode_version": "${CHAINCODE_VERSION}", 127 | "chaincode_arguments": "[\"12345\"]" 128 | } 129 | EOF 130 | while ! OUTPUT=$(do_curl -X POST -H 'Content-Type: application/json' -u ${BLOCKCHAIN_KEY}:${BLOCKCHAIN_SECRET} --data-binary @request.json ${BLOCKCHAIN_URL}/api/v1/networks/${BLOCKCHAIN_NETWORK_ID}/channels/${CHANNEL}/chaincode/instantiate) 131 | do 132 | if [[ "${OUTPUT}" = *"Failed to establish a backside connection"* ]] 133 | then 134 | sleep 30 135 | elif [[ "${OUTPUT}" = *"premature execution"* ]] 136 | then 137 | sleep 30 138 | elif [[ "${OUTPUT}" = *"version already exists for chaincode"* ]] 139 | then 140 | break 141 | else 142 | echo failed to start fabric contract ${CONTRACT} 143 | exit 1 144 | fi 145 | done 146 | rm -f request.json 147 | popd 148 | } 149 | 150 | function deploy_rest_servers { 151 | for CONTRACT in ${CONTRACTS} 152 | do 153 | deploy_rest_server ${CONTRACT} 154 | done 155 | } 156 | 157 | function deploy_rest_server { 158 | CONTRACT=$1 159 | if [ -f contracts/${CONTRACT}/package.json ] 160 | then 161 | deploy_composer_rest_server ${CONTRACT} 162 | else 163 | echo rest server not supported for contract type ${CONTRACT} 164 | fi 165 | } 166 | 167 | function deploy_composer_rest_server { 168 | CONTRACT=$1 169 | echo deploying rest server for composer contract ${CONTRACT} 170 | pushd contracts/${CONTRACT} 171 | BUSINESS_NETWORK_NAME=$(jq --raw-output '.name' package.json) 172 | BUSINESS_NETWORK_CARD=admin@${BUSINESS_NETWORK_NAME} 173 | CF_APP_NAME=composer-rest-server-${BUSINESS_NETWORK_NAME} 174 | cf push \ 175 | ${CF_APP_NAME} \ 176 | --docker-image ibmblockchain/composer-rest-server:${COMPOSER_VERSION} \ 177 | -i 1 \ 178 | -m 256M \ 179 | --no-start \ 180 | --no-manifest 181 | cf set-env ${CF_APP_NAME} NODE_CONFIG "${NODE_CONFIG}" 182 | cf set-env ${CF_APP_NAME} COMPOSER_CARD ${BUSINESS_NETWORK_CARD} 183 | cf set-env ${CF_APP_NAME} COMPOSER_NAMESPACES required 184 | cf set-env ${CF_APP_NAME} COMPOSER_WEBSOCKETS true 185 | popd 186 | } 187 | 188 | function deploy_apps { 189 | for APP in ${APPS} 190 | do 191 | deploy_app ${APP} 192 | done 193 | } 194 | 195 | function deploy_app { 196 | APP=$1 197 | if [ -f apps/${APP}/manifest.yml ] 198 | then 199 | deploy_cf_app ${APP} 200 | elif [ -f apps/${APP}/Dockerfile ] 201 | then 202 | deploy_docker_app ${APP} 203 | else 204 | echo unrecognized app type ${APP} 205 | exit 1 206 | fi 207 | } 208 | 209 | function deploy_cf_app { 210 | APP=$1 211 | echo deploying cloud foundry app ${APP} 212 | pushd apps/${APP} 213 | cf push ${APP} -i 1 -m 128M --no-start 214 | cf bind-service ${APP} ${BLOCKCHAIN_SERVICE_INSTANCE} -c '{"permissions":"read-only"}' 215 | popd 216 | } 217 | 218 | function deploy_docker_app { 219 | APP=$1 220 | echo deploying docker app ${APP} 221 | pushd apps/${APP} 222 | echo cannot deploy docker apps just yet 223 | popd 224 | } 225 | 226 | function gather_rest_server_urls { 227 | for CONTRACT in ${CONTRACTS} 228 | do 229 | gather_rest_server_url ${CONTRACT} 230 | done 231 | } 232 | 233 | function gather_rest_server_url { 234 | CONTRACT=$1 235 | if [ -f contracts/${CONTRACT}/package.json ] 236 | then 237 | gather_composer_rest_server_url ${CONTRACT} 238 | else 239 | echo rest server not supported for contract type ${CONTRACT} 240 | fi 241 | } 242 | 243 | function gather_composer_rest_server_url { 244 | CONTRACT=$1 245 | echo gathering rest server url for composer contract ${CONTRACT} 246 | pushd contracts/${CONTRACT} 247 | BUSINESS_NETWORK_NAME=$(jq --raw-output '.name' package.json) 248 | CF_APP_NAME=composer-rest-server-${BUSINESS_NETWORK_NAME} 249 | REST_SERVER_URL=$(cf app ${CF_APP_NAME} | grep routes: | awk '{print $2}') 250 | export REST_SERVER_URLS=$(echo ${REST_SERVER_URLS} | jq ". + {\"${BUSINESS_NETWORK_NAME}\":\"https://${REST_SERVER_URL}\"}") 251 | popd 252 | } 253 | 254 | function gather_app_urls { 255 | for APP in ${APPS} 256 | do 257 | gather_app_url ${APP} 258 | done 259 | } 260 | 261 | function gather_app_url { 262 | APP=$1 263 | if [ -f apps/${APP}/manifest.yml ] 264 | then 265 | gather_cf_app_url ${APP} 266 | elif [ -f apps/${APP}/Dockerfile ] 267 | then 268 | gather_docker_app_url ${APP} 269 | else 270 | echo unrecognized app type ${APP} 271 | exit 1 272 | fi 273 | } 274 | 275 | function gather_cf_app_url { 276 | APP=$1 277 | echo gathering url for cloud foundry app ${APP} 278 | pushd apps/${APP} 279 | if [[ "${APP}" = "${BLOCKCHAIN_SAMPLE_APP}" ]] 280 | then 281 | export BLOCKCHAIN_SAMPLE_URL=$(cf app ${APP} | grep routes: | awk '{print $2}') 282 | fi 283 | popd 284 | } 285 | 286 | function gather_docker_app_url { 287 | APP=$1 288 | echo gathering url for docker app ${APP} 289 | pushd apps/${APP} 290 | echo cannot gather urls for docker apps just yet 291 | popd 292 | } 293 | 294 | function start_rest_servers { 295 | for CONTRACT in ${CONTRACTS} 296 | do 297 | start_rest_server ${CONTRACT} 298 | done 299 | } 300 | 301 | function start_rest_server { 302 | CONTRACT=$1 303 | if [ -f contracts/${CONTRACT}/package.json ] 304 | then 305 | start_composer_rest_server ${CONTRACT} 306 | else 307 | echo rest server not supported for contract type ${CONTRACT} 308 | fi 309 | } 310 | 311 | function start_composer_rest_server { 312 | CONTRACT=$1 313 | echo starting rest server for composer contract ${CONTRACT} 314 | pushd contracts/${CONTRACT} 315 | BUSINESS_NETWORK_NAME=$(jq --raw-output '.name' package.json) 316 | CF_APP_NAME=composer-rest-server-${BUSINESS_NETWORK_NAME} 317 | cf start ${CF_APP_NAME} 318 | popd 319 | } 320 | 321 | function start_apps { 322 | for APP in ${APPS} 323 | do 324 | start_app ${APP} 325 | done 326 | } 327 | 328 | function start_app { 329 | APP=$1 330 | if [ -f apps/${APP}/manifest.yml ] 331 | then 332 | start_cf_app ${APP} 333 | elif [ -f apps/${APP}/Dockerfile ] 334 | then 335 | start_docker_app ${APP} 336 | else 337 | echo unrecognized app type ${APP} 338 | exit 1 339 | fi 340 | } 341 | 342 | function start_cf_app { 343 | APP=$1 344 | echo starting cloud foundry app ${APP} 345 | pushd apps/${APP} 346 | cf set-env ${APP} REST_SERVER_URLS "${REST_SERVER_URLS}" 347 | cf start ${APP} 348 | popd 349 | } 350 | 351 | function start_docker_app { 352 | APP=$1 353 | echo starting docker app ${APP} 354 | pushd apps/${APP} 355 | echo cannot start docker apps just yet 356 | popd 357 | } 358 | 359 | install_nodejs 360 | if [[ "${HAS_COMPOSER_CONTRACTS}" = "true" ]] 361 | then 362 | install_composer 363 | provision_cloudant 364 | create_cloudant_database 365 | configure_composer_wallet 366 | fi 367 | provision_blockchain 368 | if [[ "${HAS_COMPOSER_CONTRACTS}" = "true" ]] 369 | then 370 | create_blockchain_network_card 371 | fi 372 | update_blockchain_deploy_status 1 373 | 374 | deploy_contracts & 375 | DEPLOY_CONTRACTS_PID=$! 376 | deploy_rest_servers & 377 | DEPLOY_REST_SERVERS_PID=$! 378 | deploy_apps & 379 | DEPLOY_APPS_PID=$! 380 | wait ${DEPLOY_CONTRACTS_PID} 381 | update_blockchain_deploy_status 2 382 | wait ${DEPLOY_REST_SERVERS_PID} 383 | update_blockchain_deploy_status 3 384 | wait ${DEPLOY_APPS_PID} 385 | update_blockchain_deploy_status 4 386 | 387 | gather_rest_server_urls 388 | update_blockchain_deploy_status 5 389 | gather_app_urls 390 | update_blockchain_deploy_status 6 391 | 392 | start_rest_servers & 393 | START_REST_SERVERS_PID=$! 394 | start_apps & 395 | START_APPS_PID=$! 396 | wait ${START_REST_SERVERS_PID} 397 | update_blockchain_deploy_status 7 398 | wait ${START_APPS_PID} 399 | update_blockchain_deploy_status 8 400 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blockchain-starter-kit 2 | 3 | :warning: :construction: :construction_worker: 4 | 5 | > **DISCLAIMER**: this starter kit is a **prototype** that is currently under development by the IBM Blockchain team. As a result, it may dramatically change, have major bugs, completly fail to work, or disappear altogether. There is **no** officially provided support for this starter kit; any bugs that you report may go unfixed for a longer period of time than you're comfortable with. If you're happy with all of that, then please carry on reading! As we work on this starter kit, we're extremely interested in your feedback about how we could make it better, so please do let us know by raising issues on this GitHub repository! 6 | 7 | :warning: :construction: :construction_worker: 8 | 9 | Hello! This GitHub repository contains a starter kit for building a blockchain application using the IBM Blockchain Platform. The starter kit includes everything you need for developing smart contracts, exposing them via RESTful APIs, and building end user applications. It also includes tooling that allows you to set up an IBM Cloud DevOps toolchain that will automatically deploy your blockchain application, and any future changes you make, to the IBM Cloud. 10 | 11 | Follow the steps below to get started: 12 | 13 | 1. [Setting up the local development environment](#1-setting-up-the-local-development-environment) 14 | 2. [Setting up the project and DevOps toolchain](#2-setting-up-the-project-and-devops-toolchain) 15 | 3. [Cloning the GitHub repository](#3-cloning-the-github-repository) 16 | 4. [Creating a smart contract](#4-creating-a-smart-contract) 17 | 5. [Checking the status of the DevOps toolchain](#5-checking-the-status-of-the-devops-toolchain) 18 | 6. [Accessing the deployed REST server](#6-accessing-the-deployed-rest-server) 19 | 7. [Updating the deployed smart contract](#7-updating-the-deployed-smart-contract) 20 | 8. [Creating an application](#8-creating-an-application) 21 | 9. [Accessing the deployed application](#9-accessing-the-deployed-application) 22 | 23 | ## 1. Setting up the local development environment 24 | 25 | In order to use this starter kit, you will need to know how to develop blockchain applications. The IBM Blockchain Platform is built on open source technologies from the Linux Foundation Hyperledger Project, including Hyperledger Fabric and Hyperledger Composer. 26 | 27 | You will use these skills later on as part of the starter kit, so don't skip ahead! 28 | 29 | ### Hyperledger Fabric 30 | 31 | Hyperledger Fabric is a platform for building blockchain applications. Hyperledger Fabric provides the blockchain technology itself, along with APIs and SDKs that allow you to develop smart contracts and end user applications. 32 | 33 | You can learn more about Hyperledger Fabric, including how to set up your local development environment, by following the Hyperledger Fabric tutorials available here: http://hyperledger-fabric.readthedocs.io/en/release-1.1/tutorials.html 34 | 35 | In particular, we recommend that you follow the Hyperledger Fabric "Writing Your First Application" and "Chaincode for Developers" tutorials. These tutorials will teach you how to develop a smart contract, and build an end user application. 36 | 37 | ### Hyperledger Composer 38 | 39 | Hyperledger Composer is a framework or layer that builds on top of Hyperledger Fabric, that provides functionality that allows you to model your blockchain business network, and easily generate RESTful APIs and end user applications. 40 | 41 | You can learn more about Hyperledger Composer, including how to set up your local development environment, by following the Hyperledger Composer tutorial available here: https://hyperledger.github.io/composer/latest/tutorials/tutorials.html 42 | 43 | In particular, we recommend that you follow the Hyperledger Composer development tutorial. This tutorial will teach you how to develop a smart contract, expose it via a RESTful API, and build an end user application. 44 | 45 | ### Choosing between the two 46 | 47 | This starter kit is designed to work with smart contracts developed using either Hyperledger Fabric or Hyperledger Composer. You can choose either of the two, depending on your skills, and the needs of your blockchain solution. 48 | 49 | Please note that currently, this starter kit can only generate RESTful APIs for smart contracts developed using Hyperledger Composer. You will need to build your own RESTful API server for smart contracts developed using Hyperledger Fabric. 50 | 51 | 52 | ## 2. Setting up the project and DevOps toolchain 53 | 54 | To start building a blockchain application using this starter kit, you must first clone this GitHub repository into a new GitHub repository. You will then develop your blockchain application by working on the cloned GitHub repository. You do not need to manually clone this GitHub repository; please carry on reading! 55 | 56 | You also want to set up a DevOps toolchain that will automatically build, test, and deploy your blockchain application to the IBM Cloud. The IBM Cloud DevOps service can be used to to run the DevOps toolchain, and this starter kit includes configuration suitable for use with the IBM Cloud DevOps service. 57 | 58 | Click the following link to set up a DevOps toolchain for your blockchain application: 59 | 60 | [Set up DevOps toolchain](https://console.bluemix.net/devops/setup/deploy/?repository=https%3A//github.com/sstone1/blockchain-starter-kit&branch=master&env_id=ibm%3Ayp%3Aus-south&deploy-region=ibm%3Ayp%3Aus-south) 61 | 62 | The "Create a Toolchain" page will appear: 63 | 64 | ![Toolchain initial screen](./media/toolchain-initial.png) 65 | 66 | You must need to specify a name for your DevOps toolchain in the "Toolchain Name" field. In this example, I'll use "simons-blockchain-app" as the toolchain name: 67 | 68 | ![Toolchain name specified](./media/toolchain-name-specified.png) 69 | 70 | Next, if you haven't authorized the IBM Cloud DevOps service to access your GitHub organization, you will see an "Authorize" button: 71 | 72 | ![Toolchain authorization required](./media/toolchain-auth-required.png) 73 | 74 | You must click this button and authorize the IBM Cloud DevOps service to access your GitHub organization to continue. You will then see the following options: 75 | 76 | ![Toolchain authorization complete](./media/toolchain-auth-complete.png) 77 | 78 | You must now specify a name for your clone of this GitHub repository. We recommend that you give it the same name that you used for the DevOps toolchain. In this example, I'll use "simons-blockchain-app" as the name of the cloned GitHub repository: 79 | 80 | ![Toolchain GitHub options specified](./media/toolchain-github-specified.png) 81 | 82 | Finally, you must specify a name for the Blockchain service instance you will be using on the Delivery Pipeline form. This can either be an existing service name, or the name of a new Blockchain service to create. Similary for a Cloudant NoSQL DB service which will be used as storage for a Composer wallet. For this example, I'll use "simons-blockchain-service" as the name of the new Blockchain service and "simons-cloudant-service" as the name of the new Cloudant NoSQL DB service. 83 | 84 | That's it! Click the "Create" button to create your new DevOps toolchain, and GitHub repository. You should be taken to your newly created DevOps toolchain page: 85 | 86 | ![Toolchain created](./media/toolchain-created.png) 87 | 88 | The "GitHub" button in the middle will take you to your newly created GitHub repository. You will clone this GitHub repository into your local development environment, so you can work on your blockchain application. 89 | 90 | The "Delivery Pipeline" button on the right will take you to the delivery pipeline for your DevOps toolchain. From here, you can inspect the output from the latest automated build and deployment of your blockchain application. 91 | 92 | ## 3. Cloning the GitHub repository 93 | 94 | Click on the "GitHub" button to go to your newly created GitHub repository. If you are no longer on the DevOps toolchain page, you can find it by navigating to your GitHub organisation on [https://github.com](https://github.com). 95 | 96 | ![GitHub repository](./media/github-repo.png) 97 | 98 | At the top of the page, you can see a link to the DevOps toolchain - useful if you have lost the link and need to find your way back! 99 | 100 | Clone this GitHub repository to your local development environment using your favourite Git tool of choice. If you open this repository in an editor such as Visual Studio Code, you should see the following project structure: 101 | 102 | ![VSCode initial project structure](./media/vscode-initial.png) 103 | 104 | ## 4. Creating a smart contract 105 | 106 | ### Hyperledger Fabric 107 | 108 | Create a directory in the `contracts` directory in your GitHub repository. The name of this directory will be the name of the smart contract. Inside this new directory, you can place the Go (.go) files that make up your smart contract developed using Hyperledger Fabric chaincode. 109 | 110 | In this example, I have created a smart contract called "marbles": 111 | 112 | ![Chaincode](./media/chaincode.png) 113 | 114 | You can work on this smart contract using your favourite editor, following the documentation on the Hyperledger Fabric website. 115 | 116 | ### Hyperledger Composer 117 | 118 | You can use the Yeoman plugin provided by Hyperledger Composer to create a skeleton smart contract for use in your blockchain application. You should have installed Yeoman and this plugin as part of the first step in this guide. 119 | 120 | Using the command line, change into the `contracts` directory in your GitHub repository. Run Yeoman using `yo` to generate a skeleton smart contract in this directory. Ensure that you select the "Hyperledger Composer" generator, and then specify that you want to create a "Business Network" project. 121 | 122 | In this example, I have created a smart contract called "simons-network": 123 | 124 | ![Yeoman business network](./media/yeoman-business-network.png) 125 | 126 | ![Business network](./media/business-network.png) 127 | 128 | You can work on this smart contract using your favourite editor, following the documentation on the Hyperledger Composer website. 129 | 130 | ### Pushing the changes 131 | 132 | The new smart contract will appear as pending changes in your GitHub repository. Add, commit, and push these changes into your GitHub repository. The DevOps toolchain you created earlier will detect these changes, and then automatically build, test, and deploy those changes to the IBM Cloud. 133 | 134 | ## 5. Checking the status of the DevOps toolchain 135 | 136 | Navigate to the DevOps toolchain page, and click on the "Delivery Pipeline" button. You should see the following page, giving you an overview of the current status of your delivery pipeline: 137 | 138 | ![Delivery Pipeline overview](./media/delivery-pipeline-overview.png) 139 | 140 | The delivery pipeline is made up of two phases, "BUILD" and "DEPLOY". 141 | 142 | The "BUILD" phase of the delivery pipeline clones your GitHub repository, installs any dependencies, and runs all of the automated unit tests for all of your smart contracts. If any unit tests fail, then the delivery pipeline will fail and your changes will not be deployed. 143 | 144 | The "DEPLOY" phase of the delivery pipeline deploys your smart contracts into the IBM Cloud. It is reponsible for provisioning and configuring an instance of the IBM Blockchain Platform: Starter Plan (the blockchain network), an instance of Cloudant (the wallet for blockchain credentials), deploying the smart contracts, and deploying RESTful API servers for each deployed smart contract. 145 | 146 | If you click "View logs and history", you can see the latest logs for your build: 147 | 148 | ![Delivery Pipeline logs](./media/delivery-pipeline-logs.png) 149 | 150 | Both "BUILD" and "DELIVERY" phases should be green and showing that no errors have occurred. If this is not the case, you must use the logs to investigate the cause of the errors. 151 | 152 | ## 6. Accessing the deployed REST server 153 | 154 | ### Hyperledger Fabric 155 | 156 | Currently, this starter kit does not deploy a RESTful API server for smart contracts developed using Hyperledger Fabric. 157 | 158 | ### Hyperledger Composer 159 | 160 | The DevOps toolchain has automatically deployed a RESTful API server for each deployed smart contract. You can use these RESTful APIs to build end user applications that interact with a smart contract. 161 | 162 | The URLs for the deployed RESTful API servers are available in the logs for the "DELIVERY" phase, but you can also find them in the [IBM Cloud Dashboard](https://console.bluemix.net/dashboard/apps). The RESTful API server is deployed as an application, with a name made up of "composer-rest-server-" and the name of the smart contract. In this example, the RESTful API server is called "composer-rest-server-simons-network": 163 | 164 | ![IBM Cloud Dashboard](./media/ibm-cloud-dashboard.png) 165 | 166 | Click on the application in the list to navigate to the application details page: 167 | 168 | ![IBM Cloud RESTful API server application](./media/ibm-cloud-rest-app.png) 169 | 170 | Click on the "Visit App URL" link at the top to navigate to the RESTful API explorer, that allows you to discover and try out the RESTful APIs for your deployed smart contract: 171 | 172 | ![RESTful API explorer](./media/rest-api-explorer.png) 173 | 174 | ## 7. Updating the deployed smart contract 175 | 176 | ### Hyperledger Fabric 177 | 178 | Changes to deployed smart contracts will also be automatically deployed by the DevOps toolchain as well. You can test this by changing the code in a `.go` file in your smart contract. 179 | 180 | Add, commit, and push these changes into your GitHub repository. The DevOps toolchain you created earlier will detect these changes, and then automatically build, test, and deploy those changes to the IBM Cloud. 181 | 182 | ### Hyperledger Composer 183 | 184 | Changes to deployed smart contracts will also be automatically deployed by the DevOps toolchain as well. You can test this by adding a new asset definition to a `.cto` file in your smart contract, for example: 185 | 186 | ``` 187 | asset SimonsAsset extends SampleAsset { 188 | 189 | } 190 | ``` 191 | 192 | Add, commit, and push these changes into your GitHub repository. The DevOps toolchain you created earlier will detect these changes, and then automatically build, test, and deploy those changes to the IBM Cloud. 193 | 194 | The RESTful API server will be automatically restarted as part of the deployment, and new RESTful APIs will be available for the new asset type that you have defined: 195 | 196 | ![Updated RESTful API explorer](./media/rest-api-updated.png) 197 | 198 | ## 8. Creating an application 199 | 200 | ### Hyperledger Fabric 201 | 202 | Currently, this starter kit does not assist you in creating applications that can interact with smart contracts developed using Hyperledger Fabric. You will need to develop your own applications from scratch. 203 | 204 | Add your applications into the GitHub repository by creating a directory in the `apps` directory in your GitHub repository. The name of this directory will be the name of the application. Inside this new directory, you can place the files that make up your application. You can use any technology you like to develop your applications, but you must provide a Cloud Foundry manifest file named `manifest.yml`. This Cloud Foundry file describes how to deploy to and run your application in the IBM Cloud. 205 | 206 | In this example, I have created an application called "marbles": 207 | 208 | ![Chaincode](./media/chaincode-app.png) 209 | 210 | The contents of the `manifest.yml` file at the bottom of the editor tell Cloud Foundry the command for running this application, the number of instances of the application to start, and the amount of memory each instance is given. For example: 211 | 212 | ```yaml 213 | --- 214 | applications: 215 | - disk_quota: 1024M 216 | name: marbles 217 | command: "node app.js" 218 | path: "." 219 | instances: 1 220 | memory: 256M 221 | ``` 222 | 223 | See the Cloud Foundry and IBM Cloud documentation for further information on this file. 224 | 225 | All applications deployed by this starter kit will be automatically bound to the IBM Blockchain Platform: Starter Plan instance (the blockchain network) that was created earlier. The applications can access connection information for the blockchain network from by reading the `VCAP_SERVICES` environment variable. Here is an example value for the `VCAP_SERVICES` environment variable: 226 | 227 | ```json 228 | { 229 | "ibm-blockchain-5-prod": [ 230 | { 231 | "credentials": { 232 | "org1": { 233 | "url": "https://ibmblockchain-starter.ng.bluemix.net", 234 | "network_id": "n334cef0ca48c47f884dad18a9387f36c", 235 | "key": "org1", 236 | "secret": "xxxxxxxx" 237 | }, 238 | "org2": { 239 | "url": "https://ibmblockchain-starter.ng.bluemix.net", 240 | "network_id": "n334cef0ca48c47f884dad18a9387f36c", 241 | "key": "org2", 242 | "secret": "xxxxxxxx" 243 | } 244 | }, 245 | "syslog_drain_url": null, 246 | "volume_mounts": [], 247 | "label": "ibm-blockchain-5-prod", 248 | "provider": null, 249 | "plan": "ibm-blockchain-plan-v1-starter-prod", 250 | "name": "blockchain-simons-blockchain-app", 251 | "tags": [ 252 | "blockchain", 253 | "ibm_created" 254 | ] 255 | } 256 | ] 257 | } 258 | ``` 259 | 260 | ### Hyperledger Composer 261 | 262 | You can use the Yeoman plugin provided by Hyperledger Composer to create a skeleton Angular web application. You should have installed Yeoman and this plugin as part of the first step in this guide. 263 | 264 | Using the command line, change into the `contracts` directory in your GitHub repository. Run Yeoman using `yo` to generate a skeleton Angular web application in this directory. Ensure that you select the "Hyperledger Composer" generator, and then specify that you want to create an "Angular" project. 265 | 266 | In order to run this command, you will need the business network archive (.bna) file for the currently deployed version of the smart contract. 267 | 268 | In this example, I have created an application called "simons-app": 269 | 270 | ![Yeoman application](./media/yeoman-app.png) 271 | 272 | ![Application](./media/app.png) 273 | 274 | The generated application includes a Cloud Foundry manifest file named `manifest.yml`. This Cloud Foundry file describes how to deploy to and run your application in the IBM Cloud. 275 | 276 | The contents of the `manifest.yml` file at the bottom of the editor tell Cloud Foundry the command for running this application, the number of instances of the application to start, and the amount of memory each instance is given. 277 | 278 | See the Cloud Foundry and IBM Cloud documentation for further information on this file. 279 | 280 | Applications can access the deployed RESTful API server in order to interact with the smart contract. Applications deployed by this starter kit can discover the URL of the RESTful API server by reading the `REST_SERVER_URLS` environment variable, which contains a mapping of business network name to RESTful API server URLs. Here is an example value for the `REST_SERVER_URLS` environment variable: 281 | 282 | ```json 283 | { 284 | "simons-network": "https://composer-rest-server-simons-network.mybluemix.net" 285 | } 286 | ``` 287 | 288 | Generated applications automatically read and use the RESTful API server URL in the `REST_SERVER_URLS` environment variable. 289 | 290 | Finally, all applications deployed by this starter kit will be automatically bound to the IBM Blockchain Platform: Starter Plan instance (the blockchain network) that was created earlier. The applications can access connection information for the blockchain network from by reading the `VCAP_SERVICES` environment variable. Here is an example value for the `VCAP_SERVICES` environment variable: 291 | 292 | ```json 293 | { 294 | "ibm-blockchain-5-prod": [ 295 | { 296 | "credentials": { 297 | "org1": { 298 | "url": "https://ibmblockchain-starter.ng.bluemix.net", 299 | "network_id": "n334cef0ca48c47f884dad18a9387f36c", 300 | "key": "org1", 301 | "secret": "xxxxxxxx" 302 | }, 303 | "org2": { 304 | "url": "https://ibmblockchain-starter.ng.bluemix.net", 305 | "network_id": "n334cef0ca48c47f884dad18a9387f36c", 306 | "key": "org2", 307 | "secret": "xxxxxxxx" 308 | } 309 | }, 310 | "syslog_drain_url": null, 311 | "volume_mounts": [], 312 | "label": "ibm-blockchain-5-prod", 313 | "provider": null, 314 | "plan": "ibm-blockchain-plan-v1-starter-prod", 315 | "name": "blockchain-simons-blockchain-app", 316 | "tags": [ 317 | "blockchain", 318 | "ibm_created" 319 | ] 320 | } 321 | ] 322 | } 323 | ``` 324 | 325 | ### Pushing the changes 326 | 327 | The new application will appear as pending changes in your GitHub repository. Add, commit, and push these changes into your GitHub repository. The DevOps toolchain you created earlier will detect these changes, and then automatically build, test, and deploy those changes to the IBM Cloud. 328 | 329 | ## 9. Accessing the deployed application 330 | 331 | The DevOps toolchain has automatically deployed each application. 332 | 333 | The URLs for the deployed applications are available in the logs for the "DELIVERY" phase, but you can also find them in the IBM Cloud Dashboard. In this example, the application is called "simons-app": 334 | 335 | ![IBM Cloud Dashboard](./media/ibm-cloud-dashboard-2.png) 336 | 337 | Click on the application in the list to navigate to the application details page: 338 | 339 | ![IBM Cloud application](./media/ibm-cloud-app.png) 340 | 341 | Click on the "Visit App URL" link at the top to navigate to the application: 342 | 343 | ![Running application](./media/running-app.png) 344 | --------------------------------------------------------------------------------