Merge remote-tracking branch 'open/master' into anthony-os-merge-20180710

# Conflicts:
#	.idea/compiler.xml
#	docs/source/changelog.rst
#	docs/source/network-bootstrapper.rst
#	node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
#	node/src/main/kotlin/net/corda/node/services/identity/IdentityServiceUtil.kt
#	node/src/main/kotlin/net/corda/node/services/identity/PersistentIdentityService.kt
This commit is contained in:
Anthony Keenan 2018-07-10 11:19:23 +01:00
commit 8d7aa5f590
19 changed files with 595 additions and 197 deletions

View File

@ -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()}."
}
}
}
}

View File

@ -13,6 +13,7 @@ package net.corda.core.node.services
import net.corda.core.CordaException
import net.corda.core.DoNotImplement
import net.corda.core.contracts.PartyAndReference
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.*
import java.security.InvalidAlgorithmParameterException
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
* 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)
fun assertOwnership(party: Party, anonymousParty: AnonymousParty)
@Throws(UnknownAnonymousPartyException::class)
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
@ -73,7 +81,7 @@ interface IdentityService {
* @param key The owning [PublicKey] of the [Party].
* @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
@ -92,7 +100,21 @@ interface IdentityService {
* @param party identity to determine well known identity for.
* @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].
@ -103,7 +125,7 @@ interface IdentityService {
* @param partyRef identity (and reference, which is unused) to determine well known identity for.
* @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.
@ -112,7 +134,10 @@ interface IdentityService {
* @return the well known identity.
* @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

View File

@ -12,6 +12,9 @@ Unreleased
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.
* 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.
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.

View File

@ -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.
: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:

View File

@ -24,18 +24,13 @@ You can find out more about network maps and network parameters from :doc:`netwo
Bootstrapping a test network
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The bootstrapper is distributed as part of |release| in the form of runnable JAR file "|jar_name|".
.. |jar_name| replace:: corda-tools-network-bootstrapper-|version|.jar
The bootstrapper can be downloaded from https://downloads.corda.net/network-bootstrapper-VERSION.jar, where ``VERSION``
is the Corda version.
Create a directory containing a node config file, ending in "_node.conf", for each node you want to create. Then run the
following command:
.. parsed-literal::
> java -jar |jar_name| --dir <nodes-root-dir>
..
``java -jar network-bootstrapper-VERSION.jar --dir <nodes-root-dir>``
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
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
@ -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.
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
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
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.
If you would like the Network Bootstrapper to include your CorDapps in each generated node, just place them in the directory
alongside the config files. For example, if your directory has this structure:
Running the bootstrapper again on the same network will allow a new node to be added or an existing one to have its updated
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.
.. sourcecode:: none
.
├── 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
~~~~~~~~~~~~~~~~~~~~~~
----------------------
The CorDapp JARs are also automatically used to create the *Zone whitelist* (see :doc:`api-contract-constraints`) for
the network.
Any CorDapps provided when bootstrapping a network will be scanned for contracts which will be used to create the
*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.
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`).
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
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.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.

View File

@ -10,7 +10,107 @@ Quickstart
getting-set-up.rst
tutorial-cordapp.rst
* :doc:`Set up your machine for CorDapp development <getting-set-up>`
* :doc:`Run the Example CorDapp <tutorial-cordapp>`
* `View CorDapps in Corda Explore <http://explore.corda.zone/>`_
* `Download sample CorDapps <https://www.corda.net/samples/>`_
Welcome to the Corda Quickstart Guide. Follow the links below to help get going quickly with Corda.
I want to:
* :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 |
+---------------------------------------------------------------------------------------------------------+

View File

@ -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)
}

View File

