Signed-off-by: Ed Prosser <edward.prosser@r3.com>
19 KiB
Creating nodes locally
Handcrafting a node
A node can be created manually by creating a folder that contains the following items:
The Corda Enterprise JAR
- The binary
corda-|corda_version|.jar
provided to your organisation.
- The binary
A node configuration file entitled
node.conf
, configured as percorda-configuration-file
A folder entitled
cordapps
containing any CorDapp JARs you want the node to loadAn up-to-date version of the
network-parameters
file ([see docs:](https://docs.corda.net/network-map.html#network-parameters)) generated by the bootstrapper toolOptional: A webserver JAR entitled
corda-webserver-|corda_version|.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-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"]]]
}
}
Ensure Corda Enterprise binaries are available on your machine as described in Getting Set Up <resolve-corda-enterprise-binaries>
.
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
andPartyB
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 <corda_configuration_file_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 <corda_configuration_file_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
. (seep2pAddress <corda_configuration_file_p2pAddress>
)e.g. :
p2pPort 10006 // "localhost:10006"
rpcSettings
<config>Specifies RPC settings for the node. (see
rpcSettings <corda_configuration_file_rpc_settings>
)e.g. :
rpcSettings { port 10006 adminPort 10026 }
Optional configuration
notary
<config> (seenotary <corda_configuration_file_notary>
)- Optional configuration which specifies the node is a notary.
Note
<required> for notary nodes
devMode
<boolean>When true enables development mode. (see
devMode <corda_configuration_file_dev_mode>
)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<specify-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<specify-custom-webserver>
)e.g. :
webPort 10011 // "localhost:10011"
rpcUsers
<list>Set the RPC users for this node. (see
rpcUsers <corda_configuration_file_rpc_users>
)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 <generating_a_node_extended_config>
)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 <corda_configuration_file_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 corda-configuration-file
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"
}
}
Additional properties can be also specified directly by the extraConfig
property which defines a map of keys and values. The example config above uses extraConfig
to set value of the jvmArgs
property. See the extended example of adding database configuration <testing_cordform_ref>
.
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 design/data-model-upgrades/package-namespace-ownership
configuration, 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 tofalse
, set totrue
to enable signingall
if set totrue
(by default) all CorDapps inside cordapp subdirectory will be signed, otherwise iffalse
then only the generated Cordapp will be signedoptions
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 pluginalias
the alias to sign under, the default value is cordaintermediatecastorepass
the keystore password, the default value is cordacadevpasskeypass
the private key password if it's different than the password for the keystore, the default value is cordacadevkeypassstoretype
the keystore type, the default value is JKSdname
the distinguished name for entity, the option is used whengenerateKeystore true
onlykeyalg
the method to use when generating name-value pair, the value defaults to RSA as Corda doesn't support DSA, the option is used whengenerateKeystore true
only
generateKeystore
the flag to generate a keystore, it is set tofalse
by default. If set totrue
then ad hock keystore is created and its key isused instead of the default Corda development key or any external key. The sameoptions
to specify an external keystore are used to define the newly created keystore. Additionallydname
andkeyalg
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 <corda-configuration-file>
). 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:
Run
./gradlew deployNodes
to generate the node files and folder structure.Open the
build.gradle
file and add a newdockerform
task after the existingdeployNodes
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"]]] } }
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
Run
./gradlew prepareDockerNodes
and edit the generateddocker-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.