15 KiB
Creating nodes locally
Handcrafting a node
A node can be created manually by creating a folder that contains the following items:
The Corda JAR
- Can be downloaded from https://r3.bintray.com/corda/net/corda/corda/ (under /VERSION_NUMBER/corda-VERSION_NUMBER.jar)
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 loadOptional: A webserver JAR entitled
corda-webserver.jar
that will connect to the node via RPC- The (deprecated) default webserver can be downloaded from http://r3.bintray.com/corda/net/corda/corda-webserver/ (under /VERSION_NUMBER/corda-VERSION_NUMBER.jar)
- 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:
deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
task "./build/nodes"
directory
{
nodeDefaults = [
cordapps "net.corda:corda-finance-contracts:$corda_release_version",
"net.corda:corda-finance-workflows:$corda_release_version",
"net.corda:corda-confidential-identities:$corda_release_version"
]
}
{
node "O=Notary,L=London,C=GB"
name // The notary will offer a validating notary service.
= [validating : true]
notary 10002
p2pPort {
rpcSettings 10003
port 10023
adminPort }
// No webport property, so no webserver will be created.
10004
h2Port // Starts an internal SSH server providing a management shell on the node.
2223
sshdPort = [
extraConfig // Setting the JMX reporter type.
: 'JOLOKIA',
jmxReporterType// Setting the H2 address.
: [ address: 'localhost:10030' ]
h2Settings]
}
{
node "O=PartyA,L=London,C=GB"
name 10005
p2pPort {
rpcSettings 10006
port 10026
adminPort }
10007
webPort 10008
h2Port // Grants user1 all RPC permissions.
= [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
rpcUsers }
{
node "O=PartyB,L=New York,C=US"
name 10009
p2pPort {
rpcSettings 10010
port 10030
adminPort }
10011
webPort 10012
h2Port // Grants user1 the ability to start the MyFlow flow.
= [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
rpcUsers }
}
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.
You can extend deployNodes
to generate additional
nodes.
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
:
deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
task [...]
{
node "O=PartyB,L=New York,C=US"
name [...]
// Grants user1 the ability to start the MyFlow flow.
= [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
rpcUsers = "samples/trader-demo/src/main/resources/node-b.conf"
configFile }
}
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`.
deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
task .drivers = ['lib/my_common_jar.jar']
ext[...]
{
node "O=PartyB,L=New York,C=US"
name [...]
= ext.drivers + ['lib/my_specific_jar.jar']
drivers }
}
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:
deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
task {
signing true
enabled true
generateKeystore false
all {
options "./build/nodes/jarSignKeystore.p12"
keystore "cordapp-signer"
alias "secret1!"
storepass "PKCS12"
storetype "OU=Dummy Cordapp Distributor, O=Corda, L=London, C=GB"
dname "RSA"
keyalg }
}
//...
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:
deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
task = [ "net.corda.finance.contracts.asset.Cash", "net.corda.finance.contracts.asset.CommercialPaper" ]
includeWhitelist //...
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 "O=PartyA,L=New York,C=US"
name 10005
webPort "lib/my_webserver.jar"
webserverJar }
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
There is no need to specify the nodes' ports, as every node has a
separate container, so no ports conflict will occur. Every node will
expose port 10003
for RPC connections.
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 = 2222 }
Where 2222
is the port you want to open to SSH into the
shell.
Below you can find the example task from the IRS Demo included in the samples directory of main Corda GitHub repository:
def rpcUsersList = [
['username' : "user",
'password' : "password",
'permissions' : [
"StartFlow.net.corda.irs.flows.AutoOfferFlow\$Requester",
"StartFlow.net.corda.irs.flows.UpdateBusinessDayFlow\$Broadcast",
"StartFlow.net.corda.irs.api.NodeInterestRates\$UploadFixesFlow",
"InvokeRpc.vaultQueryBy",
"InvokeRpc.networkMapSnapshot",
"InvokeRpc.currentNodeTime",
"InvokeRpc.wellKnownPartyFromX500Name"
]]
]
// (...)
deployNodes(type: net.corda.plugins.Dockerform, dependsOn: ['jar']) {
task
{
nodeDefaults = [
cordapps "net.corda:corda-finance-contracts:$corda_release_version",
"net.corda:corda-finance-workflows:$corda_release_version",
"net.corda:corda-confidential-identities:$corda_release_version"
]
}
{
node "O=Notary Service,L=Zurich,C=CH"
name = [validating : true]
notary = rpcUsersList
rpcUsers true
useTestClock }
{
node "O=Bank A,L=London,C=GB"
name = rpcUsersList
rpcUsers true
useTestClock }
{
node "O=Bank B,L=New York,C=US"
name = rpcUsersList
rpcUsers true
useTestClock }
{
node "O=Regulator,L=Moscow,C=RU"
name = rpcUsersList
rpcUsers true
useTestClock }
}
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, and a docker-compose.yml
file in the
build/nodes
directory.
You can now run the nodes by following the instructions in Running a node <running-a-node>
.