@ -73,7 +73,7 @@ class BootTests : IntegrationTest() {
@Test
fun `double node start doesn't write into log file`() {
driver {
driver(DriverParameters(notarySpecs = emptyList())) {
val alice = startNode(providedName = ALICE_NAME).get()
val logFolder = alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
val logFile = logFolder.list { it.filter { it.fileName.toString().endsWith(".log") }.findAny().get() }

View File

@ -21,7 +21,16 @@ import net.corda.core.concurrent.CordaFuture
import net.corda.core.context.InvocationContext
import net.corda.core.crypto.newSecureRandom
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.CordaX500Name
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.notary.NotaryService
import net.corda.core.internal.uncheckedCast
import net.corda.core.messaging.*
import net.corda.core.node.*
import net.corda.core.node.services.*
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.FlowHandle
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.SerializeAsToken
import net.corda.core.serialization.SingletonSerializeAsToken
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.VersionInfo
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.FinalityHandler
import net.corda.node.services.NotaryChangeHandler
import net.corda.node.services.api.*
import net.corda.node.services.config.*
import net.corda.node.services.api.CheckpointStorage
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.shouldInitCrashShell
import net.corda.node.services.events.NodeSchedulerService
import net.corda.node.services.events.ScheduledActivityObserver
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.NodePropertiesPersistentStore
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.NodeSchemaService
import net.corda.node.services.statemachine.ExternalEvent
import net.corda.node.services.statemachine.FlowLogicRefFactoryImpl
import net.corda.node.services.statemachine.SingleThreadedStateMachineManager
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.flowVersionAndInitiatingClass
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.SimpleNotaryService
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.vault.NodeVaultService
import net.corda.node.utilities.AffinityExecutor
@ -140,7 +181,6 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit.SECONDS
import java.util.concurrent.atomic.AtomicReference
import kotlin.collections.set
import kotlin.reflect.KClass
@ -431,7 +471,11 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
open fun startShell() {
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)
}
}

View File

@ -36,6 +36,7 @@ import net.corda.node.VersionInfo
import net.corda.node.internal.artemis.ArtemisBroker
import net.corda.node.internal.artemis.BrokerAddresses
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.RPCSecurityManagerWithAdditionalUser
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.config.NodeConfiguration
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.shouldStartLocalShell
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_STORAGE_CONTEXT
import net.corda.serialization.internal.SerializationFactoryImpl
import org.h2.jdbc.JdbcSQLException
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import rx.Scheduler
import rx.schedulers.Schedulers
import java.net.BindException
import java.nio.file.Path
import java.security.PublicKey
import java.time.Clock
@ -354,7 +356,7 @@ open class Node(configuration: NodeConfiguration,
if (databaseUrl != null && databaseUrl.startsWith(h2Prefix)) {
val effectiveH2Settings = configuration.effectiveH2Settings
if(effectiveH2Settings != null && effectiveH2Settings.address != null) {
if (effectiveH2Settings?.address != null) {
val databaseName = databaseUrl.removePrefix(h2Prefix).substringBefore(';')
val server = org.h2.tools.Server.createTcpServer(
"-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)
System.setProperty("h2.bindAddress", effectiveH2Settings.address.host)
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")
}
}

View File

@ -17,8 +17,14 @@ import com.typesafe.config.ConfigRenderOptions
import io.netty.channel.unix.Errors
import net.corda.core.cordapp.Cordapp
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.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.loggerFor
import net.corda.node.*
@ -166,6 +172,9 @@ open class NodeStartup(val args: Array<String>) {
} catch (e: CheckpointIncompatibleException) {
logger.error(e.message)
return false
} catch (e: AddressBindingException) {
logger.error(e.message)
return false
} catch (e: NetworkParametersReader.Error) {
logger.error(e.message)
return false

View File

@ -10,9 +10,11 @@
package net.corda.node.internal.artemis
import io.netty.channel.unix.Errors
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.internal.LifecycleSupport
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl
import java.net.BindException
interface ArtemisBroker : LifecycleSupport, AutoCloseable {
val addresses: BrokerAddresses
@ -24,4 +26,6 @@ interface ArtemisBroker : LifecycleSupport, AutoCloseable {
data class BrokerAddresses(val primary: NetworkHostAndPort, private val adminArg: NetworkHostAndPort?) {
val admin = adminArg ?: primary
}
}
fun java.io.IOException.isBindingError() = this is BindException || this is Errors.NativeIoException && message?.contains("Address already in use") == true

View File

@ -10,12 +10,79 @@
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.internal.CertRole
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 {
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. */
fun justVerifyAndRegisterIdentity(identity: PartyAndCertificate) {
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?
}

View File

@ -10,16 +10,13 @@
package net.corda.node.services.identity
import net.corda.core.contracts.PartyAndReference
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.*
import net.corda.core.internal.CertRole
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.UnknownAnonymousPartyException
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.contextLogger
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 java.security.InvalidAlgorithmParameterException
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.
*/
// TODO There is duplicated logic between this and PersistentIdentityService
@ThreadSafe
class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(),
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityService {
override val trustRoot: X509Certificate) : SingletonSerializeAsToken(), IdentityServiceInternal {
companion object {
private val log = contextLogger()
}
@ -54,29 +50,10 @@ class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(
}
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
// Validate the chain first, before we do anything clever with it
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? = verifyAndRegisterIdentity(trustAnchor, identity)
override fun registerIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
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" }
keyToParties[identity.owningKey] = 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
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 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> {
val results = LinkedHashSet<Party>()
@ -117,14 +75,4 @@ class InMemoryIdentityService(identities: List<PartyAndCertificate> = emptyList(
}
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()}."
}
}
}

View File

@ -10,11 +10,8 @@
package net.corda.node.services.identity
import net.corda.core.contracts.PartyAndReference
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.*
import net.corda.core.internal.CertRole
import net.corda.core.internal.hash
import net.corda.core.node.services.UnknownAnonymousPartyException
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.utilities.AppendOnlyPersistentMap
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.persistence.CordaPersistence
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 caCertificates list of additional certificates.
*/
// TODO There is duplicated logic between this and InMemoryIdentityService
@ThreadSafe
class PersistentIdentityService(override val trustRoot: X509Certificate,
private val database: CordaPersistence,
@ -137,38 +132,21 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
return database.transaction {
// 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]
verifyAndRegisterIdentity(trustAnchor, identity)
}
}
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)] }
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
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 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 requireWellKnownPartyFromAnonymous(party: AbstractParty): Party {
return wellKnownPartyFromAnonymous(party) ?: throw IllegalStateException("Could not deanonymise party ${party.owningKey.toStringShort()}")
}
override fun wellKnownPartyFromAnonymous(party: AbstractParty): Party? = database.transaction { super.wellKnownPartyFromAnonymous(party) }
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
return database.transaction {
@ -216,13 +176,6 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
}
@Throws(UnknownAnonymousPartyException::class)
override fun assertOwnership(party: Party, anonymousParty: 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()}."
}
}
}
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) = database.transaction { super.assertOwnership(party, anonymousParty) }
}

