diff --git a/docs/source/building-a-cordapp-index.rst b/docs/source/building-a-cordapp-index.rst index fa6976870f..261b80a27c 100644 --- a/docs/source/building-a-cordapp-index.rst +++ b/docs/source/building-a-cordapp-index.rst @@ -6,6 +6,7 @@ CorDapps cordapp-overview writing-a-cordapp + debugging-a-cordapp upgrade-notes upgrading-cordapps cordapp-build-systems diff --git a/docs/source/debugging-a-cordapp.rst b/docs/source/debugging-a-cordapp.rst new file mode 100644 index 0000000000..a3dab57304 --- /dev/null +++ b/docs/source/debugging-a-cordapp.rst @@ -0,0 +1,73 @@ +Debugging a CorDapp +=================== + +.. contents:: + +There are several ways to debug your CorDapp. + +Using a ``MockNetwork`` +----------------------- + +You can attach the `IntelliJ IDEA debugger `_ to a +``MockNetwork`` to debug your CorDapp: + +* Define your flow tests as per :doc:`api-testing` + + * In your ``MockNetwork``, ensure that ``threadPerNode`` is set to ``false`` + +* Set your breakpoints +* Run the flow tests using the debugger. When the tests hit a breakpoint, execution will pause + +Using the node driver +--------------------- + +You can also attach the `IntelliJ IDEA debugger `_ to nodes +running via the node driver to debug your CorDapp. + +With the nodes in-process +^^^^^^^^^^^^^^^^^^^^^^^^^ + +1. Define a network using the node driver as per :doc:`tutorial-integration-testing` + + * In your ``DriverParameters``, ensure that ``startNodesInProcess`` is set to ``true`` + +2. Run the driver using the debugger + +3. Set your breakpoints + +4. Interact with your nodes. When execution hits a breakpoint, execution will pause + + * The nodes' webservers always run in a separate process, and cannot be attached to by the debugger + +With remote debugging +^^^^^^^^^^^^^^^^^^^^^ + +1. Define a network using the node driver as per :doc:`tutorial-integration-testing` + + * In your ``DriverParameters``, ensure that ``startNodesInProcess`` is set to ``false`` and ``isDebug`` is set to + ``true`` + +2. Run the driver. The remote debug ports for each node will be automatically generated and printed to the terminal. + For example: + +.. sourcecode:: none + + [INFO ] 11:39:55,471 [driver-pool-thread-0] (DriverDSLImpl.kt:814) internal.DriverDSLImpl.startOutOfProcessNode - + Starting out-of-process Node PartyA, debug port is 5008, jolokia monitoring port is not enabled {} + +3. Attach the debugger to the node of interest on its debug port: + + * In IntelliJ IDEA, create a new run/debug configuration of type ``Remote`` + * Set the run/debug configuration's ``Port`` to the debug port + * Start the run/debug configuration in debug mode + +4. Set your breakpoints + +5. Interact with your node. When execution hits a breakpoint, execution will pause + + * The nodes' webservers always run in a separate process, and cannot be attached to by the debugger + +By enabling remote debugging on a node +-------------------------------------- + +See :ref:`enabling-remote-debugging`. \ No newline at end of file diff --git a/docs/source/running-a-node.rst b/docs/source/running-a-node.rst index 34fde7652b..65628197c2 100644 --- a/docs/source/running-a-node.rst +++ b/docs/source/running-a-node.rst @@ -57,10 +57,12 @@ Optionally run the node's webserver as well by opening a terminal window in the .. warning:: The node webserver is for testing purposes only and will be removed soon. -Starting a node with remote debugging enabled ---------------------------------------------- -To enable remote debugging of the node, run the following from the terminal window: +.. _enabling-remote-debugging: + +Enabling remote debugging +------------------------- +To enable remote debugging of the node, run the node with the following JVM arguments: ``java -Dcapsule.jvm.args="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" -jar corda.jar`` -This command line will start the debugger on port 5005 and pause the process awaiting debugger attachment. +This will allow you to attach a debugger to your node on port 5005. diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index d97120c7b1..76dfe7891c 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -9,13 +9,12 @@ The example CorDapp .. contents:: -The example CorDapp allows nodes to agree IOUs with each other. Nodes will always agree to the creation of a new IOU -if: +The example CorDapp allows nodes to agree IOUs with each other, as long as they obey the following contract rules: -* Its value is strictly positive -* The node is not trying to issue the IOU to itself +* The IOU's value is strictly positive +* A node is not trying to issue an IOU to itself -We will deploy the CorDapp on 4 test nodes: +We will deploy and run the CorDapp on four test nodes: * **Notary**, which hosts a validating notary service * **PartyA** @@ -27,7 +26,7 @@ facts" between PartyA and PartyB only. PartyC won't be aware of these IOUs. Downloading the example CorDapp ------------------------------- -We need to download the example CorDapp from GitHub. +Start by downloading the example CorDapp from GitHub: * Set up your machine by following the :doc:`quickstart guide ` @@ -36,48 +35,28 @@ We need to download the example CorDapp from GitHub. * Change directories to the freshly cloned repo: ``cd cordapp-example`` -.. note:: If you wish to build off the latest, unstable version of the codebase, follow the instructions in - :doc:`building against Master ` instead. - Opening the example CorDapp in IntelliJ --------------------------------------- -Let's open the example CorDapp in IntelliJ IDEA. - -**If opening a fresh IntelliJ instance** +Let's open the example CorDapp in IntelliJ IDEA: * Open IntelliJ -* A dialogue box will appear: - .. image:: resources/intellij-welcome.png - :width: 400 +* A splash screen will appear. Click ``open``, navigate to the folder where you cloned the ``cordapp-example``, and + click ``OK`` -* Click open, navigate to the folder where you cloned the ``cordapp-example``, and click OK +* Once the project is open, click ``File``, then ``Project Structure``. Under ``Project SDK:``, set the project SDK by + clicking ``New...``, clicking ``JDK``, and navigating to ``C:\Program Files\Java\jdk1.8.0_XXX`` (where ``XXX`` is the + latest minor version number). Click ``OK`` -* IntelliJ will show several pop-up windows, one of which requires our attention: +* Again under ``File`` then ``Project Structure``, select ``Modules``. Click ``+``, then ``Import Module``, then select + the ``cordapp-example`` folder and click ``Open``. Choose to ``Import module from external model``, select + ``Gradle``, click ``Next`` then ``Finish`` (leaving the defaults) and ``OK`` - .. image:: resources/unlinked-gradle-project.png - :width: 400 - -* Click the 'import gradle project' link. Press OK on the dialogue that pops up - -* Gradle will now download all the project dependencies and perform some indexing. This usually takes a minute or so. - - * If the 'import gradle project' pop-up does not appear, click the small green speech bubble at the bottom-right of - the IDE, or simply close and re-open IntelliJ again to make it reappear. - -**If you already have IntelliJ open** - -* Open the ``File`` menu - -* Navigate to ``Open ...`` - -* Navigate to the directory where you cloned the ``cordapp-example`` - -* Click OK +* Gradle will now download all the project dependencies and perform some indexing. This usually takes a minute or so Project structure ------------------ -The example CorDapp has the following directory structure: +~~~~~~~~~~~~~~~~~ +The example CorDapp has the following structure: .. sourcecode:: none @@ -160,11 +139,13 @@ The key files and directories are as follows: about which version is required * **lib** contains the Quasar jar which rewrites our CorDapp's flows to be checkpointable * **kotlin-source** contains the source code for the example CorDapp written in Kotlin - * **kotlin-source/src/main/kotlin** contains the source code for the example CorDapp - * **kotlin-source/src/main/resources** contains the certificate store, some static web content to be served by the - nodes and the WebServerPluginRegistry file - * **kotlin-source/src/test/kotlin** contains unit tests for the contracts and flows, and the driver to run the nodes - via IntelliJ + + * **kotlin-source/src/main/kotlin** contains the source code for the example CorDapp + * **kotlin-source/src/main/resources** contains the certificate store, some static web content to be served by the + nodes and the WebServerPluginRegistry file + * **kotlin-source/src/test/kotlin** contains unit tests for the contracts and flows, and the driver to run the nodes + via IntelliJ + * **java-source** contains the same source code, but written in Java. CorDapps can be developed in any language targeting the JVM @@ -175,11 +156,11 @@ There are two ways to run the example CorDapp: * Via the terminal * Via IntelliJ -In both cases, we will deploy a set of test nodes with our CorDapp installed, then run the nodes. You can read more -about how we define the nodes to be deployed :doc:`here `. +Both approaches will create a set of test nodes, install the CorDapp on these nodes, and then run the nodes. You can +read more about how we generate nodes :doc:`here `. -Terminal -~~~~~~~~ +Running the example CorDapp from the terminal +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Building the example CorDapp ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -191,29 +172,26 @@ Building the example CorDapp * Windows: ``gradlew.bat deployNodes`` - This will automatically build four pre-configured nodes with our CorDapp installed. These nodes are meant for local - testing only + This will automatically build four nodes with our CorDapp already installed .. note:: CorDapps can be written in any language targeting the JVM. In our case, we've provided the example source in - both Kotlin (``/kotlin-source/src``) and Java (``/java-source/src``) Since both sets of source files are - functionally identical, we will refer to the Kotlin build throughout the documentation. + both Kotlin (``/kotlin-source/src``) and Java (``/java-source/src``). Since both sets of source files are + functionally identical, we will refer to the Kotlin version throughout the documentation. -* After the build process has finished, you will see the newly-build nodes in the ``kotlin-source/build/nodes`` folder +* After the build finishes, you will see the generated nodes in the ``kotlin-source/build/nodes`` folder - * There will be one folder generated for each node you built, plus a ``runnodes`` shell script (or batch file on - Windows) to run all the nodes simultaneously + * There will be a folder for each generated node, plus a ``runnodes`` shell script (or batch file on Windows) to run + all the nodes simultaneously * Each node in the ``nodes`` folder has the following structure: .. sourcecode:: none . nodeName - ├── corda.jar - ├── node.conf - └── cordapps - - ``corda.jar`` is the Corda runtime, ``cordapps`` contains our node's CorDapps, and the node's configuration is - given by ``node.conf`` + ├── corda.jar // The Corda node runtime. + ├── corda-webserver.jar // The node development webserver. + ├── node.conf // The node configuration file. + └── cordapps // The node's CorDapps. Running the example CorDapp ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -222,7 +200,7 @@ Start the nodes by running the following command from the root of the ``cordapp- * Unix/Mac OSX: ``kotlin-source/build/nodes/runnodes`` * Windows: ``call kotlin-source\build\nodes\runnodes.bat`` -.. warn:: On Unix/Mac OSX, do not click/change focus until all seven additional terminal windows have opened, or some +.. warning:: On Unix/Mac OSX, do not click/change focus until all seven additional terminal windows have opened, or some nodes may fail to start. For each node, the ``runnodes`` script creates a node tab/window: @@ -259,12 +237,12 @@ For every node except the notary, the script also creates a webserver terminal t Starting as webserver: localhost:10009 Webserver started up in 42.02 sec -It usually takes around 60 seconds for the nodes to finish starting up. To ensure that all the nodes are running OK, -you can query the 'status' end-point located at ``http://localhost:[port]/api/status`` (e.g. +It usually takes around 60 seconds for the nodes to finish starting up. To ensure that all the nodes are running, you +can query the 'status' end-point located at ``http://localhost:[port]/api/status`` (e.g. ``http://localhost:10009/api/status`` for ``PartyA``). -IntelliJ -~~~~~~~~ +Running the example CorDapp from IntelliJ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Select the ``Run Example CorDapp - Kotlin`` run configuration from the drop-down menu at the top right-hand side of the IDE @@ -273,66 +251,38 @@ IntelliJ .. image:: resources/run-config-drop-down.png :width: 400 - The node driver defined in ``/src/test/kotlin/com/example/Main.kt`` allows you to specify how many nodes you would like - to run and the configuration settings for each node. For the example CorDapp, the driver starts up four nodes - and adds an RPC user for all but the network map/notary node: - - .. sourcecode:: kotlin - - fun main(args: Array) { - // No permissions required as we are not invoking flows. - val user = User("user1", "test", permissions = setOf()) - driver(isDebug = true, waitForNodesToFinish = true) { - startNode(getX500Name(O="NetworkMapAndNotary",L="London",C='GB"), setOf(ServiceInfo(ValidatingNotaryService.type))) - val (nodeA, nodeB, nodeC) = Futures.allAsList( - startNode(getX500Name(O="PartyA",L="London",C="GB"), rpcUsers = listOf(user)), - startNode(getX500Name(O="PartyB",L="New York",C="US"), rpcUsers = listOf(user)), - startNode(getX500Name(O="PartyC",L="Paris",C="FR"), rpcUsers = listOf(user))).getOrThrow() - - startWebserver(nodeA) - startWebserver(nodeB) - startWebserver(nodeC) - } - } - * To stop the nodes, press the red square button at the top right-hand side of the IDE, next to the run configurations -Later, we'll look at how the node driver can be useful for `debugging your CorDapp`_. - Interacting with the example CorDapp ------------------------------------ Via HTTP ~~~~~~~~ -The CorDapp defines several HTTP API end-points and a web front-end. The end-points allow you to list the IOUs a node -is involved in, agree new IOUs, and see who is on the network. +The nodes' webservers run locally on the following ports: -The nodes are running locally on the following ports: +* PartyA: ``localhost:10009`` +* PartyB: ``localhost:10012`` +* PartyC: ``localhost:10015`` -* PartyA: ``localhost:10009`` -* PartyB: ``localhost:10012`` -* PartyC: ``localhost:10015`` +These ports are defined in each node's node.conf file under ``kotlin-source/build/nodes/NodeX/node.conf``. -These ports are defined in build.gradle and in each node's node.conf file under ``kotlin-source/build/nodes/NodeX``. - -As the nodes start up, they should tell you which port their embedded web server is running on. The available API -endpoints are: +Each node webserver exposes the following endpoints: * ``/api/example/me`` * ``/api/example/peers`` * ``/api/example/ious`` * ``/api/example/create-iou`` with parameters ``iouValue`` and ``partyName`` which is CN name of a node -The web front-end is served from ``/web/example``. - -An IOU can be created by sending a PUT request to the ``api/example/create-iou`` end-point directly, or by using the -the web form hosted at ``/web/example``. +There is also a web front-end served from ``/web/example``. .. warning:: The content in ``web/example`` is only available for demonstration purposes and does not implement - anti-XSS, anti-XSRF or any other security techniques. Do not use this code in production. + anti-XSS, anti-XSRF or other security techniques. Do not use this code in production. Creating an IOU via the endpoint ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +An IOU can be created by sending a PUT request to the ``api/example/create-iou`` endpoint directly, or by using the +the web form served from ``/web/example``. + To create an IOU between PartyA and PartyB, run the following command from the command line: .. sourcecode:: bash @@ -350,29 +300,14 @@ of the page, and enter the IOU details into the web-form. The IOU must have a po .. sourcecode:: none - Counter-party: Select from list + Counterparty: Select from list Value (Int): 5 And click submit. Upon clicking submit, the modal dialogue will close, and the nodes will agree the IOU. -Once an IOU has been submitted -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Assuming all went well, you should see some activity in PartyA's web-server terminal window: - -.. sourcecode:: none - - >> Signing transaction with our private key. - >> Gathering the counterparty's signature. - >> Collecting signatures from counterparties. - >> Verifying collected signatures. - >> Done - >> Obtaining notary signature and recording transaction. - >> Requesting signature by notary service - >> Broadcasting transaction to participants - >> Done - >> Done - -You can view the newly-created IOU by accessing the vault of PartyA or PartyB: +Checking the output +^^^^^^^^^^^^^^^^^^^ +Assuming all went well, you can view the newly-created IOU by accessing the vault of PartyA or PartyB: *Via the HTTP API:* @@ -382,7 +317,7 @@ You can view the newly-created IOU by accessing the vault of PartyA or PartyB: *Via web/example:* * PartyA: Navigate to http://localhost:10009/web/example and hit the "refresh" button -* PartyA: Navigate to http://localhost:10012/web/example and hit the "refresh" button +* PartyB: Navigate to http://localhost:10012/web/example and hit the "refresh" button The vault and web front-end of PartyC (at ``localhost:10015``) will not display any IOUs. This is because PartyC was not involved in this transaction. @@ -411,6 +346,8 @@ following list: net.corda.finance.flows.CashIssueFlow net.corda.finance.flows.CashPaymentFlow +Creating an IOU via the interactive shell +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We can create a new IOU using the ``ExampleFlow$Initiator`` flow. For example, from the interactive shell of PartyA, you can agree an IOU of 50 with PartyB by running ``flow start ExampleFlow$Initiator iouValue: 50, otherParty: "O=PartyB,L=New York,C=US"``. @@ -432,9 +369,15 @@ This will print out the following progress steps: ✅ Broadcasting transaction to participants ✅ Done +Checking the output +^^^^^^^^^^^^^^^^^^^ We can also issue RPC operations to the node via the interactive shell. Type ``run`` to see the full list of available operations. +You can see the newly-created IOU by running ``run vaultQuery contractStateType: com.example.state.IOUState``. + +As before, the interactive shell of PartyC will not display any IOUs. + Via the h2 web console ~~~~~~~~~~~~~~~~~~~~~~ You can connect directly to your node's database to see its stored states, transactions and attachments. To do so, @@ -442,17 +385,17 @@ please follow the instructions in :doc:`node-database`. Using the example RPC client ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``/src/main/kotlin-source/com/example/client/ExampleClientRPC.kt`` file is a simple utility that uses the client -RPC library to connect to a node. It will log any existing IOUs and listen for any future IOUs. If you haven't created +``/src/main/kotlin-source/com/example/client/ExampleClientRPC.kt`` defines a simple RPC client that connects to a node, +logs any existing IOUs and listens for any future IOUs. If you haven't created any IOUs when you first connect to one of the nodes, the client will simply log any future IOUs that are agreed. -*Running the client via IntelliJ:* - -Select the 'Run Example RPC Client' run configuration which, by default, connects to PartyA. Click the green arrow to -run the client. You can edit the run configuration to connect on a different port. - -*Running the client via the command line:* +Running the client via IntelliJ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Run the 'Run Example RPC Client' run configuration. By default, this run configuration is configured to connect to +PartyA. You can edit the run configuration to connect on a different port. +Running the client via the command line +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Run the following gradle task: ``./gradlew runExampleClientRPCKotlin`` @@ -466,70 +409,59 @@ For more information on the client RPC interface and how to build an RPC client * :doc:`Client RPC documentation ` * :doc:`Client RPC tutorial ` -Running Nodes Across Machines +Running nodes across machines ----------------------------- -The nodes can be split across different machines and configured to communicate across the network. +The nodes can be configured to communicate as a network even when distributed across several machines: -After deploying the nodes, navigate to the build folder (``kotlin-source/build/nodes``) and for each node that needs to -be moved to another machine open its config file and change the Artemis messaging address to the IP address of the machine -where the node will run (e.g. ``p2pAddress="10.18.0.166:10007"``). +* Deploy the nodes as usual: -These changes require new node-info files to be distributed amongst the nodes. Use the network bootstrapper tool -(see :doc:`setting-up-a-corda-network` for more information on this and how to built it) to update the files and have -them distributed locally. + * Unix/Mac OSX: ``./gradlew deployNodes`` + * Windows: ``gradlew.bat deployNodes`` -``java -jar network-bootstrapper.jar kotlin-source/build/nodes`` +* Navigate to the build folder (``kotlin-source/build/nodes``) +* For each node, open its ``node.conf`` file and change ``localhost`` in its ``p2pAddress`` to the IP address of the machine + where the node will be run (e.g. ``p2pAddress="10.18.0.166:10007"``) +* These changes require new node-info files to be distributed amongst the nodes. Use the network bootstrapper tool + (see :doc:`network-bootstrapper`) to update the files and have them distributed locally: -Once that's done move the node folders to their designated machines (e.g. using a USB key). It is important that none of the -nodes - including the notary - end up on more than one machine. Each computer should also have a copy of ``runnodes`` -and ``runnodes.bat``. + ``java -jar network-bootstrapper.jar kotlin-source/build/nodes`` -For example, you may end up with the following layout: +* Move the node folders to their individual machines (e.g. using a USB key). It is important that none of the + nodes - including the notary - end up on more than one machine. Each computer should also have a copy of ``runnodes`` + and ``runnodes.bat``. -* Machine 1: ``Notary``, ``PartyA``, ``runnodes``, ``runnodes.bat`` -* Machine 2: ``PartyB``, ``PartyC``, ``runnodes``, ``runnodes.bat`` + For example, you may end up with the following layout: -After starting each node, the nodes will be able to see one another and agree IOUs among themselves. + * Machine 1: ``Notary``, ``PartyA``, ``runnodes``, ``runnodes.bat`` + * Machine 2: ``PartyB``, ``PartyC``, ``runnodes``, ``runnodes.bat`` + +* After starting each node, the nodes will be able to see one another and agree IOUs among themselves + +.. warning:: The bootstrapper must be run **after** the ``node.conf`` files have been modified, but **before** the nodes + are distributed across machines. Otherwise, the nodes will not be able to communicate. + +.. note:: If you are using H2 and wish to use the same ``h2port`` value for two or more nodes, you must only assign them that + value after the nodes have been moved to their individual machines. The initial bootstrapping process requires access to the + nodes' databases and if two nodes share the same H2 port, the process will fail. + +Testing your CorDapp +-------------------- + +Corda provides several frameworks for writing unit and integration tests for CorDapps. + +Contract tests +~~~~~~~~~~~~~~ +You can run the CorDapp's contract tests by running the ``Run Contract Tests - Kotlin`` run configuration. + +Flow tests +~~~~~~~~~~ +You can run the CorDapp's flow tests by running the ``Run Flow Tests - Kotlin`` run configuration. + +Integration tests +~~~~~~~~~~~~~~~~~ +You can run the CorDapp's integration tests by running the ``Run Integration Tests - Kotlin`` run configuration. Debugging your CorDapp ---------------------- -Debugging is done via IntelliJ as follows: -1. Edit the node driver code in ``Main.kt`` based on the number of nodes you wish to start, along with any other - configuration options. For example, the code below starts 4 nodes, with one being the network map service and - notary. It also sets up RPC credentials for the three non-notary nodes - -.. sourcecode:: kotlin - - fun main(args: Array) { - // No permissions required as we are not invoking flows. - val user = User("user1", "test", permissions = setOf()) - driver(isDebug = true, waitForNodesToFinish = true) { - startNode(getX500Name(O="NetworkMapAndNotary",L="London",C="GB"), setOf(ServiceInfo(ValidatingNotaryService.type))) - val (nodeA, nodeB, nodeC) = Futures.allAsList( - startNode(getX500Name(O="PartyA",L=London,C=GB"), rpcUsers = listOf(user)), - startNode(getX500Name(O="PartyB",L=New York,C=US"), rpcUsers = listOf(user)), - startNode(getX500Name(O="PartyC",L=Paris,C=FR"), rpcUsers = listOf(user))).getOrThrow() - - startWebserver(nodeA) - startWebserver(nodeB) - startWebserver(nodeC) - } - } - -2. Select and run the “Run Example CorDapp” run configuration in IntelliJ - -3. IntelliJ will build and run the CorDapp. The remote debug ports for each node will be automatically generated and - printed to the terminal. For example: - -.. sourcecode:: none - - [INFO ] 15:27:59.533 [main] Node.logStartupInfo - Working Directory: /Users/joeldudley/cordapp-example/build/20170707142746/PartyA - [INFO ] 15:27:59.533 [main] Node.logStartupInfo - Debug port: dt_socket:5007 - -4. Edit the “Debug CorDapp” run configuration with the port of the node you wish to connect to - -5. Run the “Debug CorDapp” run configuration - -6. Set your breakpoints and start interacting with the node you wish to connect to. When the node hits a breakpoint, - execution will pause \ No newline at end of file +See :doc:`debugging-a-cordapp`.