mirror of
https://github.com/corda/corda.git
synced 2024-12-20 21:43:14 +00:00
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:
parent
eb21458885
commit
486368d926
@ -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="build/attachment-demo-nodes/Bank B/certificates"" />
|
<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" />
|
||||||
|
@ -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="build/attachment-demo-nodes/Bank A/certificates"" />
|
<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" />
|
||||||
|
@ -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="build/trader-demo-nodes/Bank A/certificates"" />
|
<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" />
|
||||||
|
@ -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="build/trader-demo-nodes/Bank B/certificates"" />
|
<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" />
|
||||||
|
12
build.gradle
12
build.gradle
@ -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 = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
@ -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
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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 []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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() }
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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())
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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) }
|
||||||
|
@ -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?>(
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
gradlePluginsVersion=0.8.3
|
gradlePluginsVersion=0.10.0
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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",
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"]
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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 = []
|
||||||
}
|
}
|
||||||
|
@ -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 = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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(
|
||||||
|
@ -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")
|
||||||
|
@ -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()
|
||||||
|
@ -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 {
|
||||||
|
@ -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>
|
||||||
|
@ -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")
|
||||||
|
@ -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 ->
|
||||||
|
@ -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"
|
Loading…
Reference in New Issue
Block a user