View File

@ -10,6 +10,7 @@
package net.corda.node.services.messaging
import io.netty.channel.unix.Errors
import net.corda.core.internal.ThreadBox
import net.corda.core.internal.div
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.BrokerJaasLoginModule.Companion.NODE_P2P_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.nodeapi.ArtemisTcpTransport.Companion.p2pAcceptorTcpTransport
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
// Artemis IO errors
@Throws(IOException::class, KeyStoreException::class)
@Throws(IOException::class, AddressBindingException::class, KeyStoreException::class)
private fun configureAndStartServer() {
val artemisConfig = createArtemisConfig()
val securityManager = createArtemisSecurityManager()
@ -114,7 +116,15 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
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(AmqpMessageSizeChecksInterceptor(maxMessageSize))
// Config driven switch between legacy CORE bridges and the newer AMQP protocol bridges.

View File

@ -10,11 +10,13 @@
package net.corda.node.services.rpc
import io.netty.channel.unix.Errors
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.loggerFor
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.RPC_SECURITY_CONFIG
import net.corda.core.internal.errors.AddressBindingException
import net.corda.node.internal.security.RPCSecurityManager
import net.corda.nodeapi.BrokerRpcSslOptions
import net.corda.nodeapi.internal.config.SSLConfiguration
@ -54,7 +56,15 @@ internal class ArtemisRpcBroker internal constructor(
override fun start() {
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.")
}

View File

@ -14,6 +14,7 @@ package net.corda.webserver
import com.typesafe.config.ConfigException
import net.corda.core.internal.div
import net.corda.core.internal.errors.AddressBindingException
import net.corda.core.internal.location
import net.corda.core.internal.rootCause
import net.corda.webserver.internal.NodeWebServer
@ -76,6 +77,9 @@ fun main(args: Array<String>) {
val elapsed = (System.currentTimeMillis() - startTime) / 10 / 100.0
println("Webserver started up in $elapsed sec")
server.run()
} catch (e: AddressBindingException) {
log.error(e.message)
exitProcess(1)
} catch (e: Exception) {
log.error("Exception during node startup", e)
exitProcess(1)

View File

@ -11,26 +11,18 @@
package net.corda.webserver.internal
import com.google.common.html.HtmlEscapers.htmlEscaper
import io.netty.channel.unix.Errors
import net.corda.client.jackson.JacksonSupport
import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.RPCException
import net.corda.core.internal.errors.AddressBindingException
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.contextLogger
import net.corda.webserver.WebServerConfig
import net.corda.webserver.converters.CordaConverterProvider
import net.corda.webserver.services.WebServerPluginRegistry
import net.corda.webserver.servlets.AttachmentDownloadServlet
import net.corda.webserver.servlets.CorDappInfoServlet
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 net.corda.webserver.servlets.*
import org.eclipse.jetty.server.*
import org.eclipse.jetty.server.handler.ErrorHandler
import org.eclipse.jetty.server.handler.HandlerCollection
import org.eclipse.jetty.servlet.DefaultServlet
@ -44,6 +36,7 @@ import org.slf4j.LoggerFactory
import java.io.IOException
import java.io.Writer
import java.lang.reflect.InvocationTargetException
import java.net.BindException
import java.nio.file.NoSuchFileException
import java.util.*
import javax.servlet.http.HttpServletRequest
@ -105,7 +98,15 @@ class NodeWebServer(val config: WebServerConfig) {
server.connectors = arrayOf<Connector>(connector)
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")
return server
}