* Update identity docs (#2319) * Update certificate extension specification * Extend documentation on node name requirements * Moving node naming back to node generation file. Merging other permissioning information. * Update certificate structure image * Address some of the comments
11 KiB
Creating nodes locally
Node structure
Each Corda node has the following structure:
.
├── certificates // The node's certificates
├── corda-webserver.jar // The built-in node webserver
├── corda.jar // The core Corda libraries
├── logs // The node logs
├── node.conf // The node's configuration files
├── persistence.mv.db // The node's database
└── cordapps // The CorDapps jars installed on the node
The node is configured by editing its node.conf
file.
You install CorDapps on the node by dropping the CorDapp JARs into the
cordapps
folder.
In development mode (i.e. when devMode = true
, see corda-configuration-file
for
more information), the certificates
directory is filled
with pre-configured keystores if the required keystores do not exist.
This ensures that developers can get the nodes working as quickly as
possible. However, these pre-configured keystores are not secure. To
learn more see permissioning
.
Node naming
A node's name must be a valid X.500 distinguished name. In order to be compatible with other implementations (particularly TLS implementations), we constrain the allowed X.500 name attribute types to a subset of the minimum supported set for X.509 certificates (specified in RFC 3280), plus the locality attribute:
- Organization (O)
- State (ST)
- Locality (L)
- Country (C)
- Organizational-unit (OU)
- Common name (CN)
State
should be avoided unless required to differentiate
from other localities
with the same or similar names at the
country level. For example, London (GB) would not need a
state
, but St Ives would (there are two, one in Cornwall,
one in Cambridgeshire). As legal entities in Corda are likely to be
located in major cities, this attribute is not expected to be present in
the majority of names, but is an option for the cases which require
it.
The name must also obey the following constraints:
The
organisation
,locality
andcountry
attributes are present- The
state
,organisational-unit
andcommon name
attributes are optional
- The
The fields of the name have the following maximum character lengths:
- Common name: 64
- Organisation: 128
- Organisation unit: 64
- Locality: 64
- State: 64
The
country
attribute is a valid ISO 3166-1 two letter code in upper-caseAll attributes must obey the following constraints:
- Upper-case first letter
- Has at least two letters
- No leading or trailing whitespace
- Does not include the following characters:
,
,=
,$
,"
,'
,\
- Is in NFKC normalization form
- Does not contain the null character
- Only the latin, common and inherited unicode scripts are supported
The
organisation
field of the name also obeys the following constraints:No double-spacing
- This is to avoid right-to-left issues, debugging issues when we can't pronounce names over the phone, and character confusability attacks
External identifiers
Mappings to external identifiers such as company registration numbers, LEI, BIC, etc. should be stored in custom X.509 certificate extensions. These values may change for operational reasons, without the identity they're associated with necessarily changing, and their inclusion in the distinguished name would cause significant logistical complications. The OID and format for these extensions will be described in a further specification.
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 {
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 // Includes the corda-finance CorDapp on our node.
= ["net.corda:corda-finance:$corda_release_version"]
cordapps }
{
node "O=PartyA,L=London,C=GB"
name 10005
p2pPort {
rpcSettings 10006
port 10026
adminPort }
10007
webPort 10008
h2Port = ["net.corda:corda-finance:$corda_release_version"]
cordapps // 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 = ["net.corda:corda-finance:$corda_release_version"]
cordapps // Grants user1 the ability to start the MyFlow flow.
= [[ user: "user1", "password": "test", "permissions": ["StartFlow.net.corda.flows.MyFlow"]]]
rpcUsers }
}
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. 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/none-b.conf"
configFile }
}
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!
The Dockerform task
The `Dockerform
is a sister task
of Cordform
. It has nearly
the same syntax and produces very similar results - enhanced by an extra
file to enable easy spin up of nodes using
docker-compose
. Below you
can find the example task from the
IRS Demo<https://github.com/corda/corda/blob/release-V3.0/samples/irs-demo/cordapp/build.gradle#L111>
`
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"
]]
]
// (...)
prepareDockerNodes(type: net.corda.plugins.Dockerform, dependsOn: ['jar']) {
task
{
node "O=Notary Service,L=Zurich,C=CH"
name = [validating : true]
notary = ["${project(":finance").group}:finance:$corda_release_version"]
cordapps = rpcUsersList
rpcUsers true
useTestClock }
{
node "O=Bank A,L=London,C=GB"
name = ["${project(":finance").group}:finance:$corda_release_version"]
cordapps = rpcUsersList
rpcUsers true
useTestClock }
{
node "O=Bank B,L=New York,C=US"
name = ["${project(":finance").group}:finance:$corda_release_version"]
cordapps = rpcUsersList
rpcUsers true
useTestClock }
{
node "O=Regulator,L=Moscow,C=RU"
name = ["${project.group}:finance:$corda_release_version"]
cordapps = rpcUsersList
rpcUsers true
useTestClock }
}
There is no need to specify the ports, as every node is a separated
container, so no ports conflict will occur. Running the task will create
the same folders structure as described in The Cordform task
with an
additional `Dockerfile
in each node directory, and
`docker-compose.yml
in
build/nodes
` directory. Every node by default
exposes port 10003 which is the default one for RPC connections.
Warning
Webserver is not supported by this task!
Warning
Nodes are run without the local shell enabled!
Running deployNodes
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.
You can now run the nodes by following the instructions in Running a node <running-a-node>
.