Clean up messaging/RPC port configuration and docs (#296)

* Non-ssl artemis acceptor for RPC connection. (#271)

* New non-ssl acceptor in artemis server for RPC connection.

* Rename artemisAddress with messagingAddress

Rename artemisAddress with messagingAddress so that the node configuration file properties match
the code variable names.
Rename artemisPort to messagingPort in Gradle configuration to match node configuration naming.

* Add rpcPort configuration option for Gradle

* Update docs to reflect changes to RPC port configuration

* Renumber ports in example CorDapp to match numbering used elsewhere

* Restructure upgrade guide

* added config file checks on corda startup to make the upgrade path a bit smoother.
This commit is contained in:
Ross Nicoll 2017-03-17 10:32:14 +00:00 committed by Patrick Kuo
parent eb21458885
commit 486368d926
63 changed files with 402 additions and 366 deletions

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.attachmentdemo.AttachmentDemoKt" /> <option name="MAIN_CLASS_NAME" value="net.corda.attachmentdemo.AttachmentDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " /> <option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " />
<option name="PROGRAM_PARAMETERS" value="--role=RECIPIENT --certificates=&quot;build/attachment-demo-nodes/Bank B/certificates&quot;" /> <option name="PROGRAM_PARAMETERS" value="--role=RECIPIENT" />
<option name="WORKING_DIRECTORY" value="" /> <option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" /> <option name="ALTERNATIVE_JRE_PATH" />

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.attachmentdemo.AttachmentDemoKt" /> <option name="MAIN_CLASS_NAME" value="net.corda.attachmentdemo.AttachmentDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " /> <option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " />
<option name="PROGRAM_PARAMETERS" value="--role SENDER --certificates=&quot;build/attachment-demo-nodes/Bank A/certificates&quot;" /> <option name="PROGRAM_PARAMETERS" value="--role SENDER" />
<option name="WORKING_DIRECTORY" value="" /> <option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" /> <option name="ALTERNATIVE_JRE_PATH" />

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.traderdemo.TraderDemoKt" /> <option name="MAIN_CLASS_NAME" value="net.corda.traderdemo.TraderDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " /> <option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " />
<option name="PROGRAM_PARAMETERS" value="--role BUYER --certificates=&quot;build/trader-demo-nodes/Bank A/certificates&quot;" /> <option name="PROGRAM_PARAMETERS" value="--role BUYER" />
<option name="WORKING_DIRECTORY" value="" /> <option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" /> <option name="ALTERNATIVE_JRE_PATH" />

View File

@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" /> <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="net.corda.traderdemo.TraderDemoKt" /> <option name="MAIN_CLASS_NAME" value="net.corda.traderdemo.TraderDemoKt" />
<option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " /> <option name="VM_PARAMETERS" value="-ea -javaagent:lib/quasar.jar " />
<option name="PROGRAM_PARAMETERS" value="--role SELLER --certificates=&quot;build/trader-demo-nodes/Bank B/certificates&quot;" /> <option name="PROGRAM_PARAMETERS" value="--role SELLER" />
<option name="WORKING_DIRECTORY" value="" /> <option name="WORKING_DIRECTORY" value="" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" /> <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" /> <option name="ALTERNATIVE_JRE_PATH" />

View File

@ -163,23 +163,25 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Controller" name "Controller"
nearestCity "London" nearestCity "London"
advertisedServices = ["corda.notary.validating"] advertisedServices = ["corda.notary.validating"]
artemisPort 10002 p2pPort 10002
cordapps = [] cordapps = []
} }
node { node {
name "Bank A" name "Bank A"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 10004 p2pPort 10012
webPort 10005 rpcPort 10013
webPort 10014
cordapps = [] cordapps = []
} }
node { node {
name "Bank B" name "Bank B"
nearestCity "New York" nearestCity "New York"
advertisedServices = [] advertisedServices = []
artemisPort 10006 p2pPort 10007
webPort 10007 rpcPort 10008
webPort 10009
cordapps = [] cordapps = []
} }
} }

View File

@ -12,10 +12,10 @@ import net.corda.flows.CashIssueFlow
import net.corda.flows.CashPaymentFlow import net.corda.flows.CashPaymentFlow
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.services.User import net.corda.node.services.User
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.CordaRPCClient import net.corda.node.services.messaging.CordaRPCClient
import net.corda.node.services.startFlowPermission import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.testing.configureTestSSL
import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThatExceptionOfType import org.assertj.core.api.Assertions.assertThatExceptionOfType
@ -37,7 +37,7 @@ class CordaRPCClientTest : NodeBasedTest() {
@Before @Before
fun setUp() { fun setUp() {
node = startNode("Alice", rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow() node = startNode("Alice", rpcUsers = listOf(rpcUser), advertisedServices = setOf(ServiceInfo(ValidatingNotaryService.type))).getOrThrow()
client = CordaRPCClient(node.configuration.artemisAddress, configureTestSSL()) client = CordaRPCClient(node.configuration.rpcAddress!!)
} }
@After @After

View File

@ -25,8 +25,6 @@ import net.corda.flows.CashPaymentFlow
import net.corda.node.driver.DriverBasedTest import net.corda.node.driver.DriverBasedTest
import net.corda.node.driver.driver import net.corda.node.driver.driver
import net.corda.node.services.User import net.corda.node.services.User
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.network.NetworkMapService import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.startFlowPermission import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.services.transactions.SimpleNotaryService
@ -57,9 +55,10 @@ class NodeMonitorModelTest : DriverBasedTest() {
) )
val aliceNodeFuture = startNode("Alice", rpcUsers = listOf(cashUser)) val aliceNodeFuture = startNode("Alice", rpcUsers = listOf(cashUser))
val notaryNodeFuture = startNode("Notary", advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type))) val notaryNodeFuture = startNode("Notary", advertisedServices = setOf(ServiceInfo(SimpleNotaryService.type)))
val aliceNodeHandle = aliceNodeFuture.getOrThrow()
aliceNode = aliceNodeFuture.getOrThrow().nodeInfo val notaryNodeHandle = notaryNodeFuture.getOrThrow()
notaryNode = notaryNodeFuture.getOrThrow().nodeInfo aliceNode = aliceNodeHandle.nodeInfo
notaryNode = notaryNodeHandle.nodeInfo
newNode = { nodeName -> startNode(nodeName).getOrThrow().nodeInfo } newNode = { nodeName -> startNode(nodeName).getOrThrow().nodeInfo }
val monitor = NodeMonitorModel() val monitor = NodeMonitorModel()
@ -70,7 +69,7 @@ class NodeMonitorModelTest : DriverBasedTest() {
vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed() vaultUpdates = monitor.vaultUpdates.bufferUntilSubscribed()
networkMapUpdates = monitor.networkMap.bufferUntilSubscribed() networkMapUpdates = monitor.networkMap.bufferUntilSubscribed()
monitor.register(ArtemisMessagingComponent.toHostAndPort(aliceNode.address), configureTestSSL(), cashUser.username, cashUser.password) monitor.register(aliceNodeHandle.configuration.rpcAddress!!, cashUser.username, cashUser.password)
rpc = monitor.proxyObservable.value!! rpc = monitor.proxyObservable.value!!
runTest() runTest()
} }

View File

@ -52,8 +52,8 @@ class NodeMonitorModel {
* Register for updates to/from a given vault. * Register for updates to/from a given vault.
* TODO provide an unsubscribe mechanism * TODO provide an unsubscribe mechanism
*/ */
fun register(nodeHostAndPort: HostAndPort, sslConfig: SSLConfiguration, username: String, password: String) { fun register(nodeHostAndPort: HostAndPort, username: String, password: String) {
val client = CordaRPCClient(nodeHostAndPort, sslConfig){ val client = CordaRPCClient(nodeHostAndPort){
maxRetryInterval = 10.seconds.toMillis() maxRetryInterval = 10.seconds.toMillis()
} }
client.start(username, password) client.start(username, password)

View File

@ -2,11 +2,12 @@ myLegalName : "Bank A"
nearestCity : "London" nearestCity : "London"
keyStorePassword : "cordacadevpass" keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass" trustStorePassword : "trustpass"
artemisAddress : "localhost:31337" p2pAddress : "localhost:10002"
webAddress : "localhost:31339" rpcAddress : "localhost:10003"
webAddress : "localhost:10004"
extraAdvertisedServiceIds : [ "corda.interest_rates" ] extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : { networkMapService : {
address : "localhost:12345" address : "localhost:10000"
legalName : "Network Map Service" legalName : "Network Map Service"
} }
useHTTPS : false useHTTPS : false

View File

@ -2,11 +2,12 @@ myLegalName : "Bank B"
nearestCity : "London" nearestCity : "London"
keyStorePassword : "cordacadevpass" keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass" trustStorePassword : "trustpass"
artemisAddress : "localhost:31338" p2pAddress : "localhost:10005"
webAddress : "localhost:31340" rpcAddress : "localhost:10006"
webAddress : "localhost:10007"
extraAdvertisedServiceIds : [ "corda.interest_rates" ] extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : { networkMapService : {
address : "localhost:12345" address : "localhost:10000"
legalName : "Network Map Service" legalName : "Network Map Service"
} }
useHTTPS : false useHTTPS : false

View File

@ -2,7 +2,7 @@ myLegalName : "Notary Service"
nearestCity : "London" nearestCity : "London"
keyStorePassword : "cordacadevpass" keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass" trustStorePassword : "trustpass"
artemisAddress : "localhost:12345" p2pAddress : "localhost:10000"
webAddress : "localhost:12346" webAddress : "localhost:10001"
extraAdvertisedServiceIds : [ "corda.notary.validating" ] extraAdvertisedServiceIds : [ "corda.notary.validating" ]
useHTTPS : false useHTTPS : false

View File

@ -14,6 +14,15 @@ API changes:
using strings like "1000.00 USD" when writing. You can use any format supported by ``Amount.parseCurrency`` using strings like "1000.00 USD" when writing. You can use any format supported by ``Amount.parseCurrency``
as input. as input.
Milestone 10
------------
* Configuration:
* Replace ``artemisPort`` with ``p2pPort`` in Gradle configuration
* Replace ``artemisAddress`` with ``p2pAddress`` in node configuration
* Added ``rpcAddress`` in node configuration
Milestone 9.1 Milestone 9.1
------------- -------------

View File

@ -38,8 +38,9 @@ NetworkMapService plus Simple Notary configuration file.
nearestCity : "London" nearestCity : "London"
keyStorePassword : "cordacadevpass" keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass" trustStorePassword : "trustpass"
artemisAddress : "localhost:12345" p2pAddress : "localhost:12345"
webAddress : "localhost:12346" rpcAddress : "localhost:12346"
webAddress : "localhost:12347"
extraAdvertisedServiceIds : [] extraAdvertisedServiceIds : []
useHTTPS : false useHTTPS : false
devMode : true devMode : true
@ -74,13 +75,15 @@ path to the node's base directory.
Currently the defaults in ``/node/src/main/resources/reference.conf`` are as shown in the first example. This is currently Currently the defaults in ``/node/src/main/resources/reference.conf`` are as shown in the first example. This is currently
the only configuration that has been tested, although in the future full support for other storage layers will be validated. the only configuration that has been tested, although in the future full support for other storage layers will be validated.
:artemisAddress: The host and port on which the node is available for protocol operations over ArtemisMQ. :messagingServerAddress: The address of the ArtemisMQ broker instance. If not provided the node will run one locally.
:p2pAddress: The host and port on which the node is available for protocol operations over ArtemisMQ.
.. note:: In practice the ArtemisMQ messaging services bind to all local addresses on the specified port. However, .. note:: In practice the ArtemisMQ messaging services bind to all local addresses on the specified port. However,
note that the host is the included as the advertised entry in the NetworkMapService. As a result the value listed note that the host is the included as the advertised entry in the NetworkMapService. As a result the value listed
here must be externally accessible when running nodes across a cluster of machines. here must be externally accessible when running nodes across a cluster of machines.
:messagingServerAddress: The address of the ArtemisMQ broker instance. If not provided the node will run one locally. :rpcAddress: The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC.
:webAddress: The host and port on which the bundled webserver will listen if it is started. :webAddress: The host and port on which the bundled webserver will listen if it is started.

View File

@ -214,24 +214,27 @@ is a three node example;
name "Controller" name "Controller"
nearestCity "London" nearestCity "London"
advertisedServices = [ "corda.notary.validating" ] advertisedServices = [ "corda.notary.validating" ]
artemisPort 12345 p2pPort 10002
webPort 12346 rpcPort 10003
webPort 10004
cordapps [] cordapps []
} }
node { node {
name "NodeA" name "NodeA"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 31337 p2pPort 10005
webPort 31339 rpcPort 10006
webPort 10007
cordapps [] cordapps []
} }
node { node {
name "NodeB" name "NodeB"
nearestCity "New York" nearestCity "New York"
advertisedServices = [] advertisedServices = []
artemisPort 31338 p2pPort 10008
webPort 31340 rpcPort 10009
webPort 10010
cordapps [] cordapps []
} }
} }

