mirror of
https://github.com/corda/corda.git
synced 2025-06-01 07:00:54 +00:00
Merge pull request #1238 from corda/anthony-os-merge-20180710
O/S Merge 2018-07-10
This commit is contained in:
commit
d9923f8809
@ -0,0 +1,20 @@
|
|||||||
|
package net.corda.core.internal.errors
|
||||||
|
|
||||||
|
import net.corda.core.CordaRuntimeException
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
|
||||||
|
class AddressBindingException(val addresses: Set<NetworkHostAndPort>) : CordaRuntimeException(message(addresses)) {
|
||||||
|
|
||||||
|
constructor(address: NetworkHostAndPort) : this(setOf(address))
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private fun message(addresses: Set<NetworkHostAndPort>): String {
|
||||||
|
require(addresses.isNotEmpty())
|
||||||
|
return if (addresses.size > 1) {
|
||||||
|
"Failed to bind on an address in ${addresses.joinToString(", ", "[", "]")}."
|
||||||
|
} else {
|
||||||
|
"Failed to bind on address ${addresses.single()}."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ package net.corda.core.node.services
|
|||||||
import net.corda.core.CordaException
|
import net.corda.core.CordaException
|
||||||
import net.corda.core.DoNotImplement
|
import net.corda.core.DoNotImplement
|
||||||
import net.corda.core.contracts.PartyAndReference
|
import net.corda.core.contracts.PartyAndReference
|
||||||
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.identity.*
|
import net.corda.core.identity.*
|
||||||
import java.security.InvalidAlgorithmParameterException
|
import java.security.InvalidAlgorithmParameterException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -47,10 +48,17 @@ interface IdentityService {
|
|||||||
* Asserts that an anonymous party maps to the given full party, by looking up the certificate chain associated with
|
* Asserts that an anonymous party maps to the given full party, by looking up the certificate chain associated with
|
||||||
* the anonymous party and resolving it back to the given full party.
|
* the anonymous party and resolving it back to the given full party.
|
||||||
*
|
*
|
||||||
* @throws IllegalStateException if the anonymous party is not owned by the full party.
|
* @throws UnknownAnonymousPartyException if the anonymous party is not owned by the full party.
|
||||||
*/
|
*/
|
||||||
@Throws(IllegalStateException::class)
|
@Throws(UnknownAnonymousPartyException::class)
|
||||||
fun assertOwnership(party: Party, anonymousParty: AnonymousParty)
|
fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
||||||
|
val anonymousIdentity = certificateFromKey(anonymousParty.owningKey)
|
||||||
|
?: throw UnknownAnonymousPartyException("Unknown $anonymousParty")
|
||||||
|
val issuingCert = anonymousIdentity.certPath.certificates[1]
|
||||||
|
require(issuingCert.publicKey == party.owningKey) {
|
||||||
|
"Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all identities known to the service. This is expensive, and [partyFromKey] or [partyFromX500Name] should be
|
* Get all identities known to the service. This is expensive, and [partyFromKey] or [partyFromX500Name] should be
|
||||||
@ -73,7 +81,7 @@ interface IdentityService {
|
|||||||
* @param key The owning [PublicKey] of the [Party].
|
* @param key The owning [PublicKey] of the [Party].
|
||||||
* @return Returns a [Party] with a matching owningKey if known, else returns null.
|
* @return Returns a [Party] with a matching owningKey if known, else returns null.
|
||||||
*/
|
*/
|
||||||
fun partyFromKey(key: PublicKey): Party?
|
fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a party name to the well known identity [Party] instance for this name. Where possible well known identity
|
* Resolves a party name to the well known identity [Party] instance for this name. Where possible well known identity
|
||||||
@ -92,7 +100,21 @@ interface IdentityService {
|
|||||||
* @param party identity to determine well known identity for.
|
* @param party identity to determine well known identity for.
|
||||||
* @return well known identity, if found.
|
* @return well known identity, if found.
|
||||||
*/
|
*/
|
||||||
fun wellKnownPartyFromAnonymous(party: AbstractParty): Party?
|
fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
||||||
|
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
||||||
|
// however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
|
||||||
|
// into a party, and from there figure out the well known party.
|
||||||
|
val candidate = partyFromKey(party.owningKey)
|
||||||
|
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
||||||
|
return if (candidate != null) {
|
||||||
|
require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) {
|
||||||
|
"Candidate party $candidate does not match expected $party"
|
||||||
|
}
|
||||||
|
wellKnownPartyFromX500Name(candidate.name)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a (optionally) confidential identity to the corresponding well known identity [Party].
|
* Resolves a (optionally) confidential identity to the corresponding well known identity [Party].
|
||||||
@ -103,7 +125,7 @@ interface IdentityService {
|
|||||||
* @param partyRef identity (and reference, which is unused) to determine well known identity for.
|
* @param partyRef identity (and reference, which is unused) to determine well known identity for.
|
||||||
* @return the well known identity, or null if unknown.
|
* @return the well known identity, or null if unknown.
|
||||||
*/
|
*/
|
||||||
fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference) = wellKnownPartyFromAnonymous(partyRef.party)
|
fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference): Party? = wellKnownPartyFromAnonymous(partyRef.party)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the well known identity of a party. Throws an exception if the party cannot be identified.
|
* Resolve the well known identity of a party. Throws an exception if the party cannot be identified.
|
||||||
@ -112,7 +134,10 @@ interface IdentityService {
|
|||||||
* @return the well known identity.
|
* @return the well known identity.
|
||||||
* @throws IllegalArgumentException
|
* @throws IllegalArgumentException
|
||||||
*/
|
*/
|
||||||
fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party
|
fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party {
|
||||||
|
return wellKnownPartyFromAnonymous(party)
|
||||||
|
?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of candidate matches for a given string, with optional fuzzy(ish) matching. Fuzzy matching may
|
* Returns a list of candidate matches for a given string, with optional fuzzy(ish) matching. Fuzzy matching may
|
||||||
|
@ -12,6 +12,9 @@ Unreleased
|
|||||||
and within that file the ``bridgeMode`` propety has been modified to ``firewallMode`` for overall consistency.
|
and within that file the ``bridgeMode`` propety has been modified to ``firewallMode`` for overall consistency.
|
||||||
This will be a breaking change for early adopters and their deployments, but hopefully will be more future proof.
|
This will be a breaking change for early adopters and their deployments, but hopefully will be more future proof.
|
||||||
|
|
||||||
|
* Docs for IdentityService. assertOwnership updated to correctly state that an UnknownAnonymousPartyException is thrown
|
||||||
|
rather than IllegalStateException.
|
||||||
|
|
||||||
* The Corda JPA entities no longer implement java.io.Serializable, as this was causing persistence errors in obscure cases.
|
* The Corda JPA entities no longer implement java.io.Serializable, as this was causing persistence errors in obscure cases.
|
||||||
Java serialization is disabled globally in the node, but in the unlikely event you were relying on these types being Java serializable please contact us.
|
Java serialization is disabled globally in the node, but in the unlikely event you were relying on these types being Java serializable please contact us.
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ absolute path to the node's base directory.
|
|||||||
:validating: Boolean to determine whether the notary is a validating or non-validating one.
|
:validating: Boolean to determine whether the notary is a validating or non-validating one.
|
||||||
|
|
||||||
:serviceLegalName: If the node is part of a distributed cluster, specify the legal name of the cluster. At runtime, Corda
|
:serviceLegalName: If the node is part of a distributed cluster, specify the legal name of the cluster. At runtime, Corda
|
||||||
checks whether this name matches the name of the certificate of the notary cluster.
|
checks whether this name matches the name of the certificate of the notary cluster.
|
||||||
|
|
||||||
:raft: If part of a distributed Raft cluster specify this config object, with the following settings:
|
:raft: If part of a distributed Raft cluster specify this config object, with the following settings:
|
||||||
|
|
||||||
|
@ -24,18 +24,13 @@ You can find out more about network maps and network parameters from :doc:`netwo
|
|||||||
Bootstrapping a test network
|
Bootstrapping a test network
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The bootstrapper is distributed as part of |release| in the form of runnable JAR file "|jar_name|".
|
The bootstrapper can be downloaded from https://downloads.corda.net/network-bootstrapper-VERSION.jar, where ``VERSION``
|
||||||
|
is the Corda version.
|
||||||
.. |jar_name| replace:: corda-tools-network-bootstrapper-|version|.jar
|
|
||||||
|
|
||||||
Create a directory containing a node config file, ending in "_node.conf", for each node you want to create. Then run the
|
Create a directory containing a node config file, ending in "_node.conf", for each node you want to create. Then run the
|
||||||
following command:
|
following command:
|
||||||
|
|
||||||
.. parsed-literal::
|
``java -jar network-bootstrapper-VERSION.jar --dir <nodes-root-dir>``
|
||||||
|
|
||||||
> java -jar |jar_name| --dir <nodes-root-dir>
|
|
||||||
|
|
||||||
..
|
|
||||||
|
|
||||||
For example running the command on a directory containing these files:
|
For example running the command on a directory containing these files:
|
||||||
|
|
||||||
@ -50,9 +45,7 @@ will generate directories containing three nodes: ``notary``, ``partya`` and ``p
|
|||||||
that comes with the bootstrapper. If a different version of Corda is required then simply place that ``corda.jar`` file
|
that comes with the bootstrapper. If a different version of Corda is required then simply place that ``corda.jar`` file
|
||||||
alongside the configuration files in the directory.
|
alongside the configuration files in the directory.
|
||||||
|
|
||||||
The directory can also contain CorDapp JARs which will be copied to each node's ``cordapps`` directory.
|
You can also have the node directories containing their "node.conf" files already laid out. The previous example would be:
|
||||||
|
|
||||||
You can also have the node directories containing their ``node.conf`` files already laid out. The previous example would be:
|
|
||||||
|
|
||||||
.. sourcecode:: none
|
.. sourcecode:: none
|
||||||
|
|
||||||
@ -66,33 +59,34 @@ You can also have the node directories containing their ``node.conf`` files alre
|
|||||||
|
|
||||||
Similarly, each node directory may contain its own ``corda.jar``, which the bootstrapper will use instead.
|
Similarly, each node directory may contain its own ``corda.jar``, which the bootstrapper will use instead.
|
||||||
|
|
||||||
Synchronisation
|
Providing CorDapps to the Network Bootstrapper
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This tool only bootstraps a network. It cannot dynamically update if a new node needs to join the network or if an existing
|
If you would like the Network Bootstrapper to include your CorDapps in each generated node, just place them in the directory
|
||||||
one has changed something in their node-info, e.g. their P2P address. For this the new node-info file will need to be placed
|
alongside the config files. For example, if your directory has this structure:
|
||||||
in the other nodes' ``additional-node-infos`` directory. A simple way to do this is to use `rsync <https://en.wikipedia.org/wiki/Rsync>`_.
|
|
||||||
However, if it's known beforehand the set of nodes that will eventually form part of the network then all the node directories
|
|
||||||
can be pre-generated in the bootstrap and only started when needed.
|
|
||||||
|
|
||||||
Running the bootstrapper again on the same network will allow a new node to be added or an existing one to have its updated
|
.. sourcecode:: none
|
||||||
node-info re-distributed. However, this comes at the expense of having to temporarily collect the node directories back
|
|
||||||
together again under a common parent directory.
|
.
|
||||||
|
├── notary_node.conf // The notary's node.conf file
|
||||||
|
├── partya_node.conf // Party A's node.conf file
|
||||||
|
├── partyb_node.conf // Party B's node.conf file
|
||||||
|
├── cordapp-a.jar // A cordapp to be installed on all nodes
|
||||||
|
└── cordapp-b.jar // Another cordapp to be installed on all nodes
|
||||||
|
|
||||||
|
The ``cordapp-a.jar`` and ``cordapp-b.jar`` will be installed in each node directory, and any contracts within them will be
|
||||||
|
added to the Contract Whitelist (see below).
|
||||||
|
|
||||||
Whitelisting contracts
|
Whitelisting contracts
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
----------------------
|
||||||
|
|
||||||
The CorDapp JARs are also automatically used to create the *Zone whitelist* (see :doc:`api-contract-constraints`) for
|
Any CorDapps provided when bootstrapping a network will be scanned for contracts which will be used to create the
|
||||||
the network.
|
*Zone whitelist* (see :doc:`api-contract-constraints`) for the network.
|
||||||
|
|
||||||
.. note:: If you only wish to whitelist the CorDapps but not copy them to each node then run with the ``--no-copy`` flag.
|
.. note:: If you only wish to whitelist the CorDapps but not copy them to each node then run with the ``--no-copy`` flag.
|
||||||
|
|
||||||
The CorDapp JARs will be hashed and scanned for ``Contract`` classes. These contract class implementations will become part
|
The CorDapp JARs will be hashed and scanned for ``Contract`` classes. These contract class implementations will become part
|
||||||
of the whitelisted contracts in the network parameters (see ``NetworkParameters.whitelistedContractImplementations`` :doc:`network-map`).
|
of the whitelisted contracts in the network parameters (see ``NetworkParameters.whitelistedContractImplementations`` :doc:`network-map`).
|
||||||
If the network already has a set of network parameters defined (i.e. the node directories all contain the same network-parameters
|
|
||||||
file) then the new set of contracts will be appended to the current whitelist.
|
|
||||||
|
|
||||||
.. note:: The whitelist can only ever be appended to. Once added a contract implementation can never be removed.
|
|
||||||
|
|
||||||
By default the bootstrapper will whitelist all the contracts found in all the CorDapp JARs. To prevent certain
|
By default the bootstrapper will whitelist all the contracts found in all the CorDapp JARs. To prevent certain
|
||||||
contracts from being whitelisted, add their fully qualified class name in the ``exclude_whitelist.txt``. These will instead
|
contracts from being whitelisted, add their fully qualified class name in the ``exclude_whitelist.txt``. These will instead
|
||||||
@ -104,3 +98,151 @@ For example:
|
|||||||
|
|
||||||
net.corda.finance.contracts.asset.Cash
|
net.corda.finance.contracts.asset.Cash
|
||||||
net.corda.finance.contracts.asset.CommercialPaper
|
net.corda.finance.contracts.asset.CommercialPaper
|
||||||
|
|
||||||
|
Modifying a bootstrapped network
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The network bootstrapper is provided as a development tool for setting up Corda networks for development and testing.
|
||||||
|
There is some limited functionality which can be used to make changes to a network, but for anything more complicated consider
|
||||||
|
using a :doc:`network-map` server.
|
||||||
|
|
||||||
|
When running the Network Bootstrapper, each ``node-info`` file needs to be gathered together in one directory. If
|
||||||
|
the nodes are being run on different machines you need to do the following:
|
||||||
|
|
||||||
|
* Copy the node directories from each machine into one directory, on one machine
|
||||||
|
* Depending on the modification being made (see below for more information), add any new files required to the root directory
|
||||||
|
* Run the Network Bootstrapper from the root directory
|
||||||
|
* Copy each individual node's directory back to the original machine
|
||||||
|
|
||||||
|
The network bootstrapper cannot dynamically update the network if an existing node has changed something in their node-info,
|
||||||
|
e.g. their P2P address. For this the new node-info file will need to be placed in the other nodes' ``additional-node-infos`` directory.
|
||||||
|
If the nodes are located on different machines, then a utility such as `rsync <https://en.wikipedia.org/wiki/Rsync>`_ can be used
|
||||||
|
so that the nodes can share node-infos.
|
||||||
|
|
||||||
|
Adding a new node to the network
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Running the bootstrapper again on the same network will allow a new node to be added and its
|
||||||
|
node-info distributed to the existing nodes.
|
||||||
|
|
||||||
|
As an example, if we have an existing bootstrapped network, with a Notary and PartyA and we want to add a PartyB, we
|
||||||
|
can use the network bootstrapper on the following network structure:
|
||||||
|
|
||||||
|
.. sourcecode:: none
|
||||||
|
|
||||||
|
.
|
||||||
|
├── notary // existing node directories
|
||||||
|
│ ├── node.conf
|
||||||
|
│ ├── network-parameters
|
||||||
|
│ ├── node-info-notary
|
||||||
|
│ └── additional-node-infos
|
||||||
|
│ ├── node-info-notary
|
||||||
|
│ └── node-info-partya
|
||||||
|
├── partya
|
||||||
|
│ ├── node.conf
|
||||||
|
│ ├── network-parameters
|
||||||
|
│ ├── node-info-partya
|
||||||
|
│ └── additional-node-infos
|
||||||
|
│ ├── node-info-notary
|
||||||
|
│ └── node-info-partya
|
||||||
|
└── partyb_node.conf // the node.conf for the node to be added
|
||||||
|
|
||||||
|
Then run the network bootstrapper again from the root dir:
|
||||||
|
|
||||||
|
``java -jar network-bootstrapper-VERSION.jar --dir <nodes-root-dir>``
|
||||||
|
|
||||||
|
Which will give the following:
|
||||||
|
|
||||||
|
.. sourcecode:: none
|
||||||
|
|
||||||
|
.
|
||||||
|
├── notary // the contents of the existing nodes (keys, db's etc...) are unchanged
|
||||||
|
│ ├── node.conf
|
||||||
|
│ ├── network-parameters
|
||||||
|
│ ├── node-info-notary
|
||||||
|
│ └── additional-node-infos
|
||||||
|
│ ├── node-info-notary
|
||||||
|
│ ├── node-info-partya
|
||||||
|
│ └── node-info-partyb
|
||||||
|
├── partya
|
||||||
|
│ ├── node.conf
|
||||||
|
│ ├── network-parameters
|
||||||
|
│ ├── node-info-partya
|
||||||
|
│ └── additional-node-infos
|
||||||
|
│ ├── node-info-notary
|
||||||
|
│ ├── node-info-partya
|
||||||
|
│ └── node-info-partyb
|
||||||
|
└── partyb // a new node directory is created for PartyB
|
||||||
|
├── node.conf
|
||||||
|
├── network-parameters
|
||||||
|
├── node-info-partyb
|
||||||
|
└── additional-node-infos
|
||||||
|
├── node-info-notary
|
||||||
|
├── node-info-partya
|
||||||
|
└── node-info-partyb
|
||||||
|
|
||||||
|
The bootstrapper will generate a directory and the ``node-info`` file for PartyB, and will also make sure a copy of each
|
||||||
|
nodes' ``node-info`` file is in the ``additional-node-info`` directory of every node. Any other files in the existing nodes,
|
||||||
|
such a generated keys, will be unaffected.
|
||||||
|
|
||||||
|
.. note:: The bootstrapper is provided for test deployments and can only generate information for nodes collected on
|
||||||
|
the same machine. If a network needs to be updated using the bootstrapper once deployed, the nodes will need
|
||||||
|
collecting back together.
|
||||||
|
|
||||||
|
Updating the contract whitelist for bootstrapped networks
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
If the network already has a set of network parameters defined (i.e. the node directories all contain the same network-parameters
|
||||||
|
file) then the bootstrapper can be used to append contracts from new CorDapps to the current whitelist.
|
||||||
|
For example, with the following pre-generated network:
|
||||||
|
|
||||||
|
.. sourcecode:: none
|
||||||
|
|
||||||
|
.
|
||||||
|
├── notary
|
||||||
|
│ ├── node.conf
|
||||||
|
│ ├── network-parameters
|
||||||
|
│ └── cordapps
|
||||||
|
│ └── cordapp-a.jar
|
||||||
|
├── partya
|
||||||
|
│ ├── node.conf
|
||||||
|
│ ├── network-parameters
|
||||||
|
│ └── cordapps
|
||||||
|
│ └── cordapp-a.jar
|
||||||
|
├── partyb
|
||||||
|
│ ├── node.conf
|
||||||
|
│ ├── network-parameters
|
||||||
|
│ └── cordapps
|
||||||
|
│ └── cordapp-a.jar
|
||||||
|
└── cordapp-b.jar // The new cordapp to add to the existing nodes
|
||||||
|
|
||||||
|
Then run the network bootstrapper again from the root dir:
|
||||||
|
|
||||||
|
``java -jar network-bootstrapper-VERSION.jar --dir <nodes-root-dir>``
|
||||||
|
|
||||||
|
To give the following:
|
||||||
|
|
||||||
|
.. sourcecode:: none
|
||||||
|
|
||||||
|
.
|
||||||
|
├── notary
|
||||||
|
│ ├── node.conf
|
||||||
|
│ ├── network-parameters // The contracts from cordapp-b are appended to the whitelist in network-parameters
|
||||||
|
│ └── cordapps
|
||||||
|
│ ├── cordapp-a.jar
|
||||||
|
│ └── cordapp-b.jar // The updated cordapp is placed in the nodes cordapp directory
|
||||||
|
├── partya
|
||||||
|
│ ├── node.conf
|
||||||
|
│ ├── network-parameters // The contracts from cordapp-b are appended to the whitelist in network-parameters
|
||||||
|
│ └── cordapps
|
||||||
|
│ ├── cordapp-a.jar
|
||||||
|
│ └── cordapp-b.jar // The updated cordapp is placed in the nodes cordapp directory
|
||||||
|
└── partyb
|
||||||
|
├── node.conf
|
||||||
|
├── network-parameters // The contracts from cordapp-b are appended to the whitelist in network-parameters
|
||||||
|
└── cordapps
|
||||||
|
├── cordapp-a.jar
|
||||||
|
└── cordapp-b.jar // The updated cordapp is placed in the nodes cordapp directory
|
||||||
|
|
||||||
|
.. note:: The whitelist can only ever be appended to. Once added a contract implementation can never be removed.
|
||||||
|
|
||||||
|
@ -10,7 +10,107 @@ Quickstart
|
|||||||
getting-set-up.rst
|
getting-set-up.rst
|
||||||
tutorial-cordapp.rst
|
tutorial-cordapp.rst
|
||||||
|
|
||||||
* :doc:`Set up your machine for CorDapp development <getting-set-up>`
|
Welcome to the Corda Quickstart Guide. Follow the links below to help get going quickly with Corda.
|
||||||
* :doc:`Run the Example CorDapp <tutorial-cordapp>`
|
|
||||||
* `View CorDapps in Corda Explore <http://explore.corda.zone/>`_
|
I want to:
|
||||||
* `Download sample CorDapps <https://www.corda.net/samples/>`_
|
|
||||||
|
* :ref:`Learn <quickstart-learn>` about Corda for the first time
|
||||||
|
* :ref:`Develop <quickstart-develop>` a CorDapp
|
||||||
|
* :ref:`Run <quickstart-run>` and test a CorDapp on a local Corda network
|
||||||
|
* :ref:`Add <quickstart-add>` a node to an existing test Corda network
|
||||||
|
* :ref:`Add <quickstart-production>` a node to an existing production network
|
||||||
|
|
||||||
|
.. _quickstart-learn:
|
||||||
|
|
||||||
|
Learn about Corda for the first time
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| Useful links | Description |
|
||||||
|
+============================================+=========================================================================================================+
|
||||||
|
| :doc:`key-concepts` | The key concepts and features of the Corda Platform |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`getting-set-up` | Set up your machine for running and developing CorDapps |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`tutorial-cordapp` | A guide to running a simple CorDapp |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
.. _quickstart-develop:
|
||||||
|
|
||||||
|
Develop a CorDapp
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| Useful links | Description |
|
||||||
|
+============================================+=========================================================================================================+
|
||||||
|
| :doc:`hello-world-introduction` | A coding walk-through of a basic CorDapp |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`cordapp-overview` | An introduction to CordApps |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`writing-a-cordapp` | How to structure a CorDapp project |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`cordapp-build-systems` | How to build a CorDapp |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`corda-api` | A guide to the CorDapp API |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
.. _quickstart-run:
|
||||||
|
|
||||||
|
Run and test a CorDapp on local Corda network
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| Useful links | Description |
|
||||||
|
+============================================+=========================================================================================================+
|
||||||
|
| :doc:`generating-a-node` | Guidance on creating Corda nodes for development and testing locally and on Docker |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`node-structure` | The Corda node folder structure and how to name your node |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`running-a-node` | Guidance on running Corda nodes locally and on Docker |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`setting-up-a-corda-network` | Considerations for setting up a Corda network |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`node-administration` | How to monitor a Corda node using an RPC interface |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
.. _quickstart-add:
|
||||||
|
|
||||||
|
Add a node to an existing test Corda network
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| Useful links | Description |
|
||||||
|
+============================================+=========================================================================================================+
|
||||||
|
| :doc:`node-structure` | The Corda node folder structure and how to name your node |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`deploying-a-node` | A step-by-step guide on deploying a Corda node to your own server |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`azure-vm` | A step-by-step guide on creating a Corda Network on Azure |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`aws-vm` | A step-by-step guide on creating a Corda Network on AWS |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`node-administration` | How to monitor a Corda node using an RPC interface |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| :doc:`blob-inspector` | A troubleshooting tool allowing you to read the contents of a binary blob file |
|
||||||
|
+--------------------------------------------+---------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
.. _quickstart-production:
|
||||||
|
|
||||||
|
Add a node to an existing production network
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
+---------------------------------------------------------------------------------------------------------+
|
||||||
|
| Contact R3 Solutions Engineering at support@r3.com |
|
||||||
|
+---------------------------------------------------------------------------------------------------------+
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package net.corda.node
|
||||||
|
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.core.internal.errors.AddressBindingException
|
||||||
|
import net.corda.testing.driver.DriverParameters
|
||||||
|
import net.corda.testing.driver.PortAllocation
|
||||||
|
import net.corda.testing.driver.driver
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
|
import org.junit.Test
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.net.ServerSocket
|
||||||
|
|
||||||
|
class AddressBindingFailureTests {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val portAllocation = PortAllocation.Incremental(20_000)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `p2p address`() = assertBindExceptionForOverrides { address -> mapOf("p2pAddress" to address.toString()) }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `rpc address`() = assertBindExceptionForOverrides { address -> mapOf("rpcSettings" to mapOf("address" to address.toString())) }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `rpc admin address`() = assertBindExceptionForOverrides { address -> mapOf("rpcSettings" to mapOf("adminAddress" to address.toString())) }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `H2 address`() = assertBindExceptionForOverrides { address -> mapOf("h2Settings" to mapOf("address" to address.toString())) }
|
||||||
|
|
||||||
|
private fun assertBindExceptionForOverrides(overrides: (NetworkHostAndPort) -> Map<String, Any?>) {
|
||||||
|
|
||||||
|
ServerSocket(0).use { socket ->
|
||||||
|
|
||||||
|
val address = InetSocketAddress(socket.localPort).toNetworkHostAndPort()
|
||||||
|
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList(), inMemoryDB = false, portAllocation = portAllocation)) {
|
||||||
|
|
||||||
|
assertThatThrownBy { startNode(customOverrides = overrides(address)).getOrThrow() }.isInstanceOfSatisfying(AddressBindingException::class.java) { exception ->
|
||||||
|
assertThat(exception.addresses).contains(address).withFailMessage("Expected addresses to contain $address but was ${exception.addresses}.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun InetSocketAddress.toNetworkHostAndPort() = NetworkHostAndPort(hostName, port)
|
||||||
|
}
|
@ -73,7 +73,7 @@ class BootTests : IntegrationTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `double node start doesn't write into log file`() {
|
fun `double node start doesn't write into log file`() {
|
||||||
driver {
|
driver(DriverParameters(notarySpecs = emptyList())) {
|
||||||
val alice = startNode(providedName = ALICE_NAME).get()
|
val alice = startNode(providedName = ALICE_NAME).get()
|
||||||
val logFolder = alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
|
val logFolder = alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
|
||||||
val logFile = logFolder.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() }
|
val logFile = logFolder.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() }
|
||||||
|
@ -21,7 +21,16 @@ import net.corda.core.concurrent.CordaFuture
|
|||||||
import net.corda.core.context.InvocationContext
|
import net.corda.core.context.InvocationContext
|
||||||
import net.corda.core.crypto.newSecureRandom
|
import net.corda.core.crypto.newSecureRandom
|
||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.flows.*
|
import net.corda.core.flows.ContractUpgradeFlow
|
||||||
|
import net.corda.core.flows.FinalityFlow
|
||||||
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.flows.FlowLogicRefFactory
|
||||||
|
import net.corda.core.flows.FlowSession
|
||||||
|
import net.corda.core.flows.InitiatedBy
|
||||||
|
import net.corda.core.flows.InitiatingFlow
|
||||||
|
import net.corda.core.flows.NotaryChangeFlow
|
||||||
|
import net.corda.core.flows.NotaryFlow
|
||||||
|
import net.corda.core.flows.StartableByService
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -33,14 +42,31 @@ import net.corda.core.internal.concurrent.map
|
|||||||
import net.corda.core.internal.concurrent.openFuture
|
import net.corda.core.internal.concurrent.openFuture
|
||||||
import net.corda.core.internal.notary.NotaryService
|
import net.corda.core.internal.notary.NotaryService
|
||||||
import net.corda.core.internal.uncheckedCast
|
import net.corda.core.internal.uncheckedCast
|
||||||
import net.corda.core.messaging.*
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.node.*
|
import net.corda.core.messaging.FlowHandle
|
||||||
import net.corda.core.node.services.*
|
import net.corda.core.messaging.FlowHandleImpl
|
||||||
|
import net.corda.core.messaging.FlowProgressHandle
|
||||||
|
import net.corda.core.messaging.FlowProgressHandleImpl
|
||||||
|
import net.corda.core.messaging.RPCOps
|
||||||
|
import net.corda.core.node.AppServiceHub
|
||||||
|
import net.corda.core.node.NetworkParameters
|
||||||
|
import net.corda.core.node.NodeInfo
|
||||||
|
import net.corda.core.node.ServiceHub
|
||||||
|
import net.corda.core.node.ServicesForResolution
|
||||||
|
import net.corda.core.node.services.AttachmentStorage
|
||||||
|
import net.corda.core.node.services.CordaService
|
||||||
|
import net.corda.core.node.services.IdentityService
|
||||||
|
import net.corda.core.node.services.KeyManagementService
|
||||||
|
import net.corda.core.node.services.TransactionVerifierService
|
||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
import net.corda.core.serialization.SerializeAsToken
|
import net.corda.core.serialization.SerializeAsToken
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.core.utilities.days
|
||||||
|
import net.corda.core.utilities.debug
|
||||||
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.core.utilities.minutes
|
||||||
import net.corda.node.CordaClock
|
import net.corda.node.CordaClock
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.internal.CheckpointVerifier.verifyCheckpointsCompatible
|
import net.corda.node.internal.CheckpointVerifier.verifyCheckpointsCompatible
|
||||||
@ -56,9 +82,26 @@ import net.corda.node.internal.security.RPCSecurityManager
|
|||||||
import net.corda.node.services.ContractUpgradeHandler
|
import net.corda.node.services.ContractUpgradeHandler
|
||||||
import net.corda.node.services.FinalityHandler
|
import net.corda.node.services.FinalityHandler
|
||||||
import net.corda.node.services.NotaryChangeHandler
|
import net.corda.node.services.NotaryChangeHandler
|
||||||
import net.corda.node.services.api.*
|
import net.corda.node.services.api.CheckpointStorage
|
||||||
import net.corda.node.services.config.*
|
import net.corda.node.services.api.DummyAuditService
|
||||||
|
import net.corda.node.services.api.FlowStarter
|
||||||
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
|
import net.corda.node.services.api.MonitoringService
|
||||||
|
import net.corda.node.services.api.NetworkMapCacheBaseInternal
|
||||||
|
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||||
|
import net.corda.node.services.api.NodePropertiesStore
|
||||||
|
import net.corda.node.services.api.SchedulerService
|
||||||
|
import net.corda.node.services.api.SchemaService
|
||||||
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
|
import net.corda.node.services.api.StartedNodeServices
|
||||||
|
import net.corda.node.services.api.VaultServiceInternal
|
||||||
|
import net.corda.node.services.api.WritableTransactionStorage
|
||||||
|
import net.corda.node.services.config.BFTSMaRtConfiguration
|
||||||
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
|
import net.corda.node.services.config.NotaryConfig
|
||||||
|
import net.corda.node.services.config.configureWithDevSSLCertificate
|
||||||
import net.corda.node.services.config.shell.toShellConfig
|
import net.corda.node.services.config.shell.toShellConfig
|
||||||
|
import net.corda.node.services.config.shouldInitCrashShell
|
||||||
import net.corda.node.services.events.NodeSchedulerService
|
import net.corda.node.services.events.NodeSchedulerService
|
||||||
import net.corda.node.services.events.ScheduledActivityObserver
|
import net.corda.node.services.events.ScheduledActivityObserver
|
||||||
import net.corda.node.services.identity.PersistentIdentityService
|
import net.corda.node.services.identity.PersistentIdentityService
|
||||||
@ -78,14 +121,14 @@ import net.corda.node.services.persistence.DBTransactionStorage
|
|||||||
import net.corda.node.services.persistence.NodeAttachmentService
|
import net.corda.node.services.persistence.NodeAttachmentService
|
||||||
import net.corda.node.services.persistence.NodePropertiesPersistentStore
|
import net.corda.node.services.persistence.NodePropertiesPersistentStore
|
||||||
import net.corda.node.services.persistence.RunOnceService
|
import net.corda.node.services.persistence.RunOnceService
|
||||||
import net.corda.node.services.network.*
|
|
||||||
import net.corda.node.services.persistence.*
|
|
||||||
import net.corda.node.services.schema.HibernateObserver
|
import net.corda.node.services.schema.HibernateObserver
|
||||||
import net.corda.node.services.schema.NodeSchemaService
|
import net.corda.node.services.schema.NodeSchemaService
|
||||||
import net.corda.node.services.statemachine.ExternalEvent
|
import net.corda.node.services.statemachine.ExternalEvent
|
||||||
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
|
||||||
import net.corda.node.services.statemachine.SingleThreadedStateMachineManager
|
import net.corda.node.services.statemachine.SingleThreadedStateMachineManager
|
||||||
import net.corda.node.services.statemachine.StateMachineManager
|
import net.corda.node.services.statemachine.StateMachineManager
|
||||||
|
import net.corda.node.services.statemachine.FlowMonitor
|
||||||
|
import net.corda.node.services.statemachine.StateMachineManagerInternal
|
||||||
import net.corda.node.services.statemachine.appName
|
import net.corda.node.services.statemachine.appName
|
||||||
import net.corda.node.services.statemachine.flowVersionAndInitiatingClass
|
import net.corda.node.services.statemachine.flowVersionAndInitiatingClass
|
||||||
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
import net.corda.node.services.transactions.BFTNonValidatingNotaryService
|
||||||
@ -97,8 +140,6 @@ import net.corda.node.services.transactions.RaftUniquenessProvider
|
|||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
import net.corda.node.services.transactions.SimpleNotaryService
|
||||||
import net.corda.node.services.transactions.ValidatingNotaryService
|
import net.corda.node.services.transactions.ValidatingNotaryService
|
||||||
import net.corda.node.services.statemachine.*
|
|
||||||
import net.corda.node.services.transactions.*
|
|
||||||
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
|
||||||
import net.corda.node.services.vault.NodeVaultService
|
import net.corda.node.services.vault.NodeVaultService
|
||||||
import net.corda.node.utilities.AffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
@ -140,7 +181,6 @@ import java.util.concurrent.ConcurrentHashMap
|
|||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.concurrent.TimeUnit.SECONDS
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@ -431,7 +471,11 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
open fun startShell() {
|
open fun startShell() {
|
||||||
if (configuration.shouldInitCrashShell()) {
|
if (configuration.shouldInitCrashShell()) {
|
||||||
InteractiveShell.startShellInternal(configuration.toShellConfig(), cordappLoader.appClassLoader)
|
val shellConfiguration = configuration.toShellConfig()
|
||||||
|
shellConfiguration.sshHostKeyDirectory?.let {
|
||||||
|
log.info("Binding Shell SSHD server on port $it.")
|
||||||
|
}
|
||||||
|
InteractiveShell.startShellInternal(shellConfiguration, cordappLoader.appClassLoader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ import net.corda.node.VersionInfo
|
|||||||
import net.corda.node.internal.artemis.ArtemisBroker
|
import net.corda.node.internal.artemis.ArtemisBroker
|
||||||
import net.corda.node.internal.artemis.BrokerAddresses
|
import net.corda.node.internal.artemis.BrokerAddresses
|
||||||
import net.corda.node.internal.cordapp.CordappLoader
|
import net.corda.node.internal.cordapp.CordappLoader
|
||||||
|
import net.corda.core.internal.errors.AddressBindingException
|
||||||
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
import net.corda.node.internal.security.RPCSecurityManagerImpl
|
||||||
import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser
|
import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser
|
||||||
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
|
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
|
||||||
@ -46,7 +47,6 @@ import net.corda.node.services.api.NodePropertiesStore
|
|||||||
import net.corda.node.services.api.SchemaService
|
import net.corda.node.services.api.SchemaService
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.config.SecurityConfiguration
|
import net.corda.node.services.config.SecurityConfiguration
|
||||||
import net.corda.node.services.config.VerifierType
|
|
||||||
import net.corda.node.services.config.shouldInitCrashShell
|
import net.corda.node.services.config.shouldInitCrashShell
|
||||||
import net.corda.node.services.config.shouldStartLocalShell
|
import net.corda.node.services.config.shouldStartLocalShell
|
||||||
import net.corda.node.services.messaging.ArtemisMessagingServer
|
import net.corda.node.services.messaging.ArtemisMessagingServer
|
||||||
@ -71,10 +71,12 @@ import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT
|
|||||||
import net.corda.serialization.internal.AMQP_RPC_SERVER_CONTEXT
|
import net.corda.serialization.internal.AMQP_RPC_SERVER_CONTEXT
|
||||||
import net.corda.serialization.internal.AMQP_STORAGE_CONTEXT
|
import net.corda.serialization.internal.AMQP_STORAGE_CONTEXT
|
||||||
import net.corda.serialization.internal.SerializationFactoryImpl
|
import net.corda.serialization.internal.SerializationFactoryImpl
|
||||||
|
import org.h2.jdbc.JdbcSQLException
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import rx.Scheduler
|
import rx.Scheduler
|
||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
|
import java.net.BindException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
@ -354,7 +356,7 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
if (databaseUrl != null && databaseUrl.startsWith(h2Prefix)) {
|
if (databaseUrl != null && databaseUrl.startsWith(h2Prefix)) {
|
||||||
val effectiveH2Settings = configuration.effectiveH2Settings
|
val effectiveH2Settings = configuration.effectiveH2Settings
|
||||||
|
|
||||||
if(effectiveH2Settings != null && effectiveH2Settings.address != null) {
|
if (effectiveH2Settings?.address != null) {
|
||||||
val databaseName = databaseUrl.removePrefix(h2Prefix).substringBefore(';')
|
val databaseName = databaseUrl.removePrefix(h2Prefix).substringBefore(';')
|
||||||
val server = org.h2.tools.Server.createTcpServer(
|
val server = org.h2.tools.Server.createTcpServer(
|
||||||
"-tcpPort", effectiveH2Settings.address.port.toString(),
|
"-tcpPort", effectiveH2Settings.address.port.toString(),
|
||||||
@ -364,7 +366,15 @@ open class Node(configuration: NodeConfiguration,
|
|||||||
// override interface that createTcpServer listens on (which is always 0.0.0.0)
|
// override interface that createTcpServer listens on (which is always 0.0.0.0)
|
||||||
System.setProperty("h2.bindAddress", effectiveH2Settings.address.host)
|
System.setProperty("h2.bindAddress", effectiveH2Settings.address.host)
|
||||||
runOnStop += server::stop
|
runOnStop += server::stop
|
||||||
val url = server.start().url
|
val url = try {
|
||||||
|
server.start().url
|
||||||
|
} catch (e: JdbcSQLException) {
|
||||||
|
if (e.cause is BindException) {
|
||||||
|
throw AddressBindingException(effectiveH2Settings.address)
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
printBasicNodeInfo("Database connection url is", "jdbc:h2:$url/node")
|
printBasicNodeInfo("Database connection url is", "jdbc:h2:$url/node")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,14 @@ import com.typesafe.config.ConfigRenderOptions
|
|||||||
import io.netty.channel.unix.Errors
|
import io.netty.channel.unix.Errors
|
||||||
import net.corda.core.cordapp.Cordapp
|
import net.corda.core.cordapp.Cordapp
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
import net.corda.core.internal.*
|
import net.corda.core.internal.Emoji
|
||||||
import net.corda.core.internal.concurrent.thenMatch
|
import net.corda.core.internal.concurrent.thenMatch
|
||||||
|
import net.corda.core.internal.createDirectories
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.internal.errors.AddressBindingException
|
||||||
|
import net.corda.core.internal.exists
|
||||||
|
import net.corda.core.internal.location
|
||||||
|
import net.corda.core.internal.randomOrNull
|
||||||
import net.corda.core.utilities.Try
|
import net.corda.core.utilities.Try
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.node.*
|
import net.corda.node.*
|
||||||
@ -166,6 +172,9 @@ open class NodeStartup(val args: Array<String>) {
|
|||||||
} catch (e: CheckpointIncompatibleException) {
|
} catch (e: CheckpointIncompatibleException) {
|
||||||
logger.error(e.message)
|
logger.error(e.message)
|
||||||
return false
|
return false
|
||||||
|
} catch (e: AddressBindingException) {
|
||||||
|
logger.error(e.message)
|
||||||
|
return false
|
||||||
} catch (e: NetworkParametersReader.Error) {
|
} catch (e: NetworkParametersReader.Error) {
|
||||||
logger.error(e.message)
|
logger.error(e.message)
|
||||||
return false
|
return false
|
||||||
|
@ -10,9 +10,11 @@
|
|||||||
|
|
||||||
package net.corda.node.internal.artemis
|
package net.corda.node.internal.artemis
|
||||||
|
|
||||||
|
import io.netty.channel.unix.Errors
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.internal.LifecycleSupport
|
import net.corda.node.internal.LifecycleSupport
|
||||||
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
|
||||||
|
import java.net.BindException
|
||||||
|
|
||||||
interface ArtemisBroker : LifecycleSupport, AutoCloseable {
|
interface ArtemisBroker : LifecycleSupport, AutoCloseable {
|
||||||
val addresses: BrokerAddresses
|
val addresses: BrokerAddresses
|
||||||
@ -24,4 +26,6 @@ interface ArtemisBroker : LifecycleSupport, AutoCloseable {
|
|||||||
|
|
||||||
data class BrokerAddresses(val primary: NetworkHostAndPort, private val adminArg: NetworkHostAndPort?) {
|
data class BrokerAddresses(val primary: NetworkHostAndPort, private val adminArg: NetworkHostAndPort?) {
|
||||||
val admin = adminArg ?: primary
|
val admin = adminArg ?: primary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun java.io.IOException.isBindingError() = this is BindException || this is Errors.NativeIoException && message?.contains("Address already in use") == true
|
@ -10,12 +10,79 @@
|
|||||||
|
|
||||||
package net.corda.node.services.api
|
package net.corda.node.services.api
|
||||||
|
|
||||||
|
import net.corda.core.identity.CordaX500Name
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.PartyAndCertificate
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
|
import net.corda.core.internal.CertRole
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
|
import net.corda.core.utilities.contextLogger
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||||
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
|
import java.security.InvalidAlgorithmParameterException
|
||||||
|
import java.security.cert.CertPathValidatorException
|
||||||
|
import java.security.cert.CertificateExpiredException
|
||||||
|
import java.security.cert.CertificateNotYetValidException
|
||||||
|
import java.security.cert.TrustAnchor
|
||||||
|
|
||||||
interface IdentityServiceInternal : IdentityService {
|
interface IdentityServiceInternal : IdentityService {
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
val log = contextLogger()
|
||||||
|
}
|
||||||
|
|
||||||
/** This method exists so it can be mocked with doNothing, rather than having to make up a possibly invalid return value. */
|
/** This method exists so it can be mocked with doNothing, rather than having to make up a possibly invalid return value. */
|
||||||
fun justVerifyAndRegisterIdentity(identity: PartyAndCertificate) {
|
fun justVerifyAndRegisterIdentity(identity: PartyAndCertificate) {
|
||||||
verifyAndRegisterIdentity(identity)
|
verifyAndRegisterIdentity(identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun partiesFromName(query: String, exactMatch: Boolean, x500name: CordaX500Name, results: LinkedHashSet<Party>, party: Party) {
|
||||||
|
val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country)
|
||||||
|
components.forEach { component ->
|
||||||
|
if (exactMatch && component == query) {
|
||||||
|
results += party
|
||||||
|
} else if (!exactMatch) {
|
||||||
|
// We can imagine this being a query over a lucene index in future.
|
||||||
|
//
|
||||||
|
// Kostas says: We can easily use the Jaro-Winkler distance metric as it is best suited for short
|
||||||
|
// strings such as entity/company names, and to detect small typos. We can also apply it for city
|
||||||
|
// or any keyword related search in lists of records (not raw text - for raw text we need indexing)
|
||||||
|
// and we can return results in hierarchical order (based on normalised String similarity 0.0-1.0).
|
||||||
|
if (component.contains(query, ignoreCase = true))
|
||||||
|
results += party
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies that an identity is valid.
|
||||||
|
*
|
||||||
|
* @param trustAnchor The trust anchor that will verify the identity's validity
|
||||||
|
* @param identity The identity to verify
|
||||||
|
*/
|
||||||
|
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||||
|
fun verifyAndRegisterIdentity(trustAnchor: TrustAnchor, identity: PartyAndCertificate): PartyAndCertificate? {
|
||||||
|
// Validate the chain first, before we do anything clever with it
|
||||||
|
val identityCertChain = identity.certPath.x509Certificates
|
||||||
|
try {
|
||||||
|
identity.verify(trustAnchor)
|
||||||
|
} catch (e: CertPathValidatorException) {
|
||||||
|
log.warn("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.")
|
||||||
|
log.warn("Certificate path :")
|
||||||
|
identityCertChain.reversed().forEachIndexed { index, certificate ->
|
||||||
|
val space = (0 until index).joinToString("") { " " }
|
||||||
|
log.warn("$space${certificate.subjectX500Principal}")
|
||||||
|
}
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
// Ensure we record the first identity of the same name, first
|
||||||
|
val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
|
||||||
|
if (wellKnownCert != identity.certificate) {
|
||||||
|
val idx = identityCertChain.lastIndexOf(wellKnownCert)
|
||||||
|
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
|
||||||
|
verifyAndRegisterIdentity(trustAnchor, PartyAndCertificate(firstPath))
|
||||||
|
}
|
||||||
|
return registerIdentity(identity)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun registerIdentity(identity: PartyAndCertificate): PartyAndCertificate?
|
||||||
}
|
}
|
||||||
|
@ -10,16 +10,13 @@
|
|||||||
|
|
||||||
package net.corda.node.services.identity
|
package net.corda.node.services.identity
|
||||||
|
|
||||||
import net.corda.core.contracts.PartyAndReference
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.identity.*
|
import net.corda.core.identity.PartyAndCertificate
|
||||||
import net.corda.core.internal.CertRole
|
|
||||||
import net.corda.core.node.services.IdentityService
|
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
import java.security.InvalidAlgorithmParameterException
|
import java.security.InvalidAlgorithmParameterException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -32,10 +29,9 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
*
|
*
|
||||||
* @param identities initial set of identities for the service, typically only used for unit tests.
|
* @param identities initial set of identities for the service, typically only used for unit tests.
|
||||||
*/
|
*/
|
||||||
// TODO There is duplicated logic between this and PersistentIdentityService
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(),
|
class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(),
|
||||||
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityService {
|
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = contextLogger()
|
private val log = contextLogger()
|
||||||
}
|
}
|
||||||
@ -54,29 +50,10 @@ class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? = verifyAndRegisterIdentity(trustAnchor, identity)
|
||||||
// Validate the chain first, before we do anything clever with it
|
|
||||||
|
override fun registerIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||||
val identityCertChain = identity.certPath.x509Certificates
|
val identityCertChain = identity.certPath.x509Certificates
|
||||||
try {
|
|
||||||
identity.verify(trustAnchor)
|
|
||||||
} catch (e: CertPathValidatorException) {
|
|
||||||
log.warn("Certificate validation failed for ${identity.name} against trusted root ${trustAnchor.trustedCert.subjectX500Principal}.")
|
|
||||||
log.warn("Certificate path :")
|
|
||||||
identityCertChain.reversed().forEachIndexed { index, certificate ->
|
|
||||||
val space = (0 until index).joinToString("") { " " }
|
|
||||||
log.warn("$space${certificate.subjectX500Principal}")
|
|
||||||
}
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we record the first identity of the same name, first
|
|
||||||
val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
|
|
||||||
if (wellKnownCert != identity.certificate) {
|
|
||||||
val idx = identityCertChain.lastIndexOf(wellKnownCert)
|
|
||||||
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
|
|
||||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
|
||||||
}
|
|
||||||
|
|
||||||
log.trace { "Registering identity $identity" }
|
log.trace { "Registering identity $identity" }
|
||||||
keyToParties[identity.owningKey] = identity
|
keyToParties[identity.owningKey] = identity
|
||||||
// Always keep the first party we registered, as that's the well known identity
|
// Always keep the first party we registered, as that's the well known identity
|
||||||
@ -89,26 +66,7 @@ class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(
|
|||||||
// We give the caller a copy of the data set to avoid any locking problems
|
// We give the caller a copy of the data set to avoid any locking problems
|
||||||
override fun getAllIdentities(): Iterable<PartyAndCertificate> = ArrayList(keyToParties.values)
|
override fun getAllIdentities(): Iterable<PartyAndCertificate> = ArrayList(keyToParties.values)
|
||||||
|
|
||||||
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]?.party
|
|
||||||
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = principalToParties[name]?.party
|
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = principalToParties[name]?.party
|
||||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
|
||||||
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
|
||||||
// however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
|
|
||||||
// into a party, and from there figure out the well known party.
|
|
||||||
val candidate = partyFromKey(party.owningKey)
|
|
||||||
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
|
||||||
return if (candidate != null) {
|
|
||||||
require(party.nameOrNull() == null || party.nameOrNull() == candidate.name) { "Candidate party $candidate does not match expected $party" }
|
|
||||||
wellKnownPartyFromX500Name(candidate.name)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference) = wellKnownPartyFromAnonymous(partyRef.party)
|
|
||||||
override fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party {
|
|
||||||
return wellKnownPartyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
|
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
|
||||||
val results = LinkedHashSet<Party>()
|
val results = LinkedHashSet<Party>()
|
||||||
@ -117,14 +75,4 @@ class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(
|
|||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(UnknownAnonymousPartyException::class)
|
|
||||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
|
||||||
val anonymousIdentity = keyToParties[anonymousParty.owningKey] ?:
|
|
||||||
throw UnknownAnonymousPartyException("Unknown $anonymousParty")
|
|
||||||
val issuingCert = anonymousIdentity.certPath.certificates[1]
|
|
||||||
require(issuingCert.publicKey == party.owningKey) {
|
|
||||||
"Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,8 @@
|
|||||||
|
|
||||||
package net.corda.node.services.identity
|
package net.corda.node.services.identity
|
||||||
|
|
||||||
import net.corda.core.contracts.PartyAndReference
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.toStringShort
|
|
||||||
import net.corda.core.identity.*
|
import net.corda.core.identity.*
|
||||||
import net.corda.core.internal.CertRole
|
|
||||||
import net.corda.core.internal.hash
|
import net.corda.core.internal.hash
|
||||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
@ -24,7 +21,6 @@ import net.corda.core.utilities.debug
|
|||||||
import net.corda.node.services.api.IdentityServiceInternal
|
import net.corda.node.services.api.IdentityServiceInternal
|
||||||
import net.corda.node.utilities.AppendOnlyPersistentMap
|
import net.corda.node.utilities.AppendOnlyPersistentMap
|
||||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
|
||||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||||
@ -45,7 +41,6 @@ import javax.persistence.Lob
|
|||||||
* @param trustRoot certificate from the zone operator for identity on the network.
|
* @param trustRoot certificate from the zone operator for identity on the network.
|
||||||
* @param caCertificates list of additional certificates.
|
* @param caCertificates list of additional certificates.
|
||||||
*/
|
*/
|
||||||
// TODO There is duplicated logic between this and InMemoryIdentityService
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
class PersistentIdentityService(override val trustRoot: X509Certificate,
|
||||||
private val database: CordaPersistence,
|
private val database: CordaPersistence,
|
||||||
@ -137,38 +132,21 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
|||||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
|
verifyAndRegisterIdentity(trustAnchor, identity)
|
||||||
// Validate the chain first, before we do anything clever with it
|
|
||||||
val identityCertChain = identity.certPath.x509Certificates
|
|
||||||
try {
|
|
||||||
identity.verify(trustAnchor)
|
|
||||||
} catch (e: CertPathValidatorException) {
|
|
||||||
log.warn(e.localizedMessage)
|
|
||||||
log.warn("Path = ")
|
|
||||||
identityCertChain.reversed().forEach {
|
|
||||||
log.warn(it.subjectX500Principal.toString())
|
|
||||||
}
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we record the first identity of the same name, first
|
|
||||||
val wellKnownCert = identityCertChain.single { CertRole.extract(it)?.isWellKnown ?: false }
|
|
||||||
if (wellKnownCert != identity.certificate) {
|
|
||||||
val idx = identityCertChain.lastIndexOf(wellKnownCert)
|
|
||||||
val firstPath = X509Utilities.buildCertPath(identityCertChain.slice(idx until identityCertChain.size))
|
|
||||||
verifyAndRegisterIdentity(PartyAndCertificate(firstPath))
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug { "Registering identity $identity" }
|
|
||||||
val key = mapToKey(identity)
|
|
||||||
keyToParties.addWithDuplicatesAllowed(key, identity, false)
|
|
||||||
// Always keep the first party we registered, as that's the well known identity
|
|
||||||
principalToParties.addWithDuplicatesAllowed(identity.name, key, false)
|
|
||||||
val parentId = mapToKey(identityCertChain[1].publicKey)
|
|
||||||
keyToParties[parentId]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun registerIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||||
|
val identityCertChain = identity.certPath.x509Certificates
|
||||||
|
log.debug { "Registering identity $identity" }
|
||||||
|
val key = mapToKey(identity)
|
||||||
|
keyToParties.addWithDuplicatesAllowed(key, identity)
|
||||||
|
// Always keep the first party we registered, as that's the well known identity
|
||||||
|
principalToParties.addWithDuplicatesAllowed(identity.name, key, false)
|
||||||
|
val parentId = mapToKey(identityCertChain[1].publicKey)
|
||||||
|
return keyToParties[parentId]
|
||||||
|
}
|
||||||
|
|
||||||
override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = database.transaction { keyToParties[mapToKey(owningKey)] }
|
override fun certificateFromKey(owningKey: PublicKey): PartyAndCertificate? = database.transaction { keyToParties[mapToKey(owningKey)] }
|
||||||
|
|
||||||
private fun certificateFromCordaX500Name(name: CordaX500Name): PartyAndCertificate? {
|
private fun certificateFromCordaX500Name(name: CordaX500Name): PartyAndCertificate? {
|
||||||
@ -183,27 +161,9 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
|||||||
// We give the caller a copy of the data set to avoid any locking problems
|
// We give the caller a copy of the data set to avoid any locking problems
|
||||||
override fun getAllIdentities(): Iterable<PartyAndCertificate> = database.transaction { keyToParties.allPersisted().map { it.second }.asIterable() }
|
override fun getAllIdentities(): Iterable<PartyAndCertificate> = database.transaction { keyToParties.allPersisted().map { it.second }.asIterable() }
|
||||||
|
|
||||||
override fun partyFromKey(key: PublicKey): Party? = certificateFromKey(key)?.party
|
|
||||||
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party
|
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = certificateFromCordaX500Name(name)?.party
|
||||||
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? {
|
|
||||||
return database.transaction {
|
|
||||||
// The original version of this would return the party as-is if it was a Party (rather than AnonymousParty),
|
|
||||||
// however that means that we don't verify that we know who owns the key. As such as now enforce turning the key
|
|
||||||
// into a party, and from there figure out the well known party.
|
|
||||||
val candidate = partyFromKey(party.owningKey)
|
|
||||||
// TODO: This should be done via the network map cache, which is the authoritative source of well known identities
|
|
||||||
if (candidate != null) {
|
|
||||||
wellKnownPartyFromX500Name(candidate.name)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun wellKnownPartyFromAnonymous(partyRef: PartyAndReference) = wellKnownPartyFromAnonymous(partyRef.party)
|
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? = database.transaction { super.wellKnownPartyFromAnonymous(party) }
|
||||||
override fun requireWellKnownPartyFromAnonymous(party: AbstractParty): Party {
|
|
||||||
return wellKnownPartyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
|
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
|
||||||
return database.transaction {
|
return database.transaction {
|
||||||
@ -216,13 +176,6 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws(UnknownAnonymousPartyException::class)
|
@Throws(UnknownAnonymousPartyException::class)
|
||||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) = database.transaction { super.assertOwnership(party, anonymousParty) }
|
||||||
database.transaction {
|
|
||||||
val anonymousIdentity = certificateFromKey(anonymousParty.owningKey) ?: throw UnknownAnonymousPartyException("Unknown $anonymousParty")
|
|
||||||
val issuingCert = anonymousIdentity.certPath.certificates[1]
|
|
||||||
require(issuingCert.publicKey == party.owningKey) {
|
|
||||||
"Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
package net.corda.node.services.messaging
|
package net.corda.node.services.messaging
|
||||||
|
|
||||||
|
import io.netty.channel.unix.Errors
|
||||||
import net.corda.core.internal.ThreadBox
|
import net.corda.core.internal.ThreadBox
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
@ -19,6 +20,7 @@ import net.corda.core.utilities.debug
|
|||||||
import net.corda.node.internal.artemis.*
|
import net.corda.node.internal.artemis.*
|
||||||
import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.NODE_P2P_ROLE
|
import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.NODE_P2P_ROLE
|
||||||
import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.PEER_ROLE
|
import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.PEER_ROLE
|
||||||
|
import net.corda.core.internal.errors.AddressBindingException
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.nodeapi.ArtemisTcpTransport.Companion.p2pAcceptorTcpTransport
|
import net.corda.nodeapi.ArtemisTcpTransport.Companion.p2pAcceptorTcpTransport
|
||||||
import net.corda.nodeapi.internal.AmqpMessageSizeChecksInterceptor
|
import net.corda.nodeapi.internal.AmqpMessageSizeChecksInterceptor
|
||||||
@ -101,7 +103,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
|||||||
|
|
||||||
// TODO: Maybe wrap [IOException] on a key store load error so that it's clearly splitting key store loading from
|
// TODO: Maybe wrap [IOException] on a key store load error so that it's clearly splitting key store loading from
|
||||||
// Artemis IO errors
|
// Artemis IO errors
|
||||||
@Throws(IOException::class, KeyStoreException::class)
|
@Throws(IOException::class, AddressBindingException::class, KeyStoreException::class)
|
||||||
private fun configureAndStartServer() {
|
private fun configureAndStartServer() {
|
||||||
val artemisConfig = createArtemisConfig()
|
val artemisConfig = createArtemisConfig()
|
||||||
val securityManager = createArtemisSecurityManager()
|
val securityManager = createArtemisSecurityManager()
|
||||||
@ -114,7 +116,15 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
|||||||
registerPostQueueDeletionCallback { address, qName -> log.debug { "Queue deleted: $qName for $address" } }
|
registerPostQueueDeletionCallback { address, qName -> log.debug { "Queue deleted: $qName for $address" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
activeMQServer.start()
|
try {
|
||||||
|
activeMQServer.start()
|
||||||
|
} catch (e: java.io.IOException) {
|
||||||
|
if (e.isBindingError()) {
|
||||||
|
throw AddressBindingException(config.p2pAddress)
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
activeMQServer.remotingService.addIncomingInterceptor(ArtemisMessageSizeChecksInterceptor(maxMessageSize))
|
activeMQServer.remotingService.addIncomingInterceptor(ArtemisMessageSizeChecksInterceptor(maxMessageSize))
|
||||||
activeMQServer.remotingService.addIncomingInterceptor(AmqpMessageSizeChecksInterceptor(maxMessageSize))
|
activeMQServer.remotingService.addIncomingInterceptor(AmqpMessageSizeChecksInterceptor(maxMessageSize))
|
||||||
// Config driven switch between legacy CORE bridges and the newer AMQP protocol bridges.
|
// Config driven switch between legacy CORE bridges and the newer AMQP protocol bridges.
|
||||||
|
@ -10,11 +10,13 @@
|
|||||||
|
|
||||||
package net.corda.node.services.rpc
|
package net.corda.node.services.rpc
|
||||||
|
|
||||||
|
import io.netty.channel.unix.Errors
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.node.internal.artemis.*
|
import net.corda.node.internal.artemis.*
|
||||||
import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.NODE_SECURITY_CONFIG
|
import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.NODE_SECURITY_CONFIG
|
||||||
import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.RPC_SECURITY_CONFIG
|
import net.corda.node.internal.artemis.BrokerJaasLoginModule.Companion.RPC_SECURITY_CONFIG
|
||||||
|
import net.corda.core.internal.errors.AddressBindingException
|
||||||
import net.corda.node.internal.security.RPCSecurityManager
|
import net.corda.node.internal.security.RPCSecurityManager
|
||||||
import net.corda.nodeapi.BrokerRpcSslOptions
|
import net.corda.nodeapi.BrokerRpcSslOptions
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
@ -54,7 +56,15 @@ internal class ArtemisRpcBroker internal constructor(
|
|||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
logger.debug("Artemis RPC broker is starting.")
|
logger.debug("Artemis RPC broker is starting.")
|
||||||
server.start()
|
try {
|
||||||
|
server.start()
|
||||||
|
} catch (e: java.io.IOException) {
|
||||||
|
if (e.isBindingError()) {
|
||||||
|
throw AddressBindingException(adminAddressOptional?.let { setOf(it, addresses.primary) } ?: setOf(addresses.primary))
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
logger.debug("Artemis RPC broker is started.")
|
logger.debug("Artemis RPC broker is started.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ package net.corda.webserver
|
|||||||
|
|
||||||
import com.typesafe.config.ConfigException
|
import com.typesafe.config.ConfigException
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.internal.errors.AddressBindingException
|
||||||
import net.corda.core.internal.location
|
import net.corda.core.internal.location
|
||||||
import net.corda.core.internal.rootCause
|
import net.corda.core.internal.rootCause
|
||||||
import net.corda.webserver.internal.NodeWebServer
|
import net.corda.webserver.internal.NodeWebServer
|
||||||
@ -76,6 +77,9 @@ fun main(args: Array<String>) {
|
|||||||
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
|
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
|
||||||
println("Webserver started up in $elapsed sec")
|
println("Webserver started up in $elapsed sec")
|
||||||
server.run()
|
server.run()
|
||||||
|
} catch (e: AddressBindingException) {
|
||||||
|
log.error(e.message)
|
||||||
|
exitProcess(1)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log.error("Exception during node startup", e)
|
log.error("Exception during node startup", e)
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
|
@ -11,26 +11,18 @@
|
|||||||
package net.corda.webserver.internal
|
package net.corda.webserver.internal
|
||||||
|
|
||||||
import com.google.common.html.HtmlEscapers.htmlEscaper
|
import com.google.common.html.HtmlEscapers.htmlEscaper
|
||||||
|
import io.netty.channel.unix.Errors
|
||||||
import net.corda.client.jackson.JacksonSupport
|
import net.corda.client.jackson.JacksonSupport
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
import net.corda.client.rpc.RPCException
|
import net.corda.client.rpc.RPCException
|
||||||
|
import net.corda.core.internal.errors.AddressBindingException
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.webserver.WebServerConfig
|
import net.corda.webserver.WebServerConfig
|
||||||
import net.corda.webserver.converters.CordaConverterProvider
|
import net.corda.webserver.converters.CordaConverterProvider
|
||||||
import net.corda.webserver.services.WebServerPluginRegistry
|
import net.corda.webserver.services.WebServerPluginRegistry
|
||||||
import net.corda.webserver.servlets.AttachmentDownloadServlet
|
import net.corda.webserver.servlets.*
|
||||||
import net.corda.webserver.servlets.CorDappInfoServlet
|
import org.eclipse.jetty.server.*
|
||||||
import net.corda.webserver.servlets.DataUploadServlet
|
|
||||||
import net.corda.webserver.servlets.ObjectMapperConfig
|
|
||||||
import net.corda.webserver.servlets.ResponseFilter
|
|
||||||
import org.eclipse.jetty.server.Connector
|
|
||||||
import org.eclipse.jetty.server.HttpConfiguration
|
|
||||||
import org.eclipse.jetty.server.HttpConnectionFactory
|
|
||||||
import org.eclipse.jetty.server.SecureRequestCustomizer
|
|
||||||
import org.eclipse.jetty.server.Server
|
|
||||||
import org.eclipse.jetty.server.ServerConnector
|
|
||||||
import org.eclipse.jetty.server.SslConnectionFactory
|
|
||||||
import org.eclipse.jetty.server.handler.ErrorHandler
|
import org.eclipse.jetty.server.handler.ErrorHandler
|
||||||
import org.eclipse.jetty.server.handler.HandlerCollection
|
import org.eclipse.jetty.server.handler.HandlerCollection
|
||||||
import org.eclipse.jetty.servlet.DefaultServlet
|
import org.eclipse.jetty.servlet.DefaultServlet
|
||||||
@ -44,6 +36,7 @@ import org.slf4j.LoggerFactory
|
|||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.Writer
|
import java.io.Writer
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
import java.net.BindException
|
||||||
import java.nio.file.NoSuchFileException
|
import java.nio.file.NoSuchFileException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.servlet.http.HttpServletRequest
|
import javax.servlet.http.HttpServletRequest
|
||||||
@ -105,7 +98,15 @@ class NodeWebServer(val config: WebServerConfig) {
|
|||||||
server.connectors = arrayOf<Connector>(connector)
|
server.connectors = arrayOf<Connector>(connector)
|
||||||
|
|
||||||
server.handler = handlerCollection
|
server.handler = handlerCollection
|
||||||
server.start()
|
try {
|
||||||
|
server.start()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
if (e is BindException || e is Errors.NativeIoException && e.message?.contains("Address already in use") == true) {
|
||||||
|
throw AddressBindingException(address)
|
||||||
|
} else {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
log.info("Starting webserver on address $address")
|
log.info("Starting webserver on address $address")
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user