#!/usr/bin/env bash # To see debug logs export HFC_LOGGING='{"debug":"console","info":"console"}' # Start the app. SSH into the bastion host in one Cloud9 session nvm use lts/carbon cd ~/hyperledger-on-kubernetes/admin-api ./start.sh # In another Cloud9 session, SSH into the bastion host and run the test cases export ENDPOINT=localhost export PORT=3000 echo connecting to server: $ENDPOINT:$PORT ######################################################################################################################## # Optional commands - the /users functions are optional, and require a connection profile to be configured. # The Query function below will test that the connection profile has been correctly configured, and the REST API # is able to communicate with the Fabric network. # # Note, for these /users based calls to work, you must have updated the connection-profile. The Admin API needs to # connect to the Fabric network to carry out these function calls. See the README for details. ######################################################################################################################## # Get the admin user response=$(curl -s -X GET http://${ENDPOINT}:${PORT}/users/admin?org=org1) echo $response # Get a list of all enrolled users for an org response=$(curl -s -X GET http://${ENDPOINT}:${PORT}/users/enrolled?org=org1) echo $response # Register and enroll a new user within a specific org USERID=michael ORG=org1 echo response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/users -H 'content-type: application/json' -d '{"username":"'"${USERID}"'","org":"'"${ORG}"'"}') echo $response # Query the chaincode. Uses the connection profile to connect to the Fabric network. You must call /users above to register # a Fabric user before calling this function. This function queries the marbles chaincode response=$(curl -s -X GET http://${ENDPOINT}:${PORT}/marbles) echo $response # Invoke the chaincode. Uses the connection profile to connect to the Fabric network. You must call /users above to register # a Fabric user before calling this function. This function invokes the chaincode to add a marble response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/marbles -H 'content-type: application/json' -d '') echo $response ######################################################################################################################## # Print out the orgs and profiles contained in configtx.yaml and env.sh response=$(curl -s -X GET http://${ENDPOINT}:${PORT}/env/orgs) echo $response response=$(curl -s -X GET http://${ENDPOINT}:${PORT}/configtx/orgs) echo $response response=$(curl -s -X GET http://${ENDPOINT}:${PORT}/configtx/profiles) echo $response # Print out the ports used by the Kubernetes pods/services contained in env.sh PORTTYPE=orderer response=$(curl -s -X GET "http://${ENDPOINT}:${PORT}/env/ports?portType=${PORTTYPE}") echo $response PORTTYPE=peer response=$(curl -s -X GET "http://${ENDPOINT}:${PORT}/env/ports?portType=${PORTTYPE}") echo $response PORTTYPE=rca response=$(curl -s -X GET "http://${ENDPOINT}:${PORT}/env/ports?portType=${PORTTYPE}") echo $response PORTTYPE=ica response=$(curl -s -X GET "http://${ENDPOINT}:${PORT}/env/ports?portType=${PORTTYPE}") echo $response ######################################################################################################################## # Start a new Fabric network. After creating the EKS cluster and following the steps in the admin-api/README to install # the API server, start the Fabric network using this API. # # You can rerun this as often as necessary. It reads the configuration from ./scripts/env.sh and starts a Fabric # network using this configuration. In the background it is starting Kubernetes pods, so there is no problem running # this multiple times. ######################################################################################################################## response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/fabric/start -H 'content-type: application/json') echo $response ######################################################################################################################## # Stop a new Fabric network. Stops everything started by /fabric/start. Removes all data in /opt/share # Stop your CLI container before running this, otherwise it will prevent the PV/PVC being deleted. # Make sure all pods and services are stopped, and the /opt/share directory is empty # # kubectl get po --all-namespaces # kubectl get svc --all-namespaces # ls -l /opt/share ######################################################################################################################## response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/fabric/stop -H 'content-type: application/json') echo $response ######################################################################################################################## ######################################################################################################################## # Add a new org # # To add a new org the steps should be carried out in this order # It is possible that the port numbers used by the new org overlap ports already in use. This should be rare, as I've # added code in genPeers in fabric-main/gen-fabric-functions.sh to handle this, but in some cases an NLB will be created # with an overlapping port number. I could fix this by scanning the ports used by K8s, but haven't had time to do so. # If you encounter a 'port in use' issue, just manually edit the associated K8s yaml file and change the port number # manually. ######################################################################################################################## ######################################################################################################################## # Set the variables ORG=org4 PROFILENAME=org4profile; CHANNELNAME=org4channel; # Try and add a channel profile for an org that does not exist. This should fail as the new org does not exist response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/configtx/profiles -H 'content-type: application/json' -d '{"profilename":"'"${PROFILENAME}"'","orgs":["org1","org4"]}') echo $response # add the new org to the Fabric config file, env.sh response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/env/orgs -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # Prepare the environment for a new org: create directories, start K8s persistent volumes, etc. response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs/setup -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # Start the CA for the new org response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs/ca -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # Register the new org - this will generate an MSP for the org response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs/register -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # add the new org response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # add the new channel profile that includes the new org response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/configtx/profiles -H 'content-type: application/json' -d '{"profilename":"'"${PROFILENAME}"'","orgs":["org1","org4"]}') echo $response # create the channel configuration transaction file response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/configtx/channelconfigs -H 'content-type: application/json' -d '{"profilename":"'"${PROFILENAME}"'","channelname":"'"${CHANNELNAME}"'"}') echo $response # create the channel response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'"}') echo $response # Register a peer for new org - this will generate an identity response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/peers/register -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # Start a peer for new org - this will generate an MSP for the peer # It could take up to 5 minutes for enrollment of the peer to complete. This is because the Kubernetes pod # starts a Fabric Tools image which does not contain a fabric-ca. It seems the newer Docker images for Fabric do # not include a CA client, so I have to install it in the script. This takes time to download and build. # An alternative would be to move the code that registers the TLS certs and identities to the /peers/register script # above, since this already runs a fabric-ca image, but this will require some work to set the right ENV variables # Another alternative would be to use the Fabric SDK to generate the necessary id's and TLS certs response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/peers/start -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response sleep 300 #### #### Wait 5 minutes before starting the next command. See comment above #### Do a 'kubectl logs' on the peer pod started above to check whether fabric-ca has been built, and has generated the identities required #### #### Look for this log entry: #### $ kubectl logs peer2-org4-6c744b54d-txxt4 -n org4 -c peer2-org4 --tail=20 #### 2019-01-28 03:35:39.458 UTC [nodeCmd] serve -> INFO 020 Started peer with ID=[name:"peer2-org4" ], network ID=[dev], address=[192.168.188.57:7051] #### # join the channel. After joining the channel you should see something like this in the peer logs: # $ kubectl logs peer2-org4-59988dbdf-f29dm -n org4 -c peer2-org4 | grep org4channel # 2019-01-28 03:37:32.993 UTC [ledgermgmt] CreateLedger -> INFO 022 Creating ledger [org4channel] with genesis block # # and something similar in the logs for the other org: # $ kubectl logs peer2-org1-65c97bb4b7-l7cnl -n org1 -c peer2-org1 | grep org4channel # 2019-01-28 03:37:31.923 UTC [ledgermgmt] CreateLedger -> INFO 022 Creating ledger [org4channel] with genesis block response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels/join -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'","orgs":["org1","org4"]}') echo $response # install chaincode on all peers belonging to an org # Before running this command, copy the chaincode into this directory: /opt/share/rca-scripts/chaincode/, # e.g. if you are copying marbles chaincode, copy here: /opt/share/rca-scripts/chaincode/marblescc # # Example of how to do this for marbles. On bastion host: # # mkdir -p /opt/share/rca-scripts/chaincode/marblescc # cd /opt/share/rca-scripts/chaincode/marblescc # git clone https://github.com/IBM-Blockchain/marbles.git # cp marbles/chaincode/src/marbles/* . # rm -rf marbles # # The end result is the marbles chaincode in the chaincode directory CHANNELNAME=org4channel; CHAINCODENAME=marblescc; CHAINCODEVERSION=1; CHAINCODELANGUAGE=golang; ORG=org1 response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels/chaincode/install -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'","chaincodename":"'"${CHAINCODENAME}"'","chaincodeversion":"'"${CHAINCODEVERSION}"'","chaincodelanguage":"'"${CHAINCODELANGUAGE}"'","org":"'"${ORG}"'"}') ORG=org4 response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels/chaincode/install -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'","chaincodename":"'"${CHAINCODENAME}"'","chaincodeversion":"'"${CHAINCODEVERSION}"'","chaincodelanguage":"'"${CHAINCODELANGUAGE}"'","org":"'"${ORG}"'"}') echo $response # instantiate chaincode on a peer CHANNELNAME=org4channel; CHAINCODENAME=marblescc; CHAINCODEVERSION=1; response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels/chaincode/instantiate -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'","chaincodename":"'"${CHAINCODENAME}"'","chaincodeversion":"'"${CHAINCODEVERSION}"'","chaincodeinit":["'"init"'"],"orgs":["org1","org4"]}') echo $response ######################################################################################################################## ######################################################################################################################## # Add a new org with a remote peer, running in another AWS account and possibly a different AWS region # # There are a number of ways to do this: # See the README in ./remote-org for details on how to setup a remote peer with its own CA. # See the README in ./remote-peer for details on how to setup a remote peer where the MSP is provided by the creator org # # In this section we will run the CA in the same account/region as the creator org, which means the MSP directory will # be available in the same EFS location. This is easier because it requires less interaction between the admins of the # two orgs, however, in a full production environment you would have separate CAs running in separate accounts, each # generating their own identities, and you would have an out-of-band process to share the public keys between each # org. This is discussed in detail in the README in ./remote-org # # The steps below create a new org, then share the MSP of the org via S3. We then start a new EKS cluster in a # different account, import the MSP, then start a CA and peer nodes. The peers connect to the orderer in the creator # Fabric network. This is the same approach taken by ./remote-peer and the workshop in workshop-remote-peer. # ######################################################################################################################## ######################################################################################################################## ######################################################################################################################## # Run these steps in the main Fabric network owned by the creator org ######################################################################################################################## # Set the variables ORG=remoteorg1 PROFILENAME=remoteorg1profile; CHANNELNAME=remoteorg1channel; # add the new org to the Fabric config file, env.sh response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/env/orgs -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # Prepare the environment for a new org: create directories, start K8s persistent volumes, etc. response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs/setup -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # Start the CA for the new org response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs/ca -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # Register the new org - this will generate an MSP for the org response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs/register -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # add the new org response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # add the new channel profile that includes the new org response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/configtx/profiles -H 'content-type: application/json' -d '{"profilename":"'"${PROFILENAME}"'","orgs":["org1","remoteorg1"]}') echo $response # create the channel configuration transaction file response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/configtx/channelconfigs -H 'content-type: application/json' -d '{"profilename":"'"${PROFILENAME}"'","channelname":"'"${CHANNELNAME}"'"}') echo $response # create the channel response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'"}') echo $response # org1 joins the channel. After joining the channel you should see something like this in the peer logs: # $ kubectl logs peer2-org4-59988dbdf-f29dm -n org4 -c peer2-org4 | grep org4channel # 2019-01-28 03:37:32.993 UTC [ledgermgmt] CreateLedger -> INFO 022 Creating ledger [org4channel] with genesis block # # and something similar in the logs for the other org: # $ kubectl logs peer2-org1-65c97bb4b7-l7cnl -n org1 -c peer2-org1 | grep org4channel # 2019-01-28 03:37:31.923 UTC [ledgermgmt] CreateLedger -> INFO 022 Creating ledger [org4channel] with genesis block response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels/join -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'","orgs":["org1"]}') echo $response # install chaincode on all peers belonging to org1 CHANNELNAME=remoteorg1channel; CHAINCODENAME=marblescc; CHAINCODEVERSION=1.0; CHAINCODELANGUAGE=golang; ORG=org1 response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels/chaincode/install -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'","chaincodename":"'"${CHAINCODENAME}"'","chaincodeversion":"'"${CHAINCODEVERSION}"'","chaincodelanguage":"'"${CHAINCODELANGUAGE}"'","org":"'"${ORG}"'"}') echo $response # share the MSP via S3 REGION=ap-southeast-1; ACCOUNT=295744685835; ORG=remoteorg1 S3BUCKETNAME=acn-bkt-s3; response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs/msp/upload -H 'content-type: application/json' -d '{"region":"'"${REGION}"'","account":"'"${ACCOUNT}"'","S3bucketname":"'"${S3BUCKETNAME}"'","org":"'"${ORG}"'"}') echo $response ######################################################################################################################## # Follow the first part of the README in ./remote-peer to create an EKS cluster in a separate AWS account. # Then run these steps in the new EKS cluster Fabric network ######################################################################################################################## # Start the app in one Cloud9 session nvm use lts/carbon node app.js # In another Cloud9 session run the test cases export ENDPOINT=localhost export PORT=3000 echo connecting to server: $ENDPOINT:$PORT ORG=remoteorg1 # download and extract the MSP ORG=remoteorg1 S3BUCKETNAME=acn-bkt-s3; response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs/msp/download -H 'content-type: application/json' -d '{"S3bucketname":"'"${S3BUCKETNAME}"'","org":"'"${ORG}"'"}') echo $response # prepare the remote org ORG=remoteorg1 response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/orgs/remote/setup -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # start a CA and peer for the remote org, which points to the MSP just downloaded ORG=remoteorg1 response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/peers/remote/start -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # Register a peer for new org - this will generate an identity ORG=remoteorg1 response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/peers/register -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response # Start a peer for new org - this will generate an MSP for the peer # It could take up to 5 minutes for enrollment of the peer to complete. This is because the Kubernetes pod # starts a Fabric Tools image which does not contain a fabric-ca. It seems the newer Docker images for Fabric do # not include a CA client, so I have to install it in the script. This takes time to download and build. # An alternative would be to move the code that registers the TLS certs and identities to the /peers/register script # above, since this already runs a fabric-ca image, but this will require some work to set the right ENV variables # Another alternative would be to use the Fabric SDK to generate the necessary id's and TLS certs response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/peers/start -H 'content-type: application/json' -d '{"org":"'"${ORG}"'"}') echo $response sleep 300 #### #### Wait 5 minutes before starting this. See comment above #### Do a 'kubectl logs' on the peer pod started above to check whether fabric-ca has been built, and has generated the identities required #### #### Look for this log entry: #### $ kubectl logs peer2-org4-6c744b54d-txxt4 -n org4 -c peer2-org4 --tail=20 #### 2019-01-28 03:35:39.458 UTC [nodeCmd] serve -> INFO 020 Started peer with ID=[name:"peer2-org4" ], network ID=[dev], address=[192.168.188.57:7051] #### # join the channel. After joining the channel you should see something like this in the peer logs: # $ kubectl logs peer2-org4-59988dbdf-f29dm -n org4 -c peer2-org4 | grep org4channel # 2019-01-28 03:37:32.993 UTC [ledgermgmt] CreateLedger -> INFO 022 Creating ledger [org4channel] with genesis block # # and something similar in the logs for the other org: # $ kubectl logs peer2-org1-65c97bb4b7-l7cnl -n org1 -c peer2-org1 | grep org4channel # 2019-01-28 03:37:31.923 UTC [ledgermgmt] CreateLedger -> INFO 022 Creating ledger [org4channel] with genesis block response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels/join -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'","orgs":["remoteorg1"]}') echo $response # install chaincode on all peers belonging to the new org CHANNELNAME=remoteorg1channel; CHAINCODENAME=marblescc; CHAINCODEVERSION=1.0; CHAINCODELANGUAGE=golang; ORG=remoteorg1 response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels/chaincode/install -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'","chaincodename":"'"${CHAINCODENAME}"'","chaincodeversion":"'"${CHAINCODEVERSION}"'","chaincodelanguage":"'"${CHAINCODELANGUAGE}"'","org":"'"${ORG}"'"}') echo $response # instantiate chaincode on a peer CHANNELNAME=remoteorg1channel; CHAINCODENAME=marblescc; CHAINCODEVERSION=1.0; CHAINCODEINIT={"Args":["init"]}; ORG=remoteorg1 response=$(curl -s -X POST http://${ENDPOINT}:${PORT}/channels/chaincode/instantiate -H 'content-type: application/json' -d '{"channelname":"'"${CHANNELNAME}"'","chaincodename":"'"${CHAINCODENAME}"'","chaincodeversion":"'"${CHAINCODEVERSION}"'","chaincodeinit":"'"${CHAINCODEINIT}"'","org":"'"${ORG}"'"}') echo $response ######################################################################################################################## # Execute a few transactions ######################################################################################################################## # Enter into the CLI container from the bastion host # kubectl exec -it cli-65db594f56-xkr2c -n org0 bash # Set the context to the MSP for one of the orgs that belong to the channel you specify below export ORG=org1 export CORE_PEER_TLS_CLIENTCERT_FILE=/data/tls/peer2-${ORG}-client.crt export CORE_PEER_TLS_CLIENTKEY_FILE=/data/tls/peer2-${ORG}-client.key export CORE_PEER_TLS_ENABLED=true export CORE_PEER_TLS_CLIENTAUTHREQUIRED=true export CORE_PEER_TLS_CLIENTROOTCAS_FILES=/data/${ORG}-ca-chain.pem export CORE_PEER_TLS_ROOTCERT_FILE=/data/${ORG}-ca-chain.pem export CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 export CORE_PEER_ID=peer2-${ORG} export CORE_PEER_ADDRESS=peer2-${ORG}.${ORG}:7051 export CORE_PEER_LOCALMSPID=${ORG}MSP export CORE_PEER_MSPCONFIGPATH=/data/orgs/${ORG}/admin/msp # Set the channel/chaincode export CHANNELNAME=org4channel; export CHAINCODENAME=marblescc; export ORDERER_CONN_ARGS="-o orderer3-org0.org0:7050 --cafile /data/org0-ca-chain.pem" # Invoke peer chaincode invoke -C $CHANNELNAME -n $CHAINCODENAME -c '{"Args":["init_owner","o8888888888888888880","edge", "United Marbles"]}' $ORDERER_CONN_ARGS peer chaincode invoke -C $CHANNELNAME -n $CHAINCODENAME -c '{"Args":["init_owner","o8888888888888888881","braendle", "United Marbles"]}' $ORDERER_CONN_ARGS peer chaincode invoke -C $CHANNELNAME -n $CHAINCODENAME -c '{"Args":["init_marble","m888888888880", "blue", "50", "o8888888888888888880", "United Marbles"]}' $ORDERER_CONN_ARGS peer chaincode invoke -C $CHANNELNAME -n $CHAINCODENAME -c '{"Args":["init_marble","m888888888881", "red", "35", "o8888888888888888881", "United Marbles"]}' $ORDERER_CONN_ARGS # Query peer chaincode query -C $CHANNELNAME -n $CHAINCODENAME -c '{"Args":["read_everything"]}' # Transfer a marble peer chaincode invoke -C $CHANNELNAME -n $CHAINCODENAME -c '{"Args":["set_owner","m888888888881","o8888888888888888880", "United Marbles"]}' $ORDERER_CONN_ARGS # Add some load while [ true ] ; do peer chaincode invoke -C $CHANNELNAME -n $CHAINCODENAME -c '{"Args":["set_owner","m888888888881","o8888888888888888880", "United Marbles"]}' $ORDERER_CONN_ARGS; done # Check the logs of the peer nodes joined to the channel to see the blocks being processed