Creating nodes locally

Handcrafting a node

A node can be created manually by creating a folder that contains the following items:

  • The Corda JAR, downloaded from https://r3.bintray.com/corda/net/corda/corda/ (under /4.3-SNAPSHOT/corda-4.3-SNAPSHOT.jar)

  • A node configuration file entitled node.conf, configured as per Node configuration

  • A folder entitled cordapps containing any CorDapp JARs you want the node to load

  • An up-to-date version of the network-parameters file ([see docs:](https://docs.corda.net/network-map.html#network-parameters)) generated by the bootstrapper tool

  • Optional: A webserver JAR entitled corda-webserver-4.3-SNAPSHOT.jar that will connect to the node via RPC

    • The (deprecated) default webserver is available to you for testing and should not be used in a production environment.
    • A Spring Boot alternative can be found here: https://github.com/corda/spring-webserver

The remaining files and folders described in Node folder structure will be generated at runtime.

The Cordform task

Corda provides a gradle plugin called Cordform that allows you to automatically generate and configure a set of nodes for testing and demos. Here is an example Cordform task called deployNodes that creates three nodes, defined in the Kotlin CorDapp Template:

task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
    directory "./build/nodes"
    node {
        name "O=Notary,L=London,C=GB"
        // The notary will offer a validating notary service.
        notary = [validating : true]
        p2pPort  10002
        rpcSettings {
            port 10003
            adminPort 10023
        }
        // No webport property, so no webserver will be created.
        h2Port   10004
        // Starts an internal SSH server providing a management shell on the node.
        sshdPort 2223
        // Includes the corda-finance CorDapp on our node.
        cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
        extraConfig = [
            // Setting the JMX reporter type.
            jmxReporterType: 'JOLOKIA',
            // Setting the H2 address.
            h2Settings: [ address: 'localhost:10030' ]
        ]
    }
    node {
        name "O=PartyA,L=London,C=GB"
        p2pPort  10005
        rpcSettings {
            port 10006
            adminPort 10026
        }
        webPort  10007
        h2Port   10008
        cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
        // Grants user1 all RPC permissions.
        rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
    }
    node {
        name "O=PartyB,L=New York,C=US"
        p2pPort  10009
        rpcSettings {
            port 10010
            adminPort 10030
        }
        webPort  10011
        h2Port   10012
        cordapps = ["$corda_release_distribution:corda-finance:$corda_release_version"]
        // Grants user1 the ability to start the MyFlow flow.
        rpcUsers = [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
    }
}

Running this task will create three nodes in the build/nodes folder:

  • A Notary node that:
    • Offers a validating notary service
    • Will not have a webserver (since webPort is not defined)
    • Is running the corda-finance CorDapp
  • PartyA and PartyB nodes that:
    • Are not offering any services
    • Will have a webserver (since webPort is defined)
    • Are running the corda-finance CorDapp
    • Have an RPC user, user1, that can be used to log into the node via RPC

Additionally, all three nodes will include any CorDapps defined in the project’s source folders, even though these CorDapps are not listed in each node’s cordapps entry. This means that running the deployNodes task from the template CorDapp, for example, would automatically build and add the template CorDapp to each node.

The configuration values available in deployNodes task are as follows:

Required configuration

  • name <string>

    • The legal identity name of the Corda node. (see myLegalName)

    • e.g.

      name "O=PartyA,L=London,C=GB"
      
  • p2pAddress <string> <required if p2pPort not specified>

    • The address/port the node uses for inbound communication from other nodes. (see p2pAddress)

    • e.g.

      p2pAddress "example.com:10002"
      
  • p2pPort <integer>

    • The port the node uses for inbound communication from other nodes. Assumes the address is localhost. (see p2pAddress)

    • e.g.

      p2pPort 10006  // "localhost:10006"
      
  • rpcSettings <config>

    • Specifies RPC settings for the node. (see rpcSettings)

    • e.g.

      rpcSettings {
        port 10006
        adminPort 10026
      }
      

Optional configuration

  • notary <config> (see notary)

    • Optional configuration which specifies the node is a notary.

    • Note

      <required> for notary nodes

  • devMode <boolean>

    • When true enables development mode. (see devMode)

    • e.g.

      devMode true
      
  • webAddress <string>

    • Configure a webserver to connect to the node via RPC. This will specify the address and port it will listen on. The node must have an RPC address configured. (see Specifying a custom webserver)

    • e.g.

      webAddress  "example.com:10011"
      
  • webPort <integer>

    • Configure a webserver to connect to the node via RPC. Defaults the address to localhost. The node must have an RPC address configured. (see Specifying a custom webserver)

    • e.g.

      webPort  10011  // "localhost:10011"
      
  • rpcUsers <list>

    • Set the RPC users for this node. (see rpcUsers)

    • e.g.

      rpcUsers = [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
      
    • This configuration block allows arbitrary configuration. Incorrect configurations will not cause a DSL error.

  • configFile <string>

    • For extending configuration of nodes. (see extended node configuration)

    • e.g.

      configFile = "samples/trader-demo/src/main/resources/node-b.conf"
      
  • https <boolean>

    • When true enables HTTPS communication from the node webserver.

    • e.g.

      https true
      
  • sshdPort <integer>

    • Specifies the port for sshd communication. (see sshd)

    • e.g.

      sshd {
        port = 2222
      }
      

You can extend the task deployNodes with more node {} blocks to generate as many nodes as necessary for your application.

Warning

When adding nodes, make sure that there are no port clashes!

To extend node configuration beyond the properties defined in the deployNodes task use the configFile property with the path (relative or absolute) set to an additional configuration file. This file should follow the standard Node configuration format, as per node.conf. The properties from this file will be appended to the generated node configuration. Note, if you add a property already created by the ‘deployNodes’ task, both properties will be present in the file. The path to the file can also be added while running the Gradle task via the -PconfigFile command line option. However, the same file will be applied to all nodes. Following the previous example PartyB node will have additional configuration options added from a file none-b.conf:

task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
    [...]
    node {
        name "O=PartyB,L=New York,C=US"
        [...]
        // Grants user1 the ability to start the MyFlow flow.
        rpcUsers = [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
        configFile = "samples/trader-demo/src/main/resources/node-b.conf"
    }
}

Cordform parameter drivers of the node entry lists paths of the files to be copied to the ./drivers subdirectory of the node. To copy the same file to all nodes ext.drivers can be defined in the top level and reused for each node via drivers=ext.drivers`.

task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
    ext.drivers = ['lib/my_common_jar.jar']
    [...]
    node {
        name "O=PartyB,L=New York,C=US"
        [...]
        drivers = ext.drivers + ['lib/my_specific_jar.jar']
    }
}

Package namespace ownership

To specify package namespace ownership, the optional networkParameterOverrides and packageOwnership blocks can be used, similar to the configuration file used in Network Bootstrapper:

task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
    [...]
    networkParameterOverrides {
        packageOwnership {
            "com.mypackagename" {
                keystore = "_teststore"
                keystorePassword = "MyStorePassword"
                keystoreAlias = "MyKeyAlias"
            }
        }
    }
    [...]
}

Signing CorDapp JARs

The default behaviour of Cordform is to deploy CorDapp JARs “as built”:

  • prior to Corda 4 all CorDapp JARs were unsigned.
  • as of Corda 4, CorDapp JARs created by the Gradle cordapp plugin are signed by a Corda development certificate by default.

The Cordform signing entry can be used to override and customise the signing of CorDapp JARs. Signing the CorDapp enables its contract classes to use signature constraints instead of other types of the constraints API: Contract Constraints.

The sign task may use an external keystore, or create a new one. The signing entry may contain the following parameters:

  • enabled the control flag to enable signing process, by default is set to false, set to true to enable signing
  • all if set to true (by default) all CorDapps inside cordapp subdirectory will be signed, otherwise if false then only the generated Cordapp will be signed
  • options any relevant parameters of SignJar ANT task and GenKey ANT task, by default the JAR file is signed by Corda development key, the external keystore can be specified, the minimal list of required options is shown below, for other options referer to SignJar task:
    • keystore the path to the keystore file, by default cordadevcakeys.jks keystore is shipped with the plugin
    • alias the alias to sign under, the default value is cordaintermediateca
    • storepass the keystore password, the default value is cordacadevpass
    • keypass the private key password if it’s different than the password for the keystore, the default value is cordacadevkeypass
    • storetype the keystore type, the default value is JKS
    • dname the distinguished name for entity, the option is used when generateKeystore true only
    • keyalg the method to use when generating name-value pair, the value defaults to RSA as Corda doesn’t support DSA, the option is used when generateKeystore true only
  • generateKeystore the flag to generate a keystore, it is set to false by default. If set to true then ad hock keystore is created and its key isused instead of the default Corda development key or any external key. The same options to specify an external keystore are used to define the newly created keystore. Additionally dname and keyalg are required. Other options are described in GenKey task. If the existing keystore is already present the task will reuse it, however if the file is inside the build directory, then it will be deleted when Gradle clean task is run.

The example below shows the minimal set of options needed to create a dummy keystore:

task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
     signing {
        enabled true
        generateKeystore true
        all false
        options {
            keystore "./build/nodes/jarSignKeystore.p12"
            alias "cordapp-signer"
            storepass "secret1!"
            storetype "PKCS12"
            dname "OU=Dummy Cordapp Distributor, O=Corda, L=London, C=GB"
            keyalg "RSA"
        }
    }
    //...

Contracts classes from signed CorDapp JARs will be checked by signature constraints by default. You can force them to be checked by zone constraints by adding contract class names to includeWhitelist entry, the list will generate include_whitelist.txt file used internally by Network Bootstrapper tool. Refer to API: Contract Constraints to understand implication of different constraint types before adding includeWhitelist to deployNodes task. The snippet below configures contracts classes from Finance CorDapp to be verified using zone constraints instead of signature constraints:

task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
    includeWhitelist = [ "net.corda.finance.contracts.asset.Cash", "net.corda.finance.contracts.asset.CommercialPaper" ]
    //...

Specifying a custom webserver

By default, any node listing a web port will use the default development webserver, which is not production-ready. You can use your own webserver JAR instead by using the webserverJar argument in a Cordform node configuration block:

node {
    name "O=PartyA,L=New York,C=US"
    webPort 10005
    webserverJar "lib/my_webserver.jar"
}

The webserver JAR will be copied into the node’s build folder with the name corda-webserver.jar.

Warning

This is an experimental feature. There is currently no support for reading the webserver’s port from the node’s node.conf file.

The Dockerform task

The Dockerform is a sister task of Cordform that provides an extra file allowing you to easily spin up nodes using docker-compose. It supports the following configuration options for each node:

  • name
  • notary
  • cordapps
  • rpcUsers
  • useTestClock

The nodes’ webservers will not be started. Instead, you should interact with each node via its shell over SSH (see the node configuration options). You have to enable the shell by adding the following line to each node’s node.conf file:

sshd { port = <NUMBER> }

Where <NUMBER> is the port you want to open to SSH into the shell.

To run the Dockerform task, follow these steps:

  1. Run ./gradlew deployNodes to generate the node files and folder structure.

  2. Open the build.gradle file and add a new dockerform task after the existing deployNodes task:

    task prepareDockerNodes(type: net.corda.plugins.Dockerform, dependsOn: ['jar']) {
        nodeDefaults {
            cordapp project(":contracts-java")
            }
        node {
            name "O=Notary,L=London,C=GB"
            notary = [validating : false]
            p2pPort 10002
            rpcSettings {
                address("localhost:10003")
                adminAddress("localhost:10023")
                }
            projectCordapp {
                deploy = false
                }
            cordapps.clear()
            }
        node {
            name "O=PartyA,L=London,C=GB"
            p2pPort 10002
            rpcSettings {
                address("localhost:10003")
                adminAddress("localhost:10023")
                }
            rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]]
            }
        node {
            name "O=PartyB,L=New York,C=US"
            p2pPort 10002
            rpcSettings {
                address("localhost:10003")
                adminAddress("localhost:10023")
                }
            rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]]
            }
    }
    
  3. Create an empty docker-compose.yml file using the following command on Mac or Linux:

    touch workflows-java/build/nodes/docker-compose.yml

For Windows, use the following command:

echo.> workflows-java\build\nodes\docker-compose.yml
  1. Run ./gradlew prepareDockerNodes and edit the generated docker-compose.yml file to change the ports:

    version: '3'
    services:
      notary:
        build: /Users/<USER>/Projects/json-cordapp/workflows-java/build/nodes/Notary
        ports:
          - "10002"
          - "10003"
      partya:
        build: /Users/<USER>/Projects/json-cordapp/workflows-java/build/nodes/PartyA
        ports:
          - "10002"
          - "10003"
      partyb:
        build: /Users/<USER>/Projects/json-cordapp/workflows-java/build/nodes/PartyB
        ports:
          - "10002"
          - "10003"
    

Running the Cordform/Dockerform tasks

To create the nodes defined in our deployNodes task, run the following command in a terminal window from the root of the project where the deployNodes task is defined:

  • Linux/macOS: ./gradlew deployNodes
  • Windows: gradlew.bat deployNodes

This will create the nodes in the build/nodes folder. There will be a node folder generated for each node defined in the deployNodes task, plus a runnodes shell script (or batch file on Windows) to run all the nodes at once for testing and development purposes. If you make any changes to your CorDapp source or deployNodes task, you will need to re-run the task to see the changes take effect.

If the task is a Dockerform task, running the task will also create an additional Dockerfile in each node directory in the build/nodes directory.