diff --git a/docs/tutorials/develop-chaincode.md b/docs/tutorials/develop-chaincode.md new file mode 100644 index 0000000..6c28f07 --- /dev/null +++ b/docs/tutorials/develop-chaincode.md @@ -0,0 +1,150 @@ +# Developing and debuging chaincode + +Publishing a chaincode Docker image and using the image digest to deploy the chaincode works well with the [Fabric chaincode lifecycle](https://hyperledger-fabric.readthedocs.io/en/latest/chaincode_lifecycle.html), however it is not as convenient while developing and debugging chaincode. + +This tutorial describes how to deploy and debug the same chaincode using the chaincode-as-a-service builder and the [fabric-samples](https://github.com/hyperledger/fabric-samples/) nano test network. + +TODO: see also... +https://github.com/hyperledger/fabric-samples/blob/main/test-network/CHAINCODE_AS_A_SERVICE_TUTORIAL.md + +First create a directory to download all the required files and run the demo. + +```shell +mkdir hlf-debug-demo +cd hlf-debug-demo +``` + +Now follow the steps below to deploy your first smart contract using the k8s builder! + +## Setup the nano test network + +Download the sample nano test network (fabric-samples isn't tagged so we'll use a known good commit). + +```shell +export FABRIC_SAMPLES_COMMIT=0db64487e5e89a81d68e6871af3f0907c67e7d75 +curl -sSL "https://github.com/hyperledger/fabric-samples/archive/${FABRIC_SAMPLES_COMMIT}.tar.gz" | tar -xzf - --strip-components=1 fabric-samples-${FABRIC_SAMPLES_COMMIT}/test-network-nano-bash +``` + +Install the Fabric binaries + +TODO: ... if they're not already in your path! + +```shell +curl -sSLO https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh && chmod +x install-fabric.sh +./install-fabric.sh binary +``` + +Update chaincode-as-a-service builder configuration + +The sample `core.yaml` file needs to be updated with the correct chaincode-as-a-service builder location since it assumes that the peer is running in a docker container. Either edit the `path` for the `ccaas_builder` in `externalBuilders`, or use the following [yq](https://github.com/mikefarah/yq) command. + +```shell +yq -i '( .chaincode.externalBuilders[] | select(.name == "ccaas_builder") | .path ) = strenv(PWD) + "/builders/ccaas"' config/core.yaml +``` + +Start the nano test network + +```shell +cd test-network-nano-bash +./network.sh start +``` + +## Deploy a chaincode-as-a-service chaincode package + +With chaincode-as-a-service you still have to go through the chaincode lifecycle once to tell Fabric where the chaincode is running but after that you can stop, restart, update, and debug the chaincode all without needing to repeat the chaincode lifecycle steps as you would do normally. +This is unlikely to be acceptable in a production environment but it is ideal in a development environment. + +Like the k8s builder, the chaincode-as-a-service chaincode package does not contain any source code. +Instead, it contains the details a Fabric peer needs to connect to the chaincode server. +These details are contained in a `connection.json` and do not include anything specific to the chaincode you want to debug. +You can use exactly the same chaincode-as-a-service chaincode package to debug any chaincode, in any language. + +Start by creating an `connection.json` file. + +```shell +cat << CONNECTIONJSON_EOF > connection.json +{ + "address": "127.0.0.1:9999", + "dial_timeout": "10s", + "tls_required": false +} +CONNECTIONJSON_EOF +``` + +Create a `metadata.json` file for the chaincode package. The chaincode-as-a-service builder provided with Fabric will detect the type of `ccaas`. + +```shell +cat << METADATAJSON_EOF > metadata.json +{ + "type": "ccaas", + "label": "dev-contract" +} +METADATAJSON_EOF +``` + +Create the chaincode package archive. + +```shell +tar -czf code.tar.gz connection.json +tar -czf dev-contract.tgz metadata.json code.tar.gz +``` + + + +```shell +cd test-network-nano-bash +. ./peer1admin.sh +peer channel list +``` + +```shell +peer lifecycle chaincode install ../dev-contract.tgz +``` + +Set the CHAINCODE_ID environment variable for use in subsequent commands: + +```shell +export CHAINCODE_ID=$(peer lifecycle chaincode calculatepackageid ../dev-contract.tgz) && echo $CHAINCODE_ID +``` + +```shell +peer lifecycle chaincode approveformyorg -o 127.0.0.1:6050 --channelID mychannel --name dev-contract --version 1 --package-id $CHAINCODE_ID --sequence 1 --tls --cafile ${PWD}/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt +``` + +```shell +peer lifecycle chaincode commit -o 127.0.0.1:6050 --channelID mychannel --name dev-contract --version 1 --sequence 1 --tls --cafile "${PWD}"/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt +``` + +## Debugging the chaincode + +Normally Fabric peers start chaincode but with chaincode-as-a-service you are responsible for starting the chaincode, which is why it is so useful for debugging. + +Importantly chaincode can be started in chaincode-as-a-service mode without any code changes in recent Fabric versions, making it easy to move from a development environment, to using the k8s builder in a production environment. + +Go and Java chaincode will start in chaincode-as-a-service mode if `CHAINCODE_SERVER_ADDRESS` and `CORE_CHAINCODE_ID_NAME` environment variables are configured. The `CHAINCODE_SERVER_ADDRESS` must match the `address` field from the `connection.json` file in the chaincode package. The `CORE_CHAINCODE_ID_NAME` can be found using the `peer lifecycle chaincode calculatepackageid` command. + +Node.js chaincode can be started in chaincode-as-a-service mode using the `server` command with `--chaincode-address` and `--chaincode-id` command line arguments. The sample Node.js contract includes a `debug` script in `package.json` which uses the same `CHAINCODE_SERVER_ADDRESS` and `CORE_CHAINCODE_ID_NAME` environment variables for these command line arguments as Go and Java chaincode. + + +https://code.visualstudio.com/docs/nodejs/nodejs-debugging + +Check everything is working. + +```shell +peer chaincode query -C mychannel -n dev-contract -c '{"Args":["org.hyperledger.fabric:GetMetadata"]}' +``` + +Remember to set a breakpoint at the start of the transaction function you want to debug. + +Remember the client/fabric transaction timeout, whilst you have the chaincode stopped in the debugger, the timeout is still 'ticking' + + +```shell +peer chaincode invoke -o 127.0.0.1:6050 -C mychannel -n dev-contract -c '{"Args":["PutValue","asset1","green"]}' --waitForEvent --tls --cafile "${PWD}"/crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/ca.crt +``` + +```shell +peer chaincode query -C mychannel -n dev-contract -c '{"Args":["GetValue","asset1"]}' +``` + +TODO: check that Java works! diff --git a/mkdocs.yml b/mkdocs.yml index 6639e00..69be1d9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -107,4 +107,5 @@ nav: - Kubernetes namespace: configuring/kubernetes-namespace.md - Kubernetes service account: configuring/kubernetes-service-account.md - Tutorials: + - Developing and debuging chaincode: tutorials/develop-chaincode.md - Creating a chaincode package: tutorials/package-chaincode.md diff --git a/samples/node-contract/package.json b/samples/node-contract/package.json index b84c179..eabe8f4 100644 --- a/samples/node-contract/package.json +++ b/samples/node-contract/package.json @@ -18,7 +18,8 @@ "fix": "gts fix", "pretest": "npm run compile", "postcompile": "npm run lint", - "start": "set -x && fabric-chaincode-node start" + "start": "set -x && fabric-chaincode-node start", + "debug": "set -x && fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CORE_CHAINCODE_ID_NAME", }, "author": "Hyperledger", "license": "Apache-2.0",