View File

@ -80,16 +80,18 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Notary" name "Notary"
nearestCity "London" nearestCity "London"
advertisedServices = ["corda.notary.validating"] advertisedServices = ["corda.notary.validating"]
artemisPort 10002 p2pPort 10002
webPort 10003 rpcPort 10003
webPort 10004
cordapps = [] cordapps = []
} }
node { node {
name "Alice" name "Alice"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 10004 p2pPort 10005
webPort 10005 rpcPort 10006
webPort 10007
cordapps = [] cordapps = []
rpcUsers = [ rpcUsers = [
['user' : "user", ['user' : "user",

View File

@ -2,7 +2,7 @@ myLegalName : "Notary Service"
nearestCity : "London" nearestCity : "London"
keyStorePassword : "cordacadevpass" keyStorePassword : "cordacadevpass"
trustStorePassword : "trustpass" trustStorePassword : "trustpass"
artemisAddress : "my-network-map:10000" p2pAddress : "my-network-map:10000"
webAddress : "localhost:10001" webAddress : "localhost:10001"
extraAdvertisedServiceIds : [] extraAdvertisedServiceIds : []
useHTTPS : false useHTTPS : false

View File

@ -8,8 +8,9 @@ dataSourceProperties : {
"dataSource.user" : sa "dataSource.user" : sa
"dataSource.password" : "" "dataSource.password" : ""
} }
artemisAddress : "my-corda-node:10002" p2pAddress : "my-corda-node:10002"
webAddress : "localhost:10003" rpcAddress : "my-corda-node:10003"
webAddress : "localhost:10004"
extraAdvertisedServiceIds : [ "corda.interest_rates" ] extraAdvertisedServiceIds : [ "corda.interest_rates" ]
networkMapService : { networkMapService : {
address : "my-network-map:10000" address : "my-network-map:10000"

View File

@ -4,6 +4,15 @@ Release notes
Here are release notes for each snapshot release from M9 onwards. This includes guidance on how to upgrade code from Here are release notes for each snapshot release from M9 onwards. This includes guidance on how to upgrade code from
the previous milestone release. the previous milestone release.
Milestone 10
------------
Important: There are configuration changes in M10 due to the split of the Artemis port into separate P2P and RPC
ports. To upgrade, you *must*:
1. In Gradle build configurations replace any references to ``artemisPort`` with ``p2pPort``.
2. In node configurations replace ``artemisAddress`` with ``p2pAddress``.
Milestone 9 Milestone 9
----------- -----------

View File

@ -235,7 +235,7 @@ To run from IntelliJ:
4. Run ``Bank Of Corda Demo: Run Web Cash Issue`` to request issuance of some cash on behalf of Big Corporation via HTTP 4. Run ``Bank Of Corda Demo: Run Web Cash Issue`` to request issuance of some cash on behalf of Big Corporation via HTTP
.. note:: To verify that the Bank of Corda node is alive and running, navigate to the following URL: .. note:: To verify that the Bank of Corda node is alive and running, navigate to the following URL:
http://localhost:10005/api/bank/date http://localhost:10007/api/bank/date
.. note:: The Bank of Corda node explicitly advertises with a node service type as follows: .. note:: The Bank of Corda node explicitly advertises with a node service type as follows:
``advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer"))))`` ``advertisedServices = setOf(ServiceInfo(ServiceType.corda.getSubType("issuer"))))``
@ -259,8 +259,8 @@ Launch the Explorer application to visualize the issuance and transfer of cash f
Using the following login details: Using the following login details:
- For the Bank of Corda node: localhost / port 10004 / username bankUser / password test - For the Bank of Corda node: localhost / port 10006 / username bankUser / password test
- For the Big Corporation node: localhost / port 10006 / username bigCorpUser / password test - For the Big Corporation node: localhost / port 10009 / username bigCorpUser / password test
See https://docs.corda.net/node-explorer.html for further details on usage. See https://docs.corda.net/node-explorer.html for further details on usage.

View File

@ -42,11 +42,13 @@ An example configuration:
The most important fields regarding network configuration are: The most important fields regarding network configuration are:
* ``artemisAddress``: This specifies a host and port. Note that the address bound will **NOT** be ``my-corda-node``, * ``p2pAddress``: This specifies a host and port to which Artemis will bind for messaging with other nodes. Note that the
but rather ``::`` (all addresses on all interfaces). The hostname specified is the hostname *that must be externally address bound will **NOT** be ``my-corda-node``, but rather ``::`` (all addresses on all network interfaces). The hostname specified
resolvable by other nodes in the network*. In the above configuration this is the resolvable name of a machine in a vpn. is the hostname *that must be externally resolvable by other nodes in the network*. In the above configuration this is the
* ``webAddress``: The address the webserver should bind. Note that the port should be distinct from that of ``artemisAddress`` resolvable name of a machine in a VPN.
if they are on the same machine. * ``rpcAddress``: The address to which Artemis will bind for RPC calls.
* ``webAddress``: The address the webserver should bind. Note that the port must be distinct from that of ``p2pAddress``
and ``rpcAddress`` if they are on the same machine.
* ``networkMapService``: Details of the node running the network map service. If it's this node that's running the service * ``networkMapService``: Details of the node running the network map service. If it's this node that's running the service
then this field must not be specified. then this field must not be specified.

View File

@ -807,16 +807,18 @@ like to deploy for testing. See further details below:
name "Controller" // Artemis name of node to be deployed. name "Controller" // Artemis name of node to be deployed.
nearestCity "London" // For use with the network visualiser. nearestCity "London" // For use with the network visualiser.
advertisedServices = ["corda.notary.validating"] // A list of services you wish the node to offer. advertisedServices = ["corda.notary.validating"] // A list of services you wish the node to offer.
artemisPort 10002 p2pPort 10002
webPort 10003 // Usually 1 higher than the Artemis port. rpcPort 10003 // Usually 1 higher than the messaging port.
webPort 10004 // Usually 1 higher than the RPC port.
cordapps = [] // Add package names of CordaApps. cordapps = [] // Add package names of CordaApps.
} }
node { // Create an additional node. node { // Create an additional node.
name "NodeA" name "NodeA"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 10004 p2pPort 10005
webPort 10005 rpcPort 10006
webPort 10007
cordapps = [] cordapps = []
} }
... ...
@ -875,9 +877,9 @@ You must now edit the configuration file for each node, including the
controller. Open each node's config file (`[nodeName]/node.conf`), and make controller. Open each node's config file (`[nodeName]/node.conf`), and make
the following changes: the following changes:
* Change the artemis address to the machine's ip address (e.g. * Change the Artemis messaging address to the machine's IP address (e.g.
`artemisAddress="10.18.0.166:10006"`) `p2pAddress="10.18.0.166:10006"`)
* Change the network map service details to the ip address of the machine where the * Change the network map service details to the IP address of the machine where the
controller node is running and to its legal name (e.g. `networkMapService.address="10.18.0.166:10002"` and controller node is running and to its legal name (e.g. `networkMapService.address="10.18.0.166:10002"` and
`networkMapService.legalName=controller`) (please note that the controller will not have the `networkMapService` config) `networkMapService.legalName=controller`) (please note that the controller will not have the `networkMapService` config)

View File

@ -92,7 +92,7 @@ class Cordform extends DefaultTask {
Node networkMapNode = getNodeByName(networkMapNodeName) Node networkMapNode = getNodeByName(networkMapNodeName)
nodes.each { nodes.each {
if(it != networkMapNode) { if(it != networkMapNode) {
it.networkMapAddress(networkMapNode.getArtemisAddress(), networkMapNodeName) it.networkMapAddress(networkMapNode.getP2PAddress(), networkMapNodeName)
} }
it.build(directory.toFile()) it.build(directory.toFile())
} }

View File

@ -80,13 +80,23 @@ class Node {
} }
/** /**
* Set the artemis port for this node. * Set the Artemis P2P port for this node.
* *
* @param artemisPort The artemis messaging queue port. * @param p2pPort The Artemis messaging queue port.
*/ */
void artemisPort(Integer artemisPort) { void p2pPort(Integer p2pPort) {
config = config.withValue("artemisAddress", config = config.withValue("p2pAddress",
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$artemisPort".toString())) ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$p2pPort".toString()))
}
/**
* Set the Artemis RPC port for this node.
*
* @param rpcPort The Artemis RPC queue port.
*/
void rpcPort(Integer rpcPort) {
config = config.withValue("rpcAddress",
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$rpcPort".toString()))
} }
/** /**
@ -140,10 +150,10 @@ class Node {
/** /**
* Get the artemis address for this node. * Get the artemis address for this node.
* *
* @return This node's artemis address. * @return This node's P2P address.
*/ */
String getArtemisAddress() { String getP2PAddress() {
return config.getString("artemisAddress") return config.getString("p2pAddress")
} }
/** /**

View File

@ -14,6 +14,9 @@ import org.junit.Test
* Runs the security tests with the attacker pretending to be a node on the network. * Runs the security tests with the attacker pretending to be a node on the network.
*/ */
class MQSecurityAsNodeTest : MQSecurityTest() { class MQSecurityAsNodeTest : MQSecurityTest() {
override fun createAttacker(): SimpleMQClient {
return clientTo(alice.configuration.p2pAddress)
}
override fun startAttacker(attacker: SimpleMQClient) { override fun startAttacker(attacker: SimpleMQClient) {
attacker.start(PEER_USER, PEER_USER) // Login as a peer attacker.start(PEER_USER, PEER_USER) // Login as a peer
@ -26,7 +29,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
@Test @Test
fun `only the node running the broker can login using the special node user`() { fun `only the node running the broker can login using the special node user`() {
val attacker = clientTo(alice.configuration.artemisAddress) val attacker = clientTo(alice.configuration.p2pAddress)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy { assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.start(NODE_USER, NODE_USER) attacker.start(NODE_USER, NODE_USER)
} }
@ -34,7 +37,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
@Test @Test
fun `login as the default cluster user`() { fun `login as the default cluster user`() {
val attacker = clientTo(alice.configuration.artemisAddress) val attacker = clientTo(alice.configuration.p2pAddress)
assertThatExceptionOfType(ActiveMQClusterSecurityException::class.java).isThrownBy { assertThatExceptionOfType(ActiveMQClusterSecurityException::class.java).isThrownBy {
attacker.start(ActiveMQDefaultConfiguration.getDefaultClusterUser(), ActiveMQDefaultConfiguration.getDefaultClusterPassword()) attacker.start(ActiveMQDefaultConfiguration.getDefaultClusterUser(), ActiveMQDefaultConfiguration.getDefaultClusterPassword())
} }
@ -42,9 +45,25 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
@Test @Test
fun `login without a username and password`() { fun `login without a username and password`() {
val attacker = clientTo(alice.configuration.artemisAddress) val attacker = clientTo(alice.configuration.p2pAddress)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy { assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.start() attacker.start()
} }
} }
@Test
fun `login to a non ssl port as a node user`() {
val attacker = clientTo(alice.configuration.rpcAddress!!, sslConfiguration = null)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.start(NODE_USER, NODE_USER, enableSSL = false)
}
}
@Test
fun `login to a non ssl port as a peer user`() {
val attacker = clientTo(alice.configuration.rpcAddress!!, sslConfiguration = null)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.start(PEER_USER, PEER_USER, enableSSL = false) // Login as a peer
}
}
} }

View File

@ -2,14 +2,35 @@ package net.corda.services.messaging
import net.corda.node.services.User import net.corda.node.services.User
import net.corda.testing.messaging.SimpleMQClient import net.corda.testing.messaging.SimpleMQClient
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Test
/** /**
* Runs the security tests with the attacker being a valid RPC user of Alice. * Runs the security tests with the attacker being a valid RPC user of Alice.
*/ */
class MQSecurityAsRPCTest : MQSecurityTest() { class MQSecurityAsRPCTest : MQSecurityTest() {
override fun createAttacker(): SimpleMQClient {
return clientTo(alice.configuration.rpcAddress!!)
}
@Test
fun `send message on logged in user's RPC address`() {
val user1Queue = loginToRPCAndGetClientQueue()
assertSendAttackFails(user1Queue)
}
override val extraRPCUsers = listOf(User("evil", "pass", permissions = emptySet())) override val extraRPCUsers = listOf(User("evil", "pass", permissions = emptySet()))
override fun startAttacker(attacker: SimpleMQClient) { override fun startAttacker(attacker: SimpleMQClient) {
attacker.loginToRPC(extraRPCUsers[0]) attacker.loginToRPC(extraRPCUsers[0])
} }
@Test
fun `login to a ssl port as a RPC user`() {
val attacker = clientTo(alice.configuration.p2pAddress)
assertThatExceptionOfType(ActiveMQSecurityException::class.java).isThrownBy {
attacker.loginToRPC(extraRPCUsers[0], enableSSL = true)
}
}
} }

View File

@ -14,7 +14,6 @@ import net.corda.core.utilities.unwrap
import net.corda.node.internal.Node import net.corda.node.internal.Node
import net.corda.node.services.User import net.corda.node.services.User
import net.corda.node.services.config.SSLConfiguration import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.CLIENTS_PREFIX import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.CLIENTS_PREFIX
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.NETWORK_MAP_QUEUE import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.NETWORK_MAP_QUEUE
@ -24,6 +23,7 @@ import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.PEE
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.RPC_QUEUE_REMOVALS_QUEUE import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.RPC_QUEUE_REMOVALS_QUEUE
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.RPC_REQUESTS_QUEUE import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.RPC_REQUESTS_QUEUE
import net.corda.node.services.messaging.CordaRPCClientImpl import net.corda.node.services.messaging.CordaRPCClientImpl
import net.corda.testing.configureTestSSL
import net.corda.testing.messaging.SimpleMQClient import net.corda.testing.messaging.SimpleMQClient
import net.corda.testing.node.NodeBasedTest import net.corda.testing.node.NodeBasedTest
import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException import org.apache.activemq.artemis.api.core.ActiveMQNonExistentQueueException
@ -49,12 +49,14 @@ abstract class MQSecurityTest : NodeBasedTest() {
@Before @Before
fun start() { fun start() {
alice = startNode("Alice", rpcUsers = extraRPCUsers + rpcUser).getOrThrow() alice = startNode("Alice", rpcUsers = extraRPCUsers + rpcUser).getOrThrow()
attacker = clientTo(alice.configuration.artemisAddress) attacker = createAttacker()
startAttacker(attacker) startAttacker(attacker)
} }
open val extraRPCUsers: List<User> get() = emptyList() open val extraRPCUsers: List<User> get() = emptyList()
abstract fun createAttacker(): SimpleMQClient
abstract fun startAttacker(attacker: SimpleMQClient) abstract fun startAttacker(attacker: SimpleMQClient)
@After @After
@ -112,12 +114,6 @@ abstract class MQSecurityTest : NodeBasedTest() {
assertConsumeAttackFails(user1Queue) assertConsumeAttackFails(user1Queue)
} }
@Test
fun `send message on logged in user's RPC address`() {
val user1Queue = loginToRPCAndGetClientQueue()
assertSendAttackFails(user1Queue)
}
@Test @Test
fun `create queue for valid RPC user`() { fun `create queue for valid RPC user`() {
val user1Queue = "$CLIENTS_PREFIX${rpcUser.username}.rpc.${random63BitValue()}" val user1Queue = "$CLIENTS_PREFIX${rpcUser.username}.rpc.${random63BitValue()}"
@ -152,26 +148,26 @@ abstract class MQSecurityTest : NodeBasedTest() {
assertAllQueueCreationAttacksFail(randomQueue) assertAllQueueCreationAttacksFail(randomQueue)
} }
fun clientTo(target: HostAndPort, config: SSLConfiguration = configureTestSSL()): SimpleMQClient { fun clientTo(target: HostAndPort, sslConfiguration: SSLConfiguration? = configureTestSSL()): SimpleMQClient {
val client = SimpleMQClient(target, config) val client = SimpleMQClient(target, sslConfiguration)
clients += client clients += client
return client return client
} }
fun loginToRPC(target: HostAndPort, rpcUser: User): SimpleMQClient { fun loginToRPC(target: HostAndPort, rpcUser: User, sslConfiguration: SSLConfiguration? = null): SimpleMQClient {
val client = clientTo(target) val client = clientTo(target, sslConfiguration)
client.loginToRPC(rpcUser) client.loginToRPC(rpcUser)
return client return client
} }
fun SimpleMQClient.loginToRPC(rpcUser: User): CordaRPCOps { fun SimpleMQClient.loginToRPC(rpcUser: User, enableSSL: Boolean = false): CordaRPCOps {
start(rpcUser.username, rpcUser.password) start(rpcUser.username, rpcUser.password, enableSSL)
val clientImpl = CordaRPCClientImpl(session, ReentrantLock(), rpcUser.username) val clientImpl = CordaRPCClientImpl(session, ReentrantLock(), rpcUser.username)
return clientImpl.proxyFor(CordaRPCOps::class.java, timeout = 1.seconds) return clientImpl.proxyFor(CordaRPCOps::class.java, timeout = 1.seconds)
} }
fun loginToRPCAndGetClientQueue(): String { fun loginToRPCAndGetClientQueue(): String {
val rpcClient = loginToRPC(alice.configuration.artemisAddress, rpcUser) val rpcClient = loginToRPC(alice.configuration.rpcAddress!!, rpcUser)
val clientQueueQuery = SimpleString("$CLIENTS_PREFIX${rpcUser.username}.rpc.*") val clientQueueQuery = SimpleString("$CLIENTS_PREFIX${rpcUser.username}.rpc.*")
return rpcClient.session.addressQuery(clientQueueQuery).queueNames.single().toString() return rpcClient.session.addressQuery(clientQueueQuery).queueNames.single().toString()
} }

View File

@ -32,7 +32,7 @@ class P2PSecurityTest : NodeBasedTest() {
val incorrectNetworkMapName = random63BitValue().toString() val incorrectNetworkMapName = random63BitValue().toString()
val node = startNode("Bob", configOverrides = mapOf( val node = startNode("Bob", configOverrides = mapOf(
"networkMapService" to mapOf( "networkMapService" to mapOf(
"address" to networkMapNode.configuration.artemisAddress.toString(), "address" to networkMapNode.configuration.p2pAddress.toString(),
"legalName" to incorrectNetworkMapName "legalName" to incorrectNetworkMapName
) )
)) ))
@ -57,7 +57,7 @@ class P2PSecurityTest : NodeBasedTest() {
val config = TestNodeConfiguration( val config = TestNodeConfiguration(
baseDirectory = tempFolder.root.toPath() / legalName, baseDirectory = tempFolder.root.toPath() / legalName,
myLegalName = legalName, myLegalName = legalName,
networkMapService = NetworkMapInfo(networkMapNode.configuration.artemisAddress, networkMapNode.info.legalIdentity.name)) networkMapService = NetworkMapInfo(networkMapNode.configuration.p2pAddress, networkMapNode.info.legalIdentity.name))
config.configureWithDevSSLCertificate() // This creates the node's TLS cert with the CN as the legal name config.configureWithDevSSLCertificate() // This creates the node's TLS cert with the CN as the legal name
return SimpleNode(config).apply { start() } return SimpleNode(config).apply { start() }
} }

View File

@ -1,6 +1,7 @@
@file:JvmName("Corda") @file:JvmName("Corda")
package net.corda.node package net.corda.node
import com.typesafe.config.Config
import com.jcabi.manifests.Manifests import com.jcabi.manifests.Manifests
import com.typesafe.config.ConfigException import com.typesafe.config.ConfigException
import joptsimple.OptionException import joptsimple.OptionException
@ -80,7 +81,9 @@ fun main(args: Array<String>) {
printBasicNodeInfo("Logs can be found in", System.getProperty("log-path")) printBasicNodeInfo("Logs can be found in", System.getProperty("log-path"))
val conf = try { val conf = try {
FullNodeConfiguration(cmdlineOptions.baseDirectory, cmdlineOptions.loadConfig()) val conf = cmdlineOptions.loadConfig()
checkConfigVersion(conf)
FullNodeConfiguration(cmdlineOptions.baseDirectory, conf)
} catch (e: ConfigException) { } catch (e: ConfigException) {
println("Unable to load the configuration file: ${e.rootCause.message}") println("Unable to load the configuration file: ${e.rootCause.message}")
exitProcess(2) exitProcess(2)
@ -110,7 +113,7 @@ fun main(args: Array<String>) {
log.info("VM ${info.vmName} ${info.vmVendor} ${info.vmVersion}") log.info("VM ${info.vmName} ${info.vmVendor} ${info.vmVersion}")
log.info("Machine: ${InetAddress.getLocalHost().hostName}") log.info("Machine: ${InetAddress.getLocalHost().hostName}")
log.info("Working Directory: ${cmdlineOptions.baseDirectory}") log.info("Working Directory: ${cmdlineOptions.baseDirectory}")
log.info("Starting as node on ${conf.artemisAddress}") log.info("Starting as node on ${conf.p2pAddress}")
try { try {
cmdlineOptions.baseDirectory.createDirectories() cmdlineOptions.baseDirectory.createDirectories()
@ -138,6 +141,16 @@ fun main(args: Array<String>) {
exitProcess(0) exitProcess(0)
} }
private fun checkConfigVersion(conf: Config) {
// TODO: Remove this check in future milestone.
if (conf.hasPath("artemisAddress")) {
// artemisAddress has been renamed to p2pAddress in M10.
println("artemisAddress has been renamed to p2pAddress in M10, please upgrade your configuration file and start Corda node again.")
println("Corda will now exit...")
exitProcess(1)
}
}
private fun checkJavaVersion() { private fun checkJavaVersion() {
// Check we're not running a version of Java with a known bug: https://github.com/corda/corda/issues/83 // Check we're not running a version of Java with a known bug: https://github.com/corda/corda/issues/83
try { try {

View File

@ -105,7 +105,7 @@ data class NodeHandle(
val configuration: FullNodeConfiguration, val configuration: FullNodeConfiguration,
val process: Process val process: Process
) { ) {
fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.artemisAddress, configuration) fun rpcClientToNode(): CordaRPCClient = CordaRPCClient(configuration.rpcAddress!!)
} }
sealed class PortAllocation { sealed class PortAllocation {
@ -342,16 +342,18 @@ open class DriverDSL(
override fun startNode(providedName: String?, advertisedServices: Set<ServiceInfo>, override fun startNode(providedName: String?, advertisedServices: Set<ServiceInfo>,
rpcUsers: List<User>, customOverrides: Map<String, Any?>): ListenableFuture<NodeHandle> { rpcUsers: List<User>, customOverrides: Map<String, Any?>): ListenableFuture<NodeHandle> {
val messagingAddress = portAllocation.nextHostAndPort() val p2pAddress = portAllocation.nextHostAndPort()
val apiAddress = portAllocation.nextHostAndPort() val rpcAddress = portAllocation.nextHostAndPort()
val webAddress = portAllocation.nextHostAndPort()
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val name = providedName ?: "${pickA(name)}-${messagingAddress.port}" val name = providedName ?: "${pickA(name)}-${p2pAddress.port}"
val baseDirectory = driverDirectory / name val baseDirectory = driverDirectory / name
val configOverrides = mapOf( val configOverrides = mapOf(
"myLegalName" to name, "myLegalName" to name,
"artemisAddress" to messagingAddress.toString(), "p2pAddress" to p2pAddress.toString(),
"webAddress" to apiAddress.toString(), "rpcAddress" to rpcAddress.toString(),
"webAddress" to webAddress.toString(),
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() }, "extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"networkMapService" to mapOf( "networkMapService" to mapOf(
"address" to networkMapAddress.toString(), "address" to networkMapAddress.toString(),
@ -379,7 +381,8 @@ open class DriverDSL(
val processFuture = startNode(executorService, configuration, quasarJarPath, debugPort) val processFuture = startNode(executorService, configuration, quasarJarPath, debugPort)
registerProcess(processFuture) registerProcess(processFuture)
return processFuture.flatMap { process -> return processFuture.flatMap { process ->
establishRpc(messagingAddress, configuration).flatMap { rpc -> // We continue to use SSL enabled port for RPC when its for node user.
establishRpc(p2pAddress, configuration).flatMap { rpc ->
rpc.waitUntilRegisteredWithNetworkMap().map { rpc.waitUntilRegisteredWithNetworkMap().map {
NodeHandle(rpc.nodeIdentity(), rpc, configuration, process) NodeHandle(rpc.nodeIdentity(), rpc, configuration, process)
} }
@ -465,7 +468,7 @@ open class DriverDSL(
// TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all // TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all
// node port numbers to be shifted, so all demos and docs need to be updated accordingly. // node port numbers to be shifted, so all demos and docs need to be updated accordingly.
"webAddress" to apiAddress, "webAddress" to apiAddress,
"artemisAddress" to networkMapAddress.toString(), "p2pAddress" to networkMapAddress.toString(),
"useTestClock" to useTestClock "useTestClock" to useTestClock
) )
) )
@ -536,7 +539,7 @@ open class DriverDSL(
val process = builder.start() val process = builder.start()
// TODO There is a race condition here. Even though the messaging address is bound it may be the case that // TODO There is a race condition here. Even though the messaging address is bound it may be the case that
// the handlers for the advertised services are not yet registered. Needs rethinking. // the handlers for the advertised services are not yet registered. Needs rethinking.
return addressMustBeBound(executorService, nodeConf.artemisAddress).map { process } return addressMustBeBound(executorService, nodeConf.p2pAddress).map { process }
} }
private fun startWebserver( private fun startWebserver(
@ -554,7 +557,7 @@ open class DriverDSL(
emptyList() emptyList()
val javaArgs = listOf(path) + val javaArgs = listOf(path) +
listOf("-Dname=node-${nodeConf.artemisAddress}-webserver") + debugPortArg + listOf("-Dname=node-${nodeConf.p2pAddress}-webserver") + debugPortArg +
listOf( listOf(
"-cp", classpath, className, "-cp", classpath, className,
"--base-directory", nodeConf.baseDirectory.toString()) "--base-directory", nodeConf.baseDirectory.toString())

View File

@ -27,6 +27,7 @@ import net.corda.node.services.transactions.PersistentUniquenessProvider
import net.corda.node.services.transactions.RaftUniquenessProvider 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.utilities.AddressUtils import net.corda.node.utilities.AddressUtils
import net.corda.node.services.transactions.*
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import org.slf4j.Logger import org.slf4j.Logger
import java.io.RandomAccessFile import java.io.RandomAccessFile
@ -141,9 +142,9 @@ class Node(override val configuration: FullNodeConfiguration,
private fun makeLocalMessageBroker(): HostAndPort { private fun makeLocalMessageBroker(): HostAndPort {
with(configuration) { with(configuration) {
val useHost = tryDetectIfNotPublicHost(artemisAddress.hostText) val useHost = tryDetectIfNotPublicHost(p2pAddress.hostText)
val useAddress = useHost?.let { HostAndPort.fromParts(it, artemisAddress.port) } ?: artemisAddress val useAddress = useHost?.let { HostAndPort.fromParts(it, p2pAddress.port) } ?: p2pAddress
messageBroker = ArtemisMessagingServer(this, useAddress, services.networkMapCache, userService) messageBroker = ArtemisMessagingServer(this, useAddress, rpcAddress, services.networkMapCache, userService)
return useAddress return useAddress
} }
} }

View File

@ -15,7 +15,6 @@ import net.corda.core.div
import net.corda.core.exists import net.corda.core.exists
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import java.net.URL import java.net.URL
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.time.Instant import java.time.Instant
@ -49,6 +48,9 @@ object ConfigHelper {
@Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN") @Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN")
operator fun <T> Config.getValue(receiver: Any, metadata: KProperty<*>): T { operator fun <T> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
if (metadata.returnType.isMarkedNullable && !hasPath(metadata.name)) {
return null as T
}
return when (metadata.returnType.javaType) { return when (metadata.returnType.javaType) {
String::class.java -> getString(metadata.name) as T String::class.java -> getString(metadata.name) as T
Int::class.java -> getInt(metadata.name) as T Int::class.java -> getInt(metadata.name) as T
@ -101,7 +103,7 @@ inline fun <reified T : Any> Config.getListOrElse(path: String, default: Config.
*/ */
fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrustStores(myLegalName) fun NodeConfiguration.configureWithDevSSLCertificate() = configureDevKeyAndTrustStores(myLegalName)
private fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: String) { fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: String) {
certificatesDirectory.createDirectories() certificatesDirectory.createDirectories()
if (!trustStoreFile.exists()) { if (!trustStoreFile.exists()) {
javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStoreFile) javaClass.classLoader.getResourceAsStream("net/corda/node/internal/certificates/cordatruststore.jks").copyTo(trustStoreFile)
@ -113,15 +115,3 @@ private fun SSLConfiguration.configureDevKeyAndTrustStores(myLegalName: String)
X509Utilities.createKeystoreForSSL(keyStoreFile, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName) X509Utilities.createKeystoreForSSL(keyStoreFile, keyStorePassword, keyStorePassword, caKeyStore, "cordacadevkeypass", myLegalName)
} }
} }
// TODO Move this to CoreTestUtils.kt once we can pry this from the explorer
@JvmOverloads
fun configureTestSSL(legalName: String = "Mega Corp."): SSLConfiguration = object : SSLConfiguration {
override val certificatesDirectory = Files.createTempDirectory("certs")
override val keyStorePassword: String get() = "cordacadevpass"
override val trustStorePassword: String get() = "trustpass"
init {
configureDevKeyAndTrustStores(legalName)
}
}

View File

@ -65,15 +65,16 @@ class FullNodeConfiguration(override val baseDirectory: Path, val config: Config
User(username, password, permissions) User(username, password, permissions)
} }
val useHTTPS: Boolean by config val useHTTPS: Boolean by config
val artemisAddress: HostAndPort by config val p2pAddress: HostAndPort by config
val rpcAddress: HostAndPort? by config
val webAddress: HostAndPort by config val webAddress: HostAndPort by config
// TODO This field is slightly redundant as artemisAddress is sufficient to hold the address of the node's MQ broker. // TODO This field is slightly redundant as p2pAddress is sufficient to hold the address of the node's MQ broker.
// Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one // Instead this should be a Boolean indicating whether that broker is an internal one started by the node or an external one
val messagingServerAddress: HostAndPort? by config.getOrElse { null } val messagingServerAddress: HostAndPort? by config
val extraAdvertisedServiceIds: List<String> = config.getListOrElse<String>("extraAdvertisedServiceIds") { emptyList() } val extraAdvertisedServiceIds: List<String> = config.getListOrElse<String>("extraAdvertisedServiceIds") { emptyList() }
val useTestClock: Boolean by config.getOrElse { false } val useTestClock: Boolean by config.getOrElse { false }
val notaryNodeId: Int? by config.getOrElse { null } val notaryNodeId: Int? by config
val notaryNodeAddress: HostAndPort? by config.getOrElse { null } val notaryNodeAddress: HostAndPort? by config
val notaryClusterAddresses: List<HostAndPort> = config val notaryClusterAddresses: List<HostAndPort> = config
.getListOrElse<String>("notaryClusterAddresses") { emptyList() } .getListOrElse<String>("notaryClusterAddresses") { emptyList() }
.map { HostAndPort.fromString(it) } .map { HostAndPort.fromString(it) }

View File

@ -22,7 +22,7 @@ import java.security.KeyStore
/** /**
* The base class for Artemis services that defines shared data structures and SSL transport configuration. * The base class for Artemis services that defines shared data structures and SSL transport configuration.
*/ */
abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() { abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
companion object { companion object {
init { init {
System.setProperty("org.jboss.logging.provider", "slf4j") System.setProperty("org.jboss.logging.provider", "slf4j")
@ -88,6 +88,7 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
fun asPeer(peerIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress { fun asPeer(peerIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress {
return NodeAddress("$PEERS_PREFIX${peerIdentity.toBase58String()}", hostAndPort) return NodeAddress("$PEERS_PREFIX${peerIdentity.toBase58String()}", hostAndPort)
} }
fun asService(serviceIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress { fun asService(serviceIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress {
return NodeAddress("$SERVICES_PREFIX${serviceIdentity.toBase58String()}", hostAndPort) return NodeAddress("$SERVICES_PREFIX${serviceIdentity.toBase58String()}", hostAndPort)
} }
@ -137,7 +138,10 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
} }
} }
protected fun tcpTransport(direction: ConnectionDirection, host: String, port: Int): TransportConfiguration { protected fun tcpTransport(direction: ConnectionDirection, host: String, port: Int, enableSSL: Boolean = true): TransportConfiguration {
// Will throw exception if enableSSL = true but config is missing
require(config != null || !enableSSL) { "SSL configuration cannot be null when SSL is enabled." }
val config = config val config = config
val options = mutableMapOf<String, Any?>( val options = mutableMapOf<String, Any?>(
// Basic TCP target details // Basic TCP target details
@ -151,7 +155,7 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP" TransportConstants.PROTOCOLS_PROP_NAME to "CORE,AMQP"
) )
if (config != null) { if (config != null && enableSSL) {
config.keyStoreFile.expectedOnDefaultFileSystem() config.keyStoreFile.expectedOnDefaultFileSystem()
config.trustStoreFile.expectedOnDefaultFileSystem() config.trustStoreFile.expectedOnDefaultFileSystem()
val tlsOptions = mapOf<String, Any?>( val tlsOptions = mapOf<String, Any?>(

View File

@ -81,7 +81,8 @@ import javax.security.cert.X509Certificate
*/ */
@ThreadSafe @ThreadSafe
class ArtemisMessagingServer(override val config: NodeConfiguration, class ArtemisMessagingServer(override val config: NodeConfiguration,
val myHostPort: HostAndPort, val p2pHostPort: HostAndPort,
val rpcHostPort: HostAndPort?,
val networkMapCache: NetworkMapCache, val networkMapCache: NetworkMapCache,
val userService: RPCUserService) : ArtemisMessagingComponent() { val userService: RPCUserService) : ArtemisMessagingComponent() {
companion object { companion object {
@ -139,7 +140,10 @@ class ArtemisMessagingServer(override 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() activeMQServer.start()
printBasicNodeInfo("Node ${this.config.myLegalName} listening on address", myHostPort.toString()) printBasicNodeInfo("Node ${this.config.myLegalName} listening on address", p2pHostPort.toString())
if (rpcHostPort != null) {
printBasicNodeInfo("Node ${this.config.myLegalName} RPC service listening on address", rpcHostPort.toString())
}
} }
private fun createArtemisConfig(): Configuration = ConfigurationImpl().apply { private fun createArtemisConfig(): Configuration = ConfigurationImpl().apply {
@ -147,7 +151,11 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
bindingsDirectory = (artemisDir / "bindings").toString() bindingsDirectory = (artemisDir / "bindings").toString()
journalDirectory = (artemisDir / "journal").toString() journalDirectory = (artemisDir / "journal").toString()
largeMessagesDirectory = (artemisDir / "large-messages").toString() largeMessagesDirectory = (artemisDir / "large-messages").toString()
acceptorConfigurations = setOf(tcpTransport(Inbound, "0.0.0.0", myHostPort.port)) val acceptors = mutableSetOf(tcpTransport(Inbound, "0.0.0.0", p2pHostPort.port))
if (rpcHostPort != null) {
acceptors.add(tcpTransport(Inbound, "0.0.0.0", rpcHostPort.port, enableSSL = false))
}
acceptorConfigurations = acceptors
// Enable built in message deduplication. Note we still have to do our own as the delayed commits // Enable built in message deduplication. Note we still have to do our own as the delayed commits
// and our own definition of commit mean that the built in deduplication cannot remove all duplicates. // and our own definition of commit mean that the built in deduplication cannot remove all duplicates.
idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess idCacheSize = 2000 // Artemis Default duplicate cache size i.e. a guess
@ -397,8 +405,7 @@ private class VerifyingNettyConnector(configuration: MutableMap<String, Any>?,
threadPool: Executor?, threadPool: Executor?,
scheduledThreadPool: ScheduledExecutorService?, scheduledThreadPool: ScheduledExecutorService?,
protocolManager: ClientProtocolManager?) : protocolManager: ClientProtocolManager?) :
NettyConnector(configuration, handler, listener, closeExecutor, threadPool, scheduledThreadPool, protocolManager) NettyConnector(configuration, handler, listener, closeExecutor, threadPool, scheduledThreadPool, protocolManager) {
{
private val server = configuration?.get(ArtemisMessagingServer::class.java.name) as? ArtemisMessagingServer private val server = configuration?.get(ArtemisMessagingServer::class.java.name) as? ArtemisMessagingServer
private val expectedCommonName = configuration?.get(ArtemisMessagingComponent.VERIFY_PEER_COMMON_NAME) as? String private val expectedCommonName = configuration?.get(ArtemisMessagingComponent.VERIFY_PEER_COMMON_NAME) as? String
@ -480,15 +487,15 @@ class NodeLoginModule : LoginModule {
val username = nameCallback.name ?: throw FailedLoginException("Username not provided") val username = nameCallback.name ?: throw FailedLoginException("Username not provided")
val password = String(passwordCallback.password ?: throw FailedLoginException("Password not provided")) val password = String(passwordCallback.password ?: throw FailedLoginException("Password not provided"))
val certificates = certificateCallback.certificates
log.info("Processing login for $username") log.info("Processing login for $username")
val validatedUser = if (username == PEER_USER || username == NODE_USER) { val validatedUser = when (determineUserRole(certificates, username)) {
val certificates = certificateCallback.certificates ?: throw FailedLoginException("No TLS?") PEER_ROLE -> authenticatePeer(certificates)
authenticateNode(certificates, username) NODE_ROLE -> authenticateNode(certificates)
} else { RPC_ROLE -> authenticateRpcUser(password, username)
// Otherwise assume they're an RPC user else -> throw FailedLoginException("Peer does not belong on our network")
authenticateRpcUser(password, username)
} }
principals += UserPrincipal(validatedUser) principals += UserPrincipal(validatedUser)
@ -496,22 +503,22 @@ class NodeLoginModule : LoginModule {
return loginSucceeded return loginSucceeded
} }
private fun authenticateNode(certificates: Array<X509Certificate>, username: String): String { private fun authenticateNode(certificates: Array<X509Certificate>): String {
val peerCertificate = certificates.first() val peerCertificate = certificates.first()
val role = if (username == NODE_USER) {
if (peerCertificate.publicKey != ourPublicKey) { if (peerCertificate.publicKey != ourPublicKey) {
throw FailedLoginException("Only the node can login as $NODE_USER") throw FailedLoginException("Only the node can login as $NODE_USER")
} }
NODE_ROLE principals += RolePrincipal(NODE_ROLE)
} else { return peerCertificate.subjectDN.name
}
private fun authenticatePeer(certificates: Array<X509Certificate>): String {
val theirRootCAPublicKey = certificates.last().publicKey val theirRootCAPublicKey = certificates.last().publicKey
if (theirRootCAPublicKey != ourRootCAPublicKey) { if (theirRootCAPublicKey != ourRootCAPublicKey) {
throw FailedLoginException("Peer does not belong on our network. Their root CA: $theirRootCAPublicKey") throw FailedLoginException("Peer does not belong on our network. Their root CA: $theirRootCAPublicKey")
} }
PEER_ROLE // This enables the peer to send to our P2P address principals += RolePrincipal(PEER_ROLE)
} return certificates.first().subjectDN.name
principals += RolePrincipal(role)
return peerCertificate.subjectDN.name
} }
private fun authenticateRpcUser(password: String, username: String): String { private fun authenticateRpcUser(password: String, username: String): String {
@ -526,6 +533,18 @@ class NodeLoginModule : LoginModule {
return username return username
} }
private fun determineUserRole(certificates: Array<X509Certificate>?, username: String): String? {
return if (username == PEER_USER || username == NODE_USER) {
certificates ?: throw FailedLoginException("No TLS?")
if (username == PEER_USER) PEER_ROLE else NODE_ROLE
} else if (certificates == null) {
// Assume they're an RPC user if its from a non-ssl connection
RPC_ROLE
} else {
null
}
}
override fun commit(): Boolean { override fun commit(): Boolean {
val result = loginSucceeded val result = loginSucceeded
if (result) { if (result) {

View File

@ -24,10 +24,10 @@ import javax.annotation.concurrent.ThreadSafe
* useful tasks. See the documentation for [proxy] or review the docsite to learn more about how this API works. * useful tasks. See the documentation for [proxy] or review the docsite to learn more about how this API works.
* *
* @param host The hostname and messaging port of the node. * @param host The hostname and messaging port of the node.
* @param config If specified, the SSL configuration to use. If not specified, SSL will be disabled and the node will not be authenticated, nor will RPC traffic be encrypted. * @param config If specified, the SSL configuration to use. If not specified, SSL will be disabled and the node will only be authenticated on non-SSL RPC port, the RPC traffic with not be encrypted when SSL is disabled.
*/ */
@ThreadSafe @ThreadSafe
class CordaRPCClient(val host: HostAndPort, override val config: SSLConfiguration?, val serviceConfigurationOverride: (ServerLocator.() -> Unit)? = null) : Closeable, ArtemisMessagingComponent() { class CordaRPCClient(val host: HostAndPort, override val config: SSLConfiguration? = null, val serviceConfigurationOverride: (ServerLocator.() -> Unit)? = null) : Closeable, ArtemisMessagingComponent() {
private companion object { private companion object {
val log = loggerFor<CordaRPCClient>() val log = loggerFor<CordaRPCClient>()
} }
@ -52,7 +52,7 @@ class CordaRPCClient(val host: HostAndPort, override val config: SSLConfiguratio
check(!running) check(!running)
log.logElapsedTime("Startup") { log.logElapsedTime("Startup") {
checkStorePasswords() checkStorePasswords()
val serverLocator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport(Outbound(), host.hostText, host.port)).apply { val serverLocator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport(Outbound(), host.hostText, host.port, enableSSL = config != null)).apply {
// TODO: Put these in config file or make it user configurable? // TODO: Put these in config file or make it user configurable?
threadPoolMaxSize = 1 threadPoolMaxSize = 1
confirmationWindowSize = 100000 // a guess confirmationWindowSize = 100000 // a guess

View File

@ -48,6 +48,7 @@ class ArtemisMessagingTests {
@Rule @JvmField val temporaryFolder = TemporaryFolder() @Rule @JvmField val temporaryFolder = TemporaryFolder()
val hostAndPort = freeLocalHostAndPort() val hostAndPort = freeLocalHostAndPort()
val rpcHostAndPort = freeLocalHostAndPort()
val topic = "platform.self" val topic = "platform.self"
val identity = generateKeyPair() val identity = generateKeyPair()
@ -232,8 +233,8 @@ class ArtemisMessagingTests {
} }
} }
private fun createMessagingServer(local: HostAndPort = hostAndPort): ArtemisMessagingServer { private fun createMessagingServer(local: HostAndPort = hostAndPort, rpc: HostAndPort = rpcHostAndPort): ArtemisMessagingServer {
return ArtemisMessagingServer(config, local, networkMapCache, userService).apply { return ArtemisMessagingServer(config, local, rpc, networkMapCache, userService).apply {
config.configureWithDevSSLCertificate() config.configureWithDevSSLCertificate()
messagingServer = this messagingServer = this
} }

View File

@ -153,7 +153,7 @@ class NodeWebServer(val config: FullNodeConfiguration) {
try { try {
return connectLocalRpcAsNodeUser() return connectLocalRpcAsNodeUser()
} catch (e: ActiveMQNotConnectedException) { } catch (e: ActiveMQNotConnectedException) {
log.debug("Could not connect to ${config.artemisAddress} due to exception: ", e) log.debug("Could not connect to ${config.p2pAddress} due to exception: ", e)
Thread.sleep(retryDelay) Thread.sleep(retryDelay)
// This error will happen if the server has yet to create the keystore // This error will happen if the server has yet to create the keystore
// Keep the fully qualified package name due to collisions with the Kotlin stdlib // Keep the fully qualified package name due to collisions with the Kotlin stdlib
@ -166,8 +166,8 @@ class NodeWebServer(val config: FullNodeConfiguration) {
} }
private fun connectLocalRpcAsNodeUser(): CordaRPCOps { private fun connectLocalRpcAsNodeUser(): CordaRPCOps {
log.info("Connecting to node at ${config.artemisAddress} as node user") log.info("Connecting to node at ${config.p2pAddress} as node user")
val client = CordaRPCClient(config.artemisAddress, config) val client = CordaRPCClient(config.p2pAddress, config)
client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER) client.start(ArtemisMessagingComponent.NODE_USER, ArtemisMessagingComponent.NODE_USER)
return client.proxy() return client.proxy()
} }

View File

@ -1 +1 @@
gradlePluginsVersion=0.8.3 gradlePluginsVersion=0.10.0

View File

@ -69,8 +69,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Controller" name "Controller"
nearestCity "London" nearestCity "London"
advertisedServices ["corda.notary.validating"] advertisedServices ["corda.notary.validating"]
artemisPort 10002 p2pPort 10002
webPort 10003 rpcPort 10003
webPort 10004
cordapps = [] cordapps = []
rpcUsers = ext.rpcUsers rpcUsers = ext.rpcUsers
} }
@ -78,8 +79,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank A" name "Bank A"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 10004 p2pPort 10005
webPort 10005 rpcPort 10006
webPort 10007
cordapps = [] cordapps = []
rpcUsers = ext.rpcUsers rpcUsers = ext.rpcUsers
} }
@ -87,8 +89,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank B" name "Bank B"
nearestCity "New York" nearestCity "New York"
advertisedServices = [] advertisedServices = []
artemisPort 10006 p2pPort 10008
webPort 10007 rpcPort 10009
webPort 10010
cordapps = [] cordapps = []
rpcUsers = ext.rpcUsers rpcUsers = ext.rpcUsers
} }

View File

@ -28,7 +28,6 @@ fun main(args: Array<String>) {
val parser = OptionParser() val parser = OptionParser()
val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).required() val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).required()
val certsPath = parser.accepts("certificates").withRequiredArg()
val options = try { val options = try {
parser.parse(*args) parser.parse(*args)
} catch (e: Exception) { } catch (e: Exception) {
@ -40,16 +39,16 @@ fun main(args: Array<String>) {
val role = options.valueOf(roleArg)!! val role = options.valueOf(roleArg)!!
when (role) { when (role) {
Role.SENDER -> { Role.SENDER -> {
val host = HostAndPort.fromString("localhost:10004") val host = HostAndPort.fromString("localhost:10006")
println("Connecting to sender node ($host)") println("Connecting to sender node ($host)")
CordaRPCClient(host, sslConfigFor("BankA", options.valueOf(certsPath))).use("demo", "demo") { CordaRPCClient(host).use("demo", "demo") {
sender(this) sender(this)
} }
} }
Role.RECIPIENT -> { Role.RECIPIENT -> {
val host = HostAndPort.fromString("localhost:10006") val host = HostAndPort.fromString("localhost:10009")
println("Connecting to the recipient node ($host)") println("Connecting to the recipient node ($host)")
CordaRPCClient(host, sslConfigFor("BankB", options.valueOf(certsPath))).use("demo", "demo") { CordaRPCClient(host).use("demo", "demo") {
recipient(this) recipient(this)
} }
} }
@ -111,7 +110,6 @@ private fun printHelp(parser: OptionParser) {
parser.printHelpOn(System.out) parser.printHelpOn(System.out)
} }
// TODO: Take this out once we have a dedicated RPC port and allow SSL on it to be optional. // TODO: Take this out once we have a dedicated RPC port and allow SSL on it to be optional.
private fun sslConfigFor(nodename: String, certsPath: String?): SSLConfiguration { private fun sslConfigFor(nodename: String, certsPath: String?): SSLConfiguration {
return object : SSLConfiguration { return object : SSLConfiguration {

View File

@ -66,16 +66,18 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Notary" name "Notary"
nearestCity "London" nearestCity "London"
advertisedServices = ["corda.notary.validating"] advertisedServices = ["corda.notary.validating"]
artemisPort 10002 p2pPort 10002
webPort 10003 rpcPort 10003
webPort 10004
cordapps = [] cordapps = []
} }
node { node {
name "BankOfCorda" name "BankOfCorda"
nearestCity "London" nearestCity "London"
advertisedServices = ["corda.issuer.USD"] advertisedServices = ["corda.issuer.USD"]
artemisPort 10004 p2pPort 10005
webPort 10005 rpcPort 10006
webPort 10007
cordapps = [] cordapps = []
rpcUsers = [ rpcUsers = [
['user' : "bankUser", ['user' : "bankUser",
@ -88,8 +90,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "BigCorporation" name "BigCorporation"
nearestCity "New York" nearestCity "New York"
advertisedServices = [] advertisedServices = []
artemisPort 10006 p2pPort 10008
webPort 10007 rpcPort 10009
webPort 10010
cordapps = [] cordapps = []
rpcUsers = [ rpcUsers = [
['user' : "bigCorpUser", ['user' : "bigCorpUser",

View File

@ -66,13 +66,13 @@ private class BankOfCordaDriver {
when (role) { when (role) {
Role.ISSUE_CASH_RPC -> { Role.ISSUE_CASH_RPC -> {
println("Requesting Cash via RPC ...") println("Requesting Cash via RPC ...")
val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10004")).requestRPCIssue(requestParams) val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10006")).requestRPCIssue(requestParams)
if (result is SignedTransaction) if (result is SignedTransaction)
println("Success!! You transaction receipt is ${result.tx.id}") println("Success!! You transaction receipt is ${result.tx.id}")
} }
Role.ISSUE_CASH_WEB -> { Role.ISSUE_CASH_WEB -> {
println("Requesting Cash via Web ...") println("Requesting Cash via Web ...")
val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10005")).requestWebIssue(requestParams) val result = BankOfCordaClientApi(HostAndPort.fromString("localhost:10007")).requestWebIssue(requestParams)
if (result) if (result)
println("Successfully processed Cash Issue request") println("Successfully processed Cash Issue request")
} }

View File

@ -9,7 +9,6 @@ import net.corda.core.messaging.startFlow
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.flows.IssuerFlow.IssuanceRequester import net.corda.flows.IssuerFlow.IssuanceRequester
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.CordaRPCClient import net.corda.node.services.messaging.CordaRPCClient
import net.corda.testing.http.HttpApi import net.corda.testing.http.HttpApi
@ -26,11 +25,12 @@ class BankOfCordaClientApi(val hostAndPort: HostAndPort) {
val api = HttpApi.fromHostAndPort(hostAndPort, apiRoot) val api = HttpApi.fromHostAndPort(hostAndPort, apiRoot)
return api.postJson("issue-asset-request", params) return api.postJson("issue-asset-request", params)
} }
/** /**
* RPC API * RPC API
*/ */
fun requestRPCIssue(params: IssueRequestParams): SignedTransaction { fun requestRPCIssue(params: IssueRequestParams): SignedTransaction {
val client = CordaRPCClient(hostAndPort, configureTestSSL()) val client = CordaRPCClient(hostAndPort)
// TODO: privileged security controls required // TODO: privileged security controls required
client.start("bankUser", "test") client.start("bankUser", "test")
val proxy = client.proxy() val proxy = client.proxy()

View File

@ -70,8 +70,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Notary" name "Notary"
nearestCity "London" nearestCity "London"
advertisedServices = ["corda.notary.validating", "corda.interest_rates"] advertisedServices = ["corda.notary.validating", "corda.interest_rates"]
artemisPort 10002 p2pPort 10002
webPort 10003 rpcPort 10003
webPort 10004
cordapps = [] cordapps = []
useTestClock true useTestClock true
} }
@ -79,8 +80,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank A" name "Bank A"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 10004 p2pPort 10005
webPort 10005 rpcPort 10006
webPort 10007
cordapps = [] cordapps = []
useTestClock true useTestClock true
} }
@ -88,8 +90,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank B" name "Bank B"
nearestCity "New York" nearestCity "New York"
advertisedServices = [] advertisedServices = []
artemisPort 10006 p2pPort 10008
webPort 10007 rpcPort 10009
webPort 10010
cordapps = [] cordapps = []
useTestClock true useTestClock true
} }

View File

@ -55,7 +55,7 @@ class IRSDemoTest : IntegrationTestCategory {
} }
fun getFixingDateObservable(config: FullNodeConfiguration): BlockingObservable<LocalDate?> { fun getFixingDateObservable(config: FullNodeConfiguration): BlockingObservable<LocalDate?> {
val client = CordaRPCClient(config.artemisAddress, config) val client = CordaRPCClient(config.rpcAddress!!)
client.start("user", "password") client.start("user", "password")
val proxy = client.proxy() val proxy = client.proxy()
val vaultUpdates = proxy.vaultAndUpdates().second val vaultUpdates = proxy.vaultAndUpdates().second

View File

@ -30,9 +30,9 @@ fun main(args: Array<String>) {
val role = options.valueOf(roleArg)!! val role = options.valueOf(roleArg)!!
val value = options.valueOf(valueArg) val value = options.valueOf(valueArg)
when (role) { when (role) {
Role.UploadRates -> IRSDemoClientApi(HostAndPort.fromString("localhost:10003")).runUploadRates() Role.UploadRates -> IRSDemoClientApi(HostAndPort.fromString("localhost:10004")).runUploadRates()
Role.Trade -> IRSDemoClientApi(HostAndPort.fromString("localhost:10005")).runTrade(value) Role.Trade -> IRSDemoClientApi(HostAndPort.fromString("localhost:10007")).runTrade(value)
Role.Date -> IRSDemoClientApi(HostAndPort.fromString("localhost:10007")).runDateChange(value) Role.Date -> IRSDemoClientApi(HostAndPort.fromString("localhost:10010")).runDateChange(value)
} }
} }

View File

@ -89,8 +89,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build', 'generat
name "Party" name "Party"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 10002 p2pPort 10002
webPort 10003 rpcPort 10003
webPort 10004
cordapps = [] cordapps = []
rpcUsers = [['user': "demo", 'password': "demo", 'permissions': [ rpcUsers = [['user': "demo", 'password': "demo", 'permissions': [
'StartFlow.net.corda.notarydemo.flows.DummyIssueAndMove', 'StartFlow.net.corda.notarydemo.flows.DummyIssueAndMove',
@ -101,16 +102,18 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build', 'generat
name "Counterparty" name "Counterparty"
nearestCity "New York" nearestCity "New York"
advertisedServices = [] advertisedServices = []
artemisPort 10004 p2pPort 10005
webPort 10005 rpcPort 10006
webPort 10007
cordapps = [] cordapps = []
} }
node { node {
name "Notary 1" name "Notary 1"
nearestCity "London" nearestCity "London"
advertisedServices = [advertisedNotary] advertisedServices = [advertisedNotary]
artemisPort 10007 p2pPort 10008
webPort 10008 rpcPort 10009
webPort 10010
cordapps = [] cordapps = []
notaryNodePort 11002 notaryNodePort 11002
} }
@ -118,8 +121,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build', 'generat
name "Notary 2" name "Notary 2"
nearestCity "London" nearestCity "London"
advertisedServices = [advertisedNotary] advertisedServices = [advertisedNotary]
artemisPort 10010 p2pPort 10011
webPort 10011 rpcPort 10012
webPort 10013
cordapps = [] cordapps = []
notaryNodePort 11004 notaryNodePort 11004
notaryClusterAddresses = ["localhost:11002"] notaryClusterAddresses = ["localhost:11002"]
@ -128,8 +132,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build', 'generat
name "Notary 3" name "Notary 3"
nearestCity "London" nearestCity "London"
advertisedServices = [advertisedNotary] advertisedServices = [advertisedNotary]
artemisPort 10013 p2pPort 10014
webPort 10014 rpcPort 10015
webPort 10016
cordapps = [] cordapps = []
notaryNodePort 11006 notaryNodePort 11006
notaryClusterAddresses = ["localhost:11002"] notaryClusterAddresses = ["localhost:11002"]

View File

@ -18,10 +18,9 @@ import java.nio.file.Paths
import kotlin.system.exitProcess import kotlin.system.exitProcess
fun main(args: Array<String>) { fun main(args: Array<String>) {
val certPath = getCertPath(args) val host = HostAndPort.fromString("localhost:10003")
val host = HostAndPort.fromString("localhost:10002")
println("Connecting to the recipient node ($host)") println("Connecting to the recipient node ($host)")
CordaRPCClient(host, sslConfigFor("Party", certPath)).use("demo", "demo") { CordaRPCClient(host).use("demo", "demo") {
val api = NotaryDemoClientApi(this) val api = NotaryDemoClientApi(this)
api.startNotarisation() api.startNotarisation()
} }

View File

@ -85,7 +85,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Controller" name "Controller"
nearestCity "London" nearestCity "London"
advertisedServices = ["corda.notary.validating"] advertisedServices = ["corda.notary.validating"]
artemisPort 10002 p2pPort 10002
webPort 10003 webPort 10003
cordapps = [] cordapps = []
} }
@ -93,7 +93,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank A" name "Bank A"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 10004 p2pPort 10004
webPort 10005 webPort 10005
cordapps = [] cordapps = []
} }
@ -101,7 +101,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank B" name "Bank B"
nearestCity "New York" nearestCity "New York"
advertisedServices = [] advertisedServices = []
artemisPort 10006 p2pPort 10006
webPort 10007 webPort 10007
cordapps = [] cordapps = []
} }
@ -109,7 +109,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank C" name "Bank C"
nearestCity "tokyo" nearestCity "tokyo"
advertisedServices = [] advertisedServices = []
artemisPort 10008 p2pPort 10008
webPort 10009 webPort 10009
cordapps = [] cordapps = []
} }

View File

@ -81,16 +81,17 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Notary" name "Notary"
nearestCity "London" nearestCity "London"
advertisedServices = ["corda.notary.validating"] advertisedServices = ["corda.notary.validating"]
artemisPort 10002 p2pPort 10002
webPort 10003 webPort 10004
cordapps = [] cordapps = []
} }
node { node {
name "Bank A" name "Bank A"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 10004 p2pPort 10005
webPort 10005 rpcPort 10006
webPort 10007
cordapps = [] cordapps = []
rpcUsers = ext.rpcUsers rpcUsers = ext.rpcUsers
} }
@ -98,8 +99,9 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "Bank B" name "Bank B"
nearestCity "New York" nearestCity "New York"
advertisedServices = [] advertisedServices = []
artemisPort 10006 p2pPort 10008
webPort 10007 rpcPort 10009
webPort 10010
cordapps = [] cordapps = []
rpcUsers = ext.rpcUsers rpcUsers = ext.rpcUsers
} }
@ -107,8 +109,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
name "BankOfCorda" name "BankOfCorda"
nearestCity "London" nearestCity "London"
advertisedServices = [] advertisedServices = []
artemisPort 10008 p2pPort 10011
webPort 10009 webPort 10012
cordapps = [] cordapps = []
} }
} }

View File

@ -27,7 +27,7 @@ class TraderDemoTest : NodeBasedTest() {
).getOrThrow() ).getOrThrow()
val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map { val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map {
val client = CordaRPCClient(it.configuration.artemisAddress, it.configuration) val client = CordaRPCClient(it.configuration.rpcAddress!!)
client.start(demoUser[0].username, demoUser[0].password).proxy() client.start(demoUser[0].username, demoUser[0].password).proxy()
} }

View File

@ -31,7 +31,6 @@ private class TraderDemo {
fun main(args: Array<String>) { fun main(args: Array<String>) {
val parser = OptionParser() val parser = OptionParser()
val certsPath = parser.accepts("certificates").withRequiredArg()
val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).required() val roleArg = parser.accepts("role").withRequiredArg().ofType(Role::class.java).required()
val options = try { val options = try {
@ -46,13 +45,13 @@ private class TraderDemo {
// will contact the buyer and actually make something happen. // will contact the buyer and actually make something happen.
val role = options.valueOf(roleArg)!! val role = options.valueOf(roleArg)!!
if (role == Role.BUYER) { if (role == Role.BUYER) {
val host = HostAndPort.fromString("localhost:10004") val host = HostAndPort.fromString("localhost:10006")
CordaRPCClient(host, sslConfigFor("BankA", options.valueOf(certsPath))).use("demo", "demo") { CordaRPCClient(host).use("demo", "demo") {
TraderDemoClientApi(this).runBuyer() TraderDemoClientApi(this).runBuyer()
} }
} else { } else {
val host = HostAndPort.fromString("localhost:10006") val host = HostAndPort.fromString("localhost:10009")
CordaRPCClient(host, sslConfigFor("BankB", options.valueOf(certsPath))).use("demo", "demo") { CordaRPCClient(host).use("demo", "demo") {
TraderDemoClientApi(this).runSeller(1000.DOLLARS, "Bank A") TraderDemoClientApi(this).runSeller(1000.DOLLARS, "Bank A")
} }
} }
@ -66,13 +65,4 @@ private class TraderDemo {
""".trimIndent()) """.trimIndent())
parser.printHelpOn(System.out) parser.printHelpOn(System.out)
} }
// TODO: Take this out once we have a dedicated RPC port and allow SSL on it to be optional.
private fun sslConfigFor(nodename: String, certsPath: String?): SSLConfiguration {
return object : SSLConfiguration {
override val keyStorePassword: String = "cordacadevpass"
override val trustStorePassword: String = "trustpass"
override val certificatesDirectory: Path = if (certsPath != null) Paths.get(certsPath) else Paths.get("build") / "nodes" / nodename / "certificates"
}
}
} }

View File

@ -5,6 +5,7 @@ package net.corda.testing
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.typesafe.config.Config
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
@ -19,6 +20,8 @@ import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.node.internal.AbstractNode import net.corda.node.internal.AbstractNode
import net.corda.node.internal.NetworkMapInfo import net.corda.node.internal.NetworkMapInfo
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.config.configureDevKeyAndTrustStores
import net.corda.node.services.statemachine.FlowStateMachineImpl import net.corda.node.services.statemachine.FlowStateMachineImpl
import net.corda.node.utilities.AddOrRemove.ADD import net.corda.node.utilities.AddOrRemove.ADD
import net.corda.testing.node.MockIdentityService import net.corda.testing.node.MockIdentityService
@ -26,6 +29,7 @@ import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import java.net.ServerSocket import java.net.ServerSocket
import java.net.URL import java.net.URL
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
import java.util.* import java.util.*
@ -163,3 +167,16 @@ data class TestNodeConfiguration(
override val exportJMXto: String = "", override val exportJMXto: String = "",
override val devMode: Boolean = true, override val devMode: Boolean = true,
override val certificateSigningService: URL = URL("http://localhost")) : NodeConfiguration override val certificateSigningService: URL = URL("http://localhost")) : NodeConfiguration
fun Config.getHostAndPort(name: String) = HostAndPort.fromString(getString(name))
@JvmOverloads
fun configureTestSSL(legalName: String = "Mega Corp."): SSLConfiguration = object : SSLConfiguration {
override val certificatesDirectory = Files.createTempDirectory("certs")
override val keyStorePassword: String get() = "cordacadevpass"
override val trustStorePassword: String get() = "trustpass"
init {
configureDevKeyAndTrustStores(legalName)
}
}

View File

@ -2,22 +2,22 @@ package net.corda.testing.messaging
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.node.services.config.SSLConfiguration import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.config.configureTestSSL
import net.corda.node.services.messaging.ArtemisMessagingComponent import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Outbound import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Outbound
import net.corda.testing.configureTestSSL
import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.*
/** /**
* As the name suggests this is a simple client for connecting to MQ brokers. * As the name suggests this is a simple client for connecting to MQ brokers.
*/ */
class SimpleMQClient(val target: HostAndPort, class SimpleMQClient(val target: HostAndPort,
override val config: SSLConfiguration = configureTestSSL("SimpleMQClient")) : ArtemisMessagingComponent() { override val config: SSLConfiguration? = configureTestSSL("SimpleMQClient")) : ArtemisMessagingComponent() {
lateinit var sessionFactory: ClientSessionFactory lateinit var sessionFactory: ClientSessionFactory
lateinit var session: ClientSession lateinit var session: ClientSession
lateinit var producer: ClientProducer lateinit var producer: ClientProducer
fun start(username: String? = null, password: String? = null) { fun start(username: String? = null, password: String? = null, enableSSL: Boolean = true) {
val tcpTransport = tcpTransport(Outbound(), target.hostText, target.port) val tcpTransport = tcpTransport(Outbound(), target.hostText, target.port, enableSSL)
val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply { val locator = ActiveMQClient.createServerLocatorWithoutHA(tcpTransport).apply {
isBlockOnNonDurableSend = true isBlockOnNonDurableSend = true
threadPoolMaxSize = 1 threadPoolMaxSize = 1

View File

@ -73,7 +73,7 @@ abstract class NodeBasedTest {
rpcUsers, rpcUsers,
mapOf( mapOf(
"networkMapService" to mapOf( "networkMapService" to mapOf(
"address" to networkMapNode.configuration.artemisAddress.toString(), "address" to networkMapNode.configuration.p2pAddress.toString(),
"legalName" to networkMapNode.info.legalIdentity.name "legalName" to networkMapNode.info.legalIdentity.name
) )
) + configOverrides ) + configOverrides
@ -121,7 +121,8 @@ abstract class NodeBasedTest {
allowMissingConfig = true, allowMissingConfig = true,
configOverrides = mapOf( configOverrides = mapOf(
"myLegalName" to legalName, "myLegalName" to legalName,
"artemisAddress" to freeLocalHostAndPort().toString(), "p2pAddress" to freeLocalHostAndPort().toString(),
"rpcAddress" to freeLocalHostAndPort().toString(),
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() }, "extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
"rpcUsers" to rpcUsers.map { "rpcUsers" to rpcUsers.map {
mapOf( mapOf(

View File

@ -24,14 +24,14 @@ import kotlin.concurrent.thread
* This is a bare-bones node which can only send and receive messages. It doesn't register with a network map service or * This is a bare-bones node which can only send and receive messages. It doesn't register with a network map service or
* any other such task that would make it functionable in a network and thus left to the user to do so manually. * any other such task that would make it functionable in a network and thus left to the user to do so manually.
*/ */
class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeLocalHostAndPort()) : AutoCloseable { class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeLocalHostAndPort(), rpcAddress: HostAndPort = freeLocalHostAndPort()) : AutoCloseable {
private val databaseWithCloseable: Pair<Closeable, Database> = configureDatabase(config.dataSourceProperties) private val databaseWithCloseable: Pair<Closeable, Database> = configureDatabase(config.dataSourceProperties)
val database: Database get() = databaseWithCloseable.second val database: Database get() = databaseWithCloseable.second
val userService = RPCUserServiceImpl(config) val userService = RPCUserServiceImpl(config)
val identity: KeyPair = generateKeyPair() val identity: KeyPair = generateKeyPair()
val executor = ServiceAffinityExecutor(config.myLegalName, 1) val executor = ServiceAffinityExecutor(config.myLegalName, 1)
val broker = ArtemisMessagingServer(config, address, InMemoryNetworkMapCache(), userService) val broker = ArtemisMessagingServer(config, address, rpcAddress, InMemoryNetworkMapCache(), userService)
val networkMapRegistrationFuture: SettableFuture<Unit> = SettableFuture.create<Unit>() val networkMapRegistrationFuture: SettableFuture<Unit> = SettableFuture.create<Unit>()
val net = databaseTransaction(database) { val net = databaseTransaction(database) {
NodeMessagingClient( NodeMessagingClient(

View File

@ -14,7 +14,6 @@ import net.corda.client.model.Models
import net.corda.client.model.observableValue import net.corda.client.model.observableValue
import net.corda.core.contracts.GBP import net.corda.core.contracts.GBP
import net.corda.core.contracts.USD import net.corda.core.contracts.USD
import net.corda.core.messaging.startFlow
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.explorer.model.CordaViewModel import net.corda.explorer.model.CordaViewModel
@ -28,7 +27,6 @@ import net.corda.flows.IssuerFlow.IssuanceRequester
import net.corda.node.driver.PortAllocation import net.corda.node.driver.PortAllocation
import net.corda.node.driver.driver import net.corda.node.driver.driver
import net.corda.node.services.User import net.corda.node.services.User
import net.corda.node.services.messaging.ArtemisMessagingComponent
import net.corda.node.services.startFlowPermission import net.corda.node.services.startFlowPermission
import net.corda.node.services.transactions.SimpleNotaryService import net.corda.node.services.transactions.SimpleNotaryService
import org.apache.commons.lang.SystemUtils import org.apache.commons.lang.SystemUtils
@ -141,7 +139,7 @@ fun main(args: Array<String>) {
val issuerNodeUSD = issuerUSD.get() val issuerNodeUSD = issuerUSD.get()
arrayOf(notaryNode, aliceNode, bobNode, issuerNodeGBP, issuerNodeUSD).forEach { arrayOf(notaryNode, aliceNode, bobNode, issuerNodeGBP, issuerNodeUSD).forEach {
println("${it.nodeInfo.legalIdentity} started on ${ArtemisMessagingComponent.toHostAndPort(it.nodeInfo.address)}") println("${it.nodeInfo.legalIdentity} started on ${it.configuration.rpcAddress}")
} }
val parser = OptionParser("S") val parser = OptionParser("S")

View File

@ -30,9 +30,6 @@ class SettingsModel(path: Path = Paths.get("conf")) : Component(), Observable {
private var username: String by config private var username: String by config
private var reportingCurrency: Currency by config private var reportingCurrency: Currency by config
private var fullscreen: Boolean by config private var fullscreen: Boolean by config
private var certificatesDir: Path by config
private var keyStorePassword: String by config
private var trustStorePassword: String by config
// Create observable Properties. // Create observable Properties.
val reportingCurrencyProperty = writableConfigProperty(SettingsModel::reportingCurrency) val reportingCurrencyProperty = writableConfigProperty(SettingsModel::reportingCurrency)
@ -41,10 +38,6 @@ class SettingsModel(path: Path = Paths.get("conf")) : Component(), Observable {
val portProperty = writableConfigProperty(SettingsModel::port) val portProperty = writableConfigProperty(SettingsModel::port)
val usernameProperty = writableConfigProperty(SettingsModel::username) val usernameProperty = writableConfigProperty(SettingsModel::username)
val fullscreenProperty = writableConfigProperty(SettingsModel::fullscreen) val fullscreenProperty = writableConfigProperty(SettingsModel::fullscreen)
val certificatesDirProperty = writableConfigProperty(SettingsModel::certificatesDir)
// TODO : We should encrypt all passwords in config file.
val keyStorePasswordProperty = writableConfigProperty(SettingsModel::keyStorePassword)
val trustStorePasswordProperty = writableConfigProperty(SettingsModel::trustStorePassword)
init { init {
load() load()

View File

@ -5,16 +5,16 @@ import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleIntegerProperty
import javafx.scene.control.* import javafx.scene.control.*
import javafx.stage.FileChooser
import net.corda.client.fxutils.map import net.corda.client.fxutils.map
import net.corda.client.model.NodeMonitorModel import net.corda.client.model.NodeMonitorModel
import net.corda.client.model.objectProperty import net.corda.client.model.objectProperty
import net.corda.core.exists
import net.corda.explorer.model.SettingsModel import net.corda.explorer.model.SettingsModel
import net.corda.node.services.config.SSLConfiguration import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.config.configureTestSSL
import org.controlsfx.dialog.ExceptionDialog import org.controlsfx.dialog.ExceptionDialog
import tornadofx.* import tornadofx.*
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths
import kotlin.system.exitProcess import kotlin.system.exitProcess
class LoginView : View() { class LoginView : View() {
@ -26,7 +26,6 @@ class LoginView : View() {
private val passwordTextField by fxid<PasswordField>() private val passwordTextField by fxid<PasswordField>()
private val rememberMeCheckBox by fxid<CheckBox>() private val rememberMeCheckBox by fxid<CheckBox>()
private val fullscreenCheckBox by fxid<CheckBox>() private val fullscreenCheckBox by fxid<CheckBox>()
private val certificateButton by fxid<Button>()
private val portProperty = SimpleIntegerProperty() private val portProperty = SimpleIntegerProperty()
private val rememberMe by objectProperty(SettingsModel::rememberMeProperty) private val rememberMe by objectProperty(SettingsModel::rememberMeProperty)
@ -34,9 +33,6 @@ class LoginView : View() {
private val host by objectProperty(SettingsModel::hostProperty) private val host by objectProperty(SettingsModel::hostProperty)
private val port by objectProperty(SettingsModel::portProperty) private val port by objectProperty(SettingsModel::portProperty)
private val fullscreen by objectProperty(SettingsModel::fullscreenProperty) private val fullscreen by objectProperty(SettingsModel::fullscreenProperty)
private val certificatesDir by objectProperty(SettingsModel::certificatesDirProperty)
private val keyStorePasswordProperty by objectProperty(SettingsModel::keyStorePasswordProperty)
private val trustStorePasswordProperty by objectProperty(SettingsModel::trustStorePasswordProperty)
fun login() { fun login() {
val status = Dialog<LoginStatus>().apply { val status = Dialog<LoginStatus>().apply {
@ -46,7 +42,7 @@ class LoginView : View() {
ButtonBar.ButtonData.OK_DONE -> try { ButtonBar.ButtonData.OK_DONE -> try {
root.isDisable = true root.isDisable = true
// TODO : Run this async to avoid UI lockup. // TODO : Run this async to avoid UI lockup.
getModel<NodeMonitorModel>().register(HostAndPort.fromParts(hostTextField.text, portProperty.value), configureSSL(), usernameTextField.text, passwordTextField.text) getModel<NodeMonitorModel>().register(HostAndPort.fromParts(hostTextField.text, portProperty.value), usernameTextField.text, passwordTextField.text)
if (!rememberMe.value) { if (!rememberMe.value) {
username.value = "" username.value = ""
host.value = "" host.value = ""
@ -79,18 +75,6 @@ class LoginView : View() {
if (status != LoginStatus.loggedIn) login() if (status != LoginStatus.loggedIn) login()
} }
private fun configureSSL(): SSLConfiguration {
val sslConfig = object : SSLConfiguration {
override val certificatesDirectory: Path get() = certificatesDir.get()
override val keyStorePassword: String get() = keyStorePasswordProperty.get()
override val trustStorePassword: String get() = trustStorePasswordProperty.get()
}
// TODO : Don't use dev certificates.
return if (sslConfig.keyStoreFile.exists()) sslConfig else configureTestSSL().apply {
alert(Alert.AlertType.WARNING, "", "KeyStore not found in certificates directory.\nDEV certificates will be used by default.")
}
}
init { init {
// Restrict text field to Integer only. // Restrict text field to Integer only.
portTextField.textFormatter = intFormatter().apply { portProperty.bind(this.valueProperty()) } portTextField.textFormatter = intFormatter().apply { portProperty.bind(this.valueProperty()) }
@ -99,44 +83,6 @@ class LoginView : View() {
usernameTextField.textProperty().bindBidirectional(username) usernameTextField.textProperty().bindBidirectional(username)
hostTextField.textProperty().bindBidirectional(host) hostTextField.textProperty().bindBidirectional(host)
portTextField.textProperty().bindBidirectional(port) portTextField.textProperty().bindBidirectional(port)
certificateButton.setOnAction {
Dialog<ButtonType>().apply {
title = "Certificates Settings"
initOwner(root.scene.window)
dialogPane.content = gridpane {
vgap = 10.0
hgap = 5.0
row("Certificates Directory :") {
textfield {
prefWidth = 400.0
textProperty().bind(certificatesDir.map(Path::toString))
isEditable = false
}
button {
graphic = FontAwesomeIconView(FontAwesomeIcon.FOLDER_OPEN_ALT)
maxHeight = Double.MAX_VALUE
setOnAction {
chooseDirectory(owner = dialogPane.scene.window) {
initialDirectoryProperty().bind(certificatesDir.map(Path::toFile))
}?.let {
certificatesDir.set(it.toPath())
}
}
}
}
row("KeyStore Password :") { passwordfield(keyStorePasswordProperty) }
row("TrustStore Password :") { passwordfield(trustStorePasswordProperty) }
}
dialogPane.buttonTypes.addAll(ButtonType.APPLY, ButtonType.CANCEL)
}.showAndWait().get().let {
when (it) {
ButtonType.APPLY -> getModel<SettingsModel>().commit()
// Discard changes.
else -> getModel<SettingsModel>().load()
}
}
}
certificateButton.tooltip("Certificate Configuration")
} }
private enum class LoginStatus { private enum class LoginStatus {

View File

@ -26,23 +26,15 @@
<Label text="Corda Node :" GridPane.halignment="RIGHT"/> <Label text="Corda Node :" GridPane.halignment="RIGHT"/>
<TextField fx:id="hostTextField" promptText="Host" GridPane.columnIndex="1"/> <TextField fx:id="hostTextField" promptText="Host" GridPane.columnIndex="1"/>
<TextField fx:id="portTextField" prefWidth="100" promptText="Port" GridPane.columnIndex="2"/> <TextField fx:id="portTextField" prefWidth="100" promptText="Port" GridPane.columnIndex="2"/>
<Button id="certificateButton" fx:id="certificateButton" GridPane.columnIndex="3" styleClass="certificateButton">
<padding>
<Insets right="6"/>
</padding>
<graphic>
<FontAwesomeIconView styleClass="certificateIcon" glyphName="LOCK" glyphSize="20"/>
</graphic>
</Button>
<Label text="Username :" GridPane.rowIndex="1" GridPane.halignment="RIGHT"/> <Label text="Username :" GridPane.rowIndex="1" GridPane.halignment="RIGHT"/>
<TextField fx:id="usernameTextField" promptText="Username" GridPane.columnIndex="1" <TextField fx:id="usernameTextField" promptText="Username" GridPane.columnIndex="1"
GridPane.columnSpan="3" GridPane.rowIndex="1"/> GridPane.columnSpan="2" GridPane.rowIndex="1"/>
<Label text="Password :" GridPane.rowIndex="2" GridPane.halignment="RIGHT"/> <Label text="Password :" GridPane.rowIndex="2" GridPane.halignment="RIGHT"/>
<PasswordField fx:id="passwordTextField" promptText="Password" GridPane.columnIndex="1" <PasswordField fx:id="passwordTextField" promptText="Password" GridPane.columnIndex="1"
GridPane.columnSpan="3" GridPane.rowIndex="2"/> GridPane.columnSpan="2" GridPane.rowIndex="2"/>
<HBox spacing="20" GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.columnSpan="3"> <HBox spacing="20" GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.columnSpan="2">
<CheckBox fx:id="rememberMeCheckBox" text="Remember me"/> <CheckBox fx:id="rememberMeCheckBox" text="Remember me"/>
<CheckBox fx:id="fullscreenCheckBox" text="Fullscreen mode"/> <CheckBox fx:id="fullscreenCheckBox" text="Fullscreen mode"/>
</HBox> </HBox>

View File

@ -71,8 +71,6 @@ class ConnectionManager(private val username: String, private val jSch: JSch) {
nodeHost: String, nodeHost: String,
remoteMessagingPort: Int, remoteMessagingPort: Int,
localTunnelAddress: HostAndPort, localTunnelAddress: HostAndPort,
certificatesBaseDirectory: Path,
remoteCertificatesDirectory: Path,
rpcUsername: String, rpcUsername: String,
rpcPassword: String rpcPassword: String
): NodeConnection { ): NodeConnection {
@ -87,19 +85,7 @@ class ConnectionManager(private val username: String, private val jSch: JSch) {
session.setPortForwardingL(localTunnelAddress.port, localTunnelAddress.hostText, remoteMessagingPort) session.setPortForwardingL(localTunnelAddress.port, localTunnelAddress.hostText, remoteMessagingPort)
log.info("Tunnel created!") log.info("Tunnel created!")
val certificatesDirectory = certificatesBaseDirectory / nodeHost val connection = NodeConnection(nodeHost, session, localTunnelAddress, rpcUsername, rpcPassword)
val sslKeyStoreFileName = "sslkeystore.jks"
val trustStoreFileName = "truststore.jks"
log.info("Copying server certificates to $certificatesDirectory")
certificatesDirectory.createDirectories()
val channel = session.openChannel("sftp") as ChannelSftp
channel.connect()
channel.get((remoteCertificatesDirectory / sslKeyStoreFileName).toString(), certificatesDirectory.toString())
channel.get((remoteCertificatesDirectory / trustStoreFileName).toString(), certificatesDirectory.toString())
channel.disconnect()
log.info("Certificates copied!")
val connection = NodeConnection(nodeHost, session, localTunnelAddress, certificatesDirectory, rpcUsername, rpcPassword)
connection.startClient() connection.startClient()
return connection return connection
} }
@ -122,7 +108,6 @@ fun <A> connectToNodes(
nodeHostsAndCertificatesPaths: List<Pair<String, Path>>, nodeHostsAndCertificatesPaths: List<Pair<String, Path>>,
remoteMessagingPort: Int, remoteMessagingPort: Int,
tunnelPortAllocation: PortAllocation, tunnelPortAllocation: PortAllocation,
certificatesBaseDirectory: Path,
rpcUsername: String, rpcUsername: String,
rpcPassword: String, rpcPassword: String,
withConnections: (List<NodeConnection>) -> A withConnections: (List<NodeConnection>) -> A
@ -133,8 +118,6 @@ fun <A> connectToNodes(
nodeHost = nodeHostAndCertificatesPath.first, nodeHost = nodeHostAndCertificatesPath.first,
remoteMessagingPort = remoteMessagingPort, remoteMessagingPort = remoteMessagingPort,
localTunnelAddress = tunnelPortAllocation.nextHostAndPort(), localTunnelAddress = tunnelPortAllocation.nextHostAndPort(),
certificatesBaseDirectory = certificatesBaseDirectory,
remoteCertificatesDirectory = nodeHostAndCertificatesPath.second,
rpcUsername = rpcUsername, rpcUsername = rpcUsername,
rpcPassword = rpcPassword rpcPassword = rpcPassword
) )
@ -157,17 +140,9 @@ class NodeConnection(
val hostName: String, val hostName: String,
private val jSchSession: Session, private val jSchSession: Session,
private val localTunnelAddress: HostAndPort, private val localTunnelAddress: HostAndPort,
private val certificatesDirectory: Path,
private val rpcUsername: String, private val rpcUsername: String,
private val rpcPassword: String private val rpcPassword: String
) : Closeable { ) : Closeable {
private val sslConfig = object : SSLConfiguration {
override val certificatesDirectory = this@NodeConnection.certificatesDirectory
override val keyStorePassword: String get() = "cordacadevpass"
override val trustStorePassword: String get() = "trustpass"
}
private var client: CordaRPCClient? = null private var client: CordaRPCClient? = null
private var _proxy: CordaRPCOps? = null private var _proxy: CordaRPCOps? = null
val proxy: CordaRPCOps get() = _proxy ?: throw IllegalStateException("proxy requested, but the client is not running") val proxy: CordaRPCOps get() = _proxy ?: throw IllegalStateException("proxy requested, but the client is not running")
@ -202,7 +177,7 @@ class NodeConnection(
return action() return action()
} finally { } finally {
log.info("Starting new RPC proxy to $hostName, tunnel at $localTunnelAddress") log.info("Starting new RPC proxy to $hostName, tunnel at $localTunnelAddress")
val newClient = CordaRPCClient(localTunnelAddress, sslConfig) val newClient = CordaRPCClient(localTunnelAddress)
// TODO expose these somehow? // TODO expose these somehow?
newClient.start(rpcUsername, rpcPassword) newClient.start(rpcUsername, rpcPassword)
val newProxy = newClient.proxy() val newProxy = newClient.proxy()
@ -213,7 +188,7 @@ class NodeConnection(
fun startClient() { fun startClient() {
log.info("Creating RPC proxy to $hostName, tunnel at $localTunnelAddress") log.info("Creating RPC proxy to $hostName, tunnel at $localTunnelAddress")
val client = CordaRPCClient(localTunnelAddress, sslConfig) val client = CordaRPCClient(localTunnelAddress)
client.start(rpcUsername, rpcPassword) client.start(rpcUsername, rpcPassword)
val proxy = client.proxy() val proxy = client.proxy()
log.info("Proxy created") log.info("Proxy created")

View File

@ -163,7 +163,6 @@ fun runLoadTests(configuration: LoadTestConfiguration, tests: List<Pair<LoadTest
configuration.nodeHosts.map { it to configuration.remoteNodeDirectory / "certificates" }, configuration.nodeHosts.map { it to configuration.remoteNodeDirectory / "certificates" },
configuration.remoteMessagingPort, configuration.remoteMessagingPort,
PortAllocation.Incremental(configuration.localTunnelStartingPort), PortAllocation.Incremental(configuration.localTunnelStartingPort),
configuration.localCertificatesBaseDirectory,
configuration.rpcUsername, configuration.rpcUsername,
configuration.rpcPassword configuration.rpcPassword
) { connections -> ) { connections ->

View File

@ -3,7 +3,7 @@
localCertificatesBaseDirectory = "build/load-test/certificates" localCertificatesBaseDirectory = "build/load-test/certificates"
localTunnelStartingPort = 10000 localTunnelStartingPort = 10000
remoteNodeDirectory = "/opt/corda" remoteNodeDirectory = "/opt/corda"
remoteMessagingPort = 10002 remoteMessagingPort = 10003
remoteSystemdServiceName = "corda" remoteSystemdServiceName = "corda"
rpcUsername = "corda" rpcUsername = "corda"
rpcPassword = "rgb" rpcPassword = "rgb"