diff --git a/asset-transfer-basic/chaincode-java/README.md b/asset-transfer-basic/chaincode-java/README.md index 860354a5..a21c3f2d 100644 --- a/asset-transfer-basic/chaincode-java/README.md +++ b/asset-transfer-basic/chaincode-java/README.md @@ -1,160 +1,12 @@ -## Basic asset transfer +## Basic Asset Transfer -This project demonstrates the use of the Java SDK, running a basic asset transfer contract using the "chaincode as a service" -pattern. - - -## Setup - -In this sample we will employ the [Kubernetes Test Network](../../test-network-k8s) to illustrate a scenario of -building, running, and debugging chaincode on a development workstation. - -This project is also compatible with the legacy chaincode builder pipeline and the compose based test-network. -For additional details, see the [End-to-end with the test-network](../../test-network/CHAINCODE_AS_A_SERVICE_TUTORIAL.md#end-to-end-with-the-the-test-network) -documentation. - -## [Quickstart](../../test-network-k8s#quickstart) - -``` -export PATH=${PWD}/../../test-network-k8s:$PATH - -network kind -``` -``` -network up -network channel create -``` -``` -network chaincode deploy asset-transfer-basic basic_1.0 ${PWD} -``` -``` -network chaincode metadata asset-transfer-basic -network chaincode invoke asset-transfer-basic '{"Args":["InitLedger"]}' -network chaincode query asset-transfer-basic '{"Args":["ReadAsset","asset1"]}' | jq -``` - -## Detailed Guide - -```shell -network down -network up -network channel create -``` - -```shell -# Build the chaincode docker image -docker build -t fabric-samples/asset-transfer-basic/chaincode-java . - -# Load the docker image directly to the KIND control plane. -# (Alternately, build/tag/push the image to a remote container registry, e.g. localhost:5000) -kind load docker-image fabric-samples/asset-transfer-basic/chaincode-java -``` - -```shell -# Assemble the chaincode package archive -network chaincode package basic_1.0 asset-transfer-basic $PWD/build/asset-transfer.tgz - -# Determine the ID for the chaincode package -CORE_CHAINCODE_ID_NAME=$(network chaincode id $PWD/build/asset-transfer.tgz) - -# Launch the chaincode in k8s as Deployment + Service -network chaincode launch asset-transfer-basic $CORE_CHAINCODE_ID_NAME fabric-samples/asset-transfer-basic/chaincode-java - -# Complete the chaincode lifecycle -network chaincode install $PWD/build/asset-transfer.tgz -network chaincode approve asset-transfer-basic $CORE_CHAINCODE_ID_NAME -network chaincode commit asset-transfer-basic -``` - -```shell -# execute the smart contract by name -network chaincode metadata asset-transfer-basic -network chaincode invoke asset-transfer-basic '{"Args":["InitLedger"]}' -network chaincode query asset-transfer-basic '{"Args":["ReadAsset","asset1"]}' -``` - -```shell -kubectl -n test-network logs -f deployment/org1peer1-ccaas-asset-transfer-basic -``` - -## Debugging - -### Build - -```shell -./gradlew shadowJar -``` -or -```shell -docker build -t fabric-samples/asset-transfer-basic/chaincode-java . -``` - - -### Package - -By instructing the peer to connect to chaincode at the Docker host alias `host.docker.internal`, pods running in -Kubernetes will access the local process via a special loopback interface established by KIND. - -Set the "address" attribute in the package connection.json descriptor and assemble the chaincode package: -```shell -export TEST_NETWORK_CHAINCODE_ADDRESS=host.docker.internal:9999 - -network cc package basic_1.0 asset-transfer-debug $PWD/build/asset-transfer-debug.tgz -``` - -### Launch - -When chaincode is launched locally, it must declare the package ID in the environment as if the process had been managed -by the peer's chaincode lifecycle manager. Calculate the package ID and start the chaincode, binding to port 9999 -on the local system: - -```shell -export CHAINCODE_SERVER_ADDRESS=0.0.0.0:9999 -export CORE_CHAINCODE_ID_NAME=$(network chaincode id $PWD/build/asset-transfer-debug.tgz) - -java -jar build/libs/chaincode.jar -``` - -Or using the editor/debugger/IDE of your choice, create a launch target for `ContractMain.main()`, specifying the -environment as above. - -Or launch the chaincode in a Docker container, binding to port 9999 on the host system: - -```shell -docker run \ - --rm \ - --name basic_1.0 \ - -p 9999:9999 \ - -e CHAINCODE_SERVER_ADDRESS \ - -e CORE_CHAINCODE_ID_NAME \ - fabric-samples/asset-transfer-basic/chaincode-java -``` - -### Approve, Invoke, and Query - -After the contract main has launched, install, approve, commit, and invoke the chaincode: - -```shell -# Complete the chaincode lifecycle -network cc activate asset-transfer-debug $PWD/build/asset-transfer-debug.tgz -``` - -```shell -# execute the smart contract by name -network cc metadata asset-transfer-debug -network cc invoke asset-transfer-debug '{"Args":["InitLedger"]}' -network cc query asset-transfer-debug '{"Args":["ReadAsset","asset1"]}' -``` - -## Tear Down - -```shell -network down -``` -or -```shell -network unkind -``` +This sample implements the basic asset transfer scenario, illustrating the use of the Java Contract SDKs to provide a +smart contract as a service. + +To run this chaincode contract locally on a development network, see: + +- [Debugging chaincode as a service](../../test-network-k8s/docs/CHAINCODE_AS_A_SERVICE.md) (Kube test network) +- [End-to-end with the test-network](../../test-network/CHAINCODE_AS_A_SERVICE_TUTORIAL.md#end-to-end-with-the-the-test-network) (Docker compose) diff --git a/asset-transfer-basic/chaincode-java/build.gradle b/asset-transfer-basic/chaincode-java/build.gradle index ba6d051b..d227840e 100644 --- a/asset-transfer-basic/chaincode-java/build.gradle +++ b/asset-transfer-basic/chaincode-java/build.gradle @@ -31,7 +31,7 @@ repositories { } application { - mainClass = 'org.hyperledger.fabric.contract.ContractRouter' + mainClass = 'org.hyperledger.fabric.samples.assettransfer.ContractMain' } checkstyle { diff --git a/test-network-k8s/docs/CHAINCODE.md b/test-network-k8s/docs/CHAINCODE.md index 5047f3d6..8e09e83b 100644 --- a/test-network-k8s/docs/CHAINCODE.md +++ b/test-network-k8s/docs/CHAINCODE.md @@ -200,73 +200,11 @@ docker push $TEST_NETWORK_CHAINCODE_IMAGE One of the most compelling features of Fabric's _Chaincode-as-a-Service_ pattern is that when the peer connects to a chaincode URL, it can connect back to a port on the local host. Instead of connecting to a pod running in a -container within Kubernetes, we can simply connect to a native binary running in a debugger, an IDE, or docker image -running locally! +container within Kubernetes, the chaincode process can be launched locally as a native binary in a debugger, an IDE, +or a docker image bound to the host network. -Using a singular framework, we can employ this method to enable _rapid_ **edit/test/debug cycles** when authoring -code, **verify** docker images generated by a CI/CD pipeline, and run integration tests on a local Kubernetes. - -For example, we can deploy the basic asset transfer smart contract with a [connection.json](../chaincode/asset-transfer-basic-debug/connection.json) -referencing a service bound to the Docker network's IP address for the local host: -```json -{ - "address": "host.docker.internal:9999", -} -``` -When the test network opens a TCP socket to the chaincode process, the connection will be made from containers -running within Kubernetes to the port opened on the local system. Let's employ this to technique by running a -chaincode endpoint in a local Docker container, native binary, or IDE debugger: - - -0. Edit assetTransfer.go and [Build the Chaincode Image](#build-a-chaincode-docker-image) - - -1. Bring up the test network with: -```shell -$ ./network up -$ ./network channel create -``` - -2. Install the debug chaincode archive, using a connection to localhost:9999 : -```shell -$ export TEST_NETWORK_CHAINCODE_NAME=asset-transfer-basic-debug -$ export TEST_NETWORK_CHAINCODE_IMAGE=localhost:5000/asset-transfer-basic - -$ ./network chaincode install -Installing chaincode "asset-transfer-basic-debug": -✅ - Packaging chaincode folder chaincode/asset-transfer-basic-debug ... -✅ - Transferring chaincode archive to org1 ... -✅ - Installing chaincode for org org1 ... -🏁 - Chaincode is installed with CHAINCODE_ID=basic_1.0:159ed2f227586f40c5804e157919903fda2b861488f35eefb365eb9d85a73da3 -``` - -3. Set the `CHAINCODE_ID` and launch the chaincode binding to localhost:9999: -```shell -$ export CHAINCODE_ID=basic_1.0:159ed2f227586f40c5804e157919903fda2b861488f35eefb365eb9d85a73da3 - -$ docker run \ - --rm \ - --name asset-transfer-basic-debug \ - -e CHAINCODE_ID \ - -e CHAINCODE_SERVER_ADDRESS=0.0.0.0:9999 \ - -p 9999:9999 \ - localhost:5000/asset-transfer-basic -``` - -4. Activate the chaincode (commit and approve on the peer): -```shell -$ ./network chaincode activate -``` - -When the peer communicates with chaincode in this fashion, the network will reach out to the grpc server -bound to the localhost:9999, rather than connecting to services locked up behind the wall of Kubernetes -networking. - -As an exercise, try using this approach to: - -- introduce some `fmt.Printf` logging output to the chaincode, attaching to a process running locally in an IDE / debugger. -- build your local modifications into a docker container, publishing locally to localhost:5000/asset-transfer-basic -- test your local modifications by running a chaincode referencing the image hosted in the local container registry. +For additional details, see the [debugging chaincode](CHAINCODE_AS_A_SERVICE.md) guide for running the basic asset +transfer chaincode in an interactive development workflow. ## Next Steps: diff --git a/test-network-k8s/docs/CHAINCODE_AS_A_SERVICE.md b/test-network-k8s/docs/CHAINCODE_AS_A_SERVICE.md new file mode 100644 index 00000000..2afe0329 --- /dev/null +++ b/test-network-k8s/docs/CHAINCODE_AS_A_SERVICE.md @@ -0,0 +1,161 @@ +# Debugging Chaincode + +In this sample we will employ the [Kubernetes Test Network](../README.md) to illustrate a scenario of +building, running, and debugging chaincode on a development workstation. + +While this guide targets the Java [asset-transfer-basic](../../asset-transfer-basic/chaincode-java) sample, the approach +may be applied to any sample and chaincode implementation language. + +When debugging chaincode as a service, the chaincode process is launched on the local system, binding to a port +on the host's network interface. In this mode the developer has complete flexibility in determining how and where the +process runs - it can be launched as a native binary from a CLI, attached to an active debugging session from an IDE, +as a Docker container, or even behind a reverse network proxy for diagnosing issues in a remote / cloud-based Fabric +network. + + +## TL/DR + +``` +export PATH=${PWD}/test-network-k8s:$PATH + +cd asset-transfer-basic/chaincode-java + +network kind +``` +``` +network up +network channel create +``` +``` +network chaincode deploy asset-transfer-basic basic_1.0 ${PWD} +``` +``` +network chaincode metadata asset-transfer-basic +network chaincode invoke asset-transfer-basic '{"Args":["InitLedger"]}' +network chaincode query asset-transfer-basic '{"Args":["ReadAsset","asset1"]}' | jq +``` + +## Detailed Guide + +```shell +network down +network up +network channel create +``` + +```shell +# Build the chaincode docker image +docker build -t fabric-samples/asset-transfer-basic/chaincode-java . + +# Load the docker image directly to the KIND control plane. +# (Alternately, build/tag/push the image to a remote container registry, e.g. localhost:5000 or ghcr.io) +kind load docker-image fabric-samples/asset-transfer-basic/chaincode-java +``` + +```shell +# Assemble the chaincode package archive +network chaincode package basic_1.0 asset-transfer-basic $PWD/build/asset-transfer.tgz + +# Determine the ID for the chaincode package +CORE_CHAINCODE_ID_NAME=$(network chaincode id $PWD/build/asset-transfer.tgz) + +# Launch the chaincode in k8s as Deployment + Service +network chaincode launch asset-transfer-basic $CORE_CHAINCODE_ID_NAME fabric-samples/asset-transfer-basic/chaincode-java + +# Complete the chaincode lifecycle +network chaincode install $PWD/build/asset-transfer.tgz +network chaincode approve asset-transfer-basic $CORE_CHAINCODE_ID_NAME +network chaincode commit asset-transfer-basic +``` + +```shell +# execute the smart contract by name +network chaincode metadata asset-transfer-basic +network chaincode invoke asset-transfer-basic '{"Args":["InitLedger"]}' +network chaincode query asset-transfer-basic '{"Args":["ReadAsset","asset1"]}' +``` + +```shell +kubectl -n test-network logs -f deployment/org1peer1-ccaas-asset-transfer-basic +``` + +## Debugging + +### Build + +```shell +./gradlew shadowJar +``` +or +```shell +docker build -t fabric-samples/asset-transfer-basic/chaincode-java . +``` + + +### Package + +By instructing the peer to connect to chaincode at the Docker host alias `host.docker.internal`, pods running in +Kubernetes will access the local process via a special loopback interface established by KIND. + +Set the "address" attribute in the package connection.json descriptor and assemble the chaincode package: +```shell +export TEST_NETWORK_CHAINCODE_ADDRESS=host.docker.internal:9999 + +network cc package basic_1.0 asset-transfer-debug $PWD/build/asset-transfer-debug.tgz +``` + +### Launch + +When chaincode is launched locally, it must declare the package ID in the environment as if the process had been managed +by the peer's chaincode lifecycle manager. Calculate the package ID and start the chaincode, binding to port 9999 +on the local system: + +```shell +export CHAINCODE_SERVER_ADDRESS=0.0.0.0:9999 +export CORE_CHAINCODE_ID_NAME=$(network chaincode id $PWD/build/asset-transfer-debug.tgz) + +java -jar build/libs/chaincode.jar +``` + +Or using the editor/debugger/IDE of your choice, create a launch target for `ContractMain.main()`, specifying the +environment as above. + +Or launch the chaincode in a Docker container, binding to port 9999 on the host system: + +```shell +docker run \ + --rm \ + --name basic_1.0 \ + -p 9999:9999 \ + -e CHAINCODE_SERVER_ADDRESS \ + -e CORE_CHAINCODE_ID_NAME \ + fabric-samples/asset-transfer-basic/chaincode-java +``` + +### Approve, Invoke, and Query + +After the contract main has launched, install, approve, commit, and invoke the chaincode: + +```shell +# Complete the chaincode lifecycle +network cc activate asset-transfer-debug $PWD/build/asset-transfer-debug.tgz +``` + +```shell +# execute the smart contract by name +network cc metadata asset-transfer-debug +network cc invoke asset-transfer-debug '{"Args":["InitLedger"]}' +network cc query asset-transfer-debug '{"Args":["ReadAsset","asset1"]}' +``` + +## Tear Down + +```shell +network down +``` +or +```shell +network unkind +``` + + diff --git a/test-network-k8s/docs/README.md b/test-network-k8s/docs/README.md index 8c3abd17..62b673b6 100644 --- a/test-network-k8s/docs/README.md +++ b/test-network-k8s/docs/README.md @@ -41,5 +41,6 @@ _Chaincode-as-a-Service_ running in a shared Kubernetes namespace. - [Deploy Orderers and Peers](TEST_NETWORK.md#starting-peers-and-orderers) - [Working with Channels](CHANNELS.md) - [Working with Chaincode](CHAINCODE.md) +- [Debugging Chaincode](CHAINCODE_AS_A_SERVICE.md) - [Working with Applications](APPLICATIONS.md) - [High Availability](HIGH_AVAILABILITY.md) diff --git a/test-network-k8s/scripts/chaincode.sh b/test-network-k8s/scripts/chaincode.sh index cdca638d..7bcf04d2 100755 --- a/test-network-k8s/scripts/chaincode.sh +++ b/test-network-k8s/scripts/chaincode.sh @@ -40,7 +40,10 @@ function set_chaincode_image() { } # Convenience routine to "do everything other than package and launch" a sample CC. -# This is useful in local debugging scenarios, where +# When debugging a chaincode server, the process must be launched prior to completing +# the chaincode lifecycle at the peer. This routine provides a route for packaging +# and installing the chaincode out of band, and a single target to complete the peer +# chaincode lifecycle. function activate_chaincode() { local cc_name=$1 local cc_package=$2