Moved certificates path to node configuration

This commit is contained in:
Patrick Kuo 2016-09-22 13:11:49 +01:00
parent 54991c90a1
commit eee18b55f5
19 changed files with 132 additions and 130 deletions

View File

@ -24,11 +24,12 @@ import kotlin.concurrent.thread
* 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.
*/ */
@ThreadSafe @ThreadSafe
class CordaRPCClient(val host: HostAndPort, certificatesPath: Path) : Closeable, ArtemisMessagingComponent(certificatesPath, sslConfig()) { class CordaRPCClient(val host: HostAndPort, certificatesPath: Path) : Closeable, ArtemisMessagingComponent(sslConfig(certificatesPath)) {
companion object { companion object {
private val rpcLog = LoggerFactory.getLogger("com.r3corda.rpc") private val rpcLog = LoggerFactory.getLogger("com.r3corda.rpc")
private fun sslConfig(): NodeSSLConfiguration = object : NodeSSLConfiguration { private fun sslConfig(certificatesPath: Path): NodeSSLConfiguration = object : NodeSSLConfiguration {
override val certificatesPath: Path = certificatesPath
override val keyStorePassword = "cordacadevpass" override val keyStorePassword = "cordacadevpass"
override val trustStorePassword = "trustpass" override val trustStorePassword = "trustpass"
} }

View File

@ -10,9 +10,9 @@ import com.r3corda.core.node.services.ServiceType
import com.r3corda.node.services.config.FullNodeConfiguration import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.config.NodeConfigurationFromConfig import com.r3corda.node.services.config.NodeConfigurationFromConfig
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.node.services.messaging.ArtemisMessagingComponent import com.r3corda.node.services.messaging.ArtemisMessagingComponent
import com.r3corda.node.services.messaging.ArtemisMessagingServer import com.r3corda.node.services.messaging.ArtemisMessagingServer
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.node.services.network.InMemoryNetworkMapCache import com.r3corda.node.services.network.InMemoryNetworkMapCache
import com.r3corda.node.services.network.NetworkMapService import com.r3corda.node.services.network.NetworkMapService
import com.r3corda.node.services.transactions.NotaryService import com.r3corda.node.services.transactions.NotaryService
@ -335,9 +335,7 @@ class DriverDSL(
) )
) )
) )
val client = NodeMessagingClient( val client = NodeMessagingClient(nodeConfiguration,
Paths.get(baseDirectory, providedName),
nodeConfiguration,
serverHostPort = serverAddress, serverHostPort = serverAddress,
myIdentity = identity.public, myIdentity = identity.public,
executor = AffinityExecutor.ServiceAffinityExecutor(providedName, 1), executor = AffinityExecutor.ServiceAffinityExecutor(providedName, 1),
@ -366,9 +364,7 @@ class DriverDSL(
) )
) )
) )
val server = ArtemisMessagingServer( val server = ArtemisMessagingServer(config,
Paths.get(baseDirectory, name),
config,
portAllocation.nextHostAndPort(), portAllocation.nextHostAndPort(),
networkMapCache networkMapCache
) )

View File

@ -66,7 +66,7 @@ import java.util.concurrent.TimeUnit
// TODO: Where this node is the initial network map service, currently no networkMapService is provided. // TODO: Where this node is the initial network map service, currently no networkMapService is provided.
// In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the // In theory the NodeInfo for the node should be passed in, instead, however currently this is constructed by the
// AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in. // AbstractNode. It should be possible to generate the NodeInfo outside of AbstractNode, so it can be passed in.
abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration, val networkMapService: SingleMessageRecipient?, abstract class AbstractNode(val configuration: NodeConfiguration, val networkMapService: SingleMessageRecipient?,
val advertisedServices: Set<ServiceType>, val platformClock: Clock): SingletonSerializeAsToken() { val advertisedServices: Set<ServiceType>, val platformClock: Clock): SingletonSerializeAsToken() {
companion object { companion object {
val PRIVATE_KEY_FILE_NAME = "identity-private-key" val PRIVATE_KEY_FILE_NAME = "identity-private-key"
@ -162,7 +162,7 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
// Do all of this in a database transaction so anything that might need a connection has one. // Do all of this in a database transaction so anything that might need a connection has one.
initialiseDatabasePersistence() { initialiseDatabasePersistence() {
val storageServices = initialiseStorageService(dir) val storageServices = initialiseStorageService(configuration.basedir)
storage = storageServices.first storage = storageServices.first
checkpointStorage = storageServices.second checkpointStorage = storageServices.second
netMapCache = InMemoryNetworkMapCache() netMapCache = InMemoryNetworkMapCache()
@ -453,8 +453,8 @@ abstract class AbstractNode(val dir: Path, val configuration: NodeConfiguration,
} }
protected fun createNodeDir() { protected fun createNodeDir() {
if (!Files.exists(dir)) { if (!Files.exists(configuration.basedir)) {
Files.createDirectories(dir) Files.createDirectories(configuration.basedir)
} }
} }
} }

View File

@ -10,8 +10,8 @@ import com.r3corda.node.serialization.NodeClock
import com.r3corda.node.services.api.MessagingServiceInternal import com.r3corda.node.services.api.MessagingServiceInternal
import com.r3corda.node.services.config.FullNodeConfiguration import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.node.services.messaging.ArtemisMessagingServer import com.r3corda.node.services.messaging.ArtemisMessagingServer
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.node.services.transactions.PersistentUniquenessProvider import com.r3corda.node.services.transactions.PersistentUniquenessProvider
import com.r3corda.node.servlets.AttachmentDownloadServlet import com.r3corda.node.servlets.AttachmentDownloadServlet
import com.r3corda.node.servlets.Config import com.r3corda.node.servlets.Config
@ -38,7 +38,6 @@ import java.time.Clock
import java.util.* import java.util.*
import javax.management.ObjectName import javax.management.ObjectName
import javax.servlet.* import javax.servlet.*
import javax.servlet.http.HttpServletResponse
import kotlin.concurrent.thread import kotlin.concurrent.thread
class ConfigurationException(message: String) : Exception(message) class ConfigurationException(message: String) : Exception(message)
@ -58,10 +57,10 @@ class ConfigurationException(message: String) : Exception(message)
* @param clock The clock used within the node and by all protocols etc. * @param clock The clock used within the node and by all protocols etc.
* @param messagingServerAddr The address of the Artemis broker instance. If not provided the node will run one locally. * @param messagingServerAddr The address of the Artemis broker instance. If not provided the node will run one locally.
*/ */
class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort, class Node(val p2pAddr: HostAndPort, val webServerAddr: HostAndPort,
configuration: NodeConfiguration, networkMapAddress: SingleMessageRecipient?, configuration: NodeConfiguration, networkMapAddress: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, clock: Clock = NodeClock(), advertisedServices: Set<ServiceType>, clock: Clock = NodeClock(),
val messagingServerAddr: HostAndPort? = null) : AbstractNode(dir, configuration, networkMapAddress, advertisedServices, clock) { val messagingServerAddr: HostAndPort? = null) : AbstractNode(configuration, networkMapAddress, advertisedServices, clock) {
companion object { companion object {
/** The port that is used by default if none is specified. As you know, 31337 is the most elite number. */ /** The port that is used by default if none is specified. As you know, 31337 is the most elite number. */
@JvmField @JvmField
@ -120,14 +119,14 @@ class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort,
override fun makeMessagingService(): MessagingServiceInternal { override fun makeMessagingService(): MessagingServiceInternal {
val serverAddr = messagingServerAddr ?: { val serverAddr = messagingServerAddr ?: {
messageBroker = ArtemisMessagingServer(dir, configuration, p2pAddr, services.networkMapCache) messageBroker = ArtemisMessagingServer(configuration, p2pAddr, services.networkMapCache)
p2pAddr p2pAddr
}() }()
val ops = ServerRPCOps(services) val ops = ServerRPCOps(services)
if (networkMapService != null) { if (networkMapService != null) {
return NodeMessagingClient(dir, configuration, serverAddr, services.storageService.myLegalIdentityKey.public, serverThread, rpcOps = ops) return NodeMessagingClient(configuration, serverAddr, services.storageService.myLegalIdentityKey.public, serverThread, rpcOps = ops)
} else { } else {
return NodeMessagingClient(dir, configuration, serverAddr, null, serverThread, rpcOps = ops) return NodeMessagingClient(configuration, serverAddr, null, serverThread, rpcOps = ops)
} }
} }
@ -172,12 +171,10 @@ class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort,
httpsConfiguration.outputBufferSize = 32768 httpsConfiguration.outputBufferSize = 32768
httpsConfiguration.addCustomizer(SecureRequestCustomizer()) httpsConfiguration.addCustomizer(SecureRequestCustomizer())
val sslContextFactory = SslContextFactory() val sslContextFactory = SslContextFactory()
val keyStorePath = dir.resolve("certificates").resolve("sslkeystore.jks") sslContextFactory.setKeyStorePath(configuration.keyStorePath.toString())
val trustStorePath = dir.resolve("certificates").resolve("truststore.jks")
sslContextFactory.setKeyStorePath(keyStorePath.toString())
sslContextFactory.setKeyStorePassword(configuration.keyStorePassword) sslContextFactory.setKeyStorePassword(configuration.keyStorePassword)
sslContextFactory.setKeyManagerPassword(configuration.keyStorePassword) sslContextFactory.setKeyManagerPassword(configuration.keyStorePassword)
sslContextFactory.setTrustStorePath(trustStorePath.toString()) sslContextFactory.setTrustStorePath(configuration.trustStorePath.toString())
sslContextFactory.setTrustStorePassword(configuration.trustStorePassword) sslContextFactory.setTrustStorePassword(configuration.trustStorePassword)
sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1") sslContextFactory.setExcludeProtocols("SSL.*", "TLSv1", "TLSv1.1")
sslContextFactory.setIncludeProtocols("TLSv1.2") sslContextFactory.setIncludeProtocols("TLSv1.2")
@ -316,7 +313,7 @@ class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort,
// file that we'll do our best to delete on exit. But if we don't, it'll be overwritten next time. If it already // file that we'll do our best to delete on exit. But if we don't, it'll be overwritten next time. If it already
// exists, we try to take the file lock first before replacing it and if that fails it means we're being started // exists, we try to take the file lock first before replacing it and if that fails it means we're being started
// twice with the same directory: that's a user error and we should bail out. // twice with the same directory: that's a user error and we should bail out.
val pidPath = dir.resolve("process-id") val pidPath = configuration.basedir.resolve("process-id")
val file = pidPath.toFile() val file = pidPath.toFile()
if (!file.exists()) { if (!file.exists()) {
file.createNewFile() file.createNewFile()
@ -325,7 +322,7 @@ class Node(dir: Path, val p2pAddr: HostAndPort, val webServerAddr: HostAndPort,
val f = RandomAccessFile(file, "rw") val f = RandomAccessFile(file, "rw")
val l = f.channel.tryLock() val l = f.channel.tryLock()
if (l == null) { if (l == null) {
log.error("It appears there is already a node running with the specified data directory $dir") log.error("It appears there is already a node running with the specified data directory ${configuration.basedir}")
log.error("Shut that other node down and try again. It may have process ID ${file.readText()}") log.error("Shut that other node down and try again. It may have process ID ${file.readText()}")
System.exit(1) System.exit(1)
} }

View File

@ -1,6 +1,7 @@
package com.r3corda.node.services.config package com.r3corda.node.services.config
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.r3corda.core.div
import com.r3corda.core.messaging.SingleMessageRecipient import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.services.ServiceType import com.r3corda.core.node.services.ServiceType
import com.r3corda.node.internal.Node import com.r3corda.node.internal.Node
@ -25,11 +26,14 @@ import kotlin.reflect.jvm.javaType
interface NodeSSLConfiguration { interface NodeSSLConfiguration {
val keyStorePassword: String val keyStorePassword: String
val trustStorePassword: String val trustStorePassword: String
val certificatesPath: Path
// TODO: Move cert paths into this interface as well. val keyStorePath: Path get() = certificatesPath / "sslkeystore.jks"
val trustStorePath: Path get() = certificatesPath / "truststore.jks"
} }
interface NodeConfiguration : NodeSSLConfiguration { interface NodeConfiguration : NodeSSLConfiguration {
val basedir: Path
override val certificatesPath: Path get() = basedir / "certificates"
val myLegalName: String val myLegalName: String
val nearestCity: String val nearestCity: String
val emailAddress: String val emailAddress: String
@ -100,6 +104,7 @@ fun Config.getProperties(path: String): Properties {
} }
class NodeConfigurationFromConfig(val config: Config = ConfigFactory.load()) : NodeConfiguration { class NodeConfigurationFromConfig(val config: Config = ConfigFactory.load()) : NodeConfiguration {
override val basedir: Path by config
override val myLegalName: String by config override val myLegalName: String by config
override val nearestCity: String by config override val nearestCity: String by config
override val emailAddress: String by config override val emailAddress: String by config
@ -112,7 +117,7 @@ class NodeConfigurationFromConfig(val config: Config = ConfigFactory.load()) : N
} }
class FullNodeConfiguration(conf: Config) : NodeConfiguration { class FullNodeConfiguration(conf: Config) : NodeConfiguration {
val basedir: Path by conf override val basedir: Path by conf
override val myLegalName: String by conf override val myLegalName: String by conf
override val nearestCity: String by conf override val nearestCity: String by conf
override val emailAddress: String by conf override val emailAddress: String by conf
@ -141,8 +146,7 @@ class FullNodeConfiguration(conf: Config) : NodeConfiguration {
} }
if (networkMapAddress == null) advertisedServices.add(NetworkMapService.Type) if (networkMapAddress == null) advertisedServices.add(NetworkMapService.Type)
val networkMapMessageAddress: SingleMessageRecipient? = if (networkMapAddress == null) null else NodeMessagingClient.makeNetworkMapAddress(networkMapAddress) val networkMapMessageAddress: SingleMessageRecipient? = if (networkMapAddress == null) null else NodeMessagingClient.makeNetworkMapAddress(networkMapAddress)
return Node(basedir.toAbsolutePath().normalize(), return Node(artemisAddress,
artemisAddress,
webAddress, webAddress,
this, this,
networkMapMessageAddress, networkMapMessageAddress,

View File

@ -27,9 +27,7 @@ import java.security.PublicKey
* @param certificatePath A place where Artemis can stash its message journal and other files. * @param certificatePath A place where Artemis can stash its message journal and other files.
* @param config The config object is used to pass in the passwords for the certificate KeyStore and TrustStore * @param config The config object is used to pass in the passwords for the certificate KeyStore and TrustStore
*/ */
abstract class ArtemisMessagingComponent(val certificatePath: Path, val config: NodeSSLConfiguration) : SingletonSerializeAsToken() { abstract class ArtemisMessagingComponent(val config: NodeSSLConfiguration) : SingletonSerializeAsToken() {
val keyStorePath: Path = certificatePath / "sslkeystore.jks"
val trustStorePath: Path = certificatePath / "truststore.jks"
companion object { companion object {
init { init {
@ -116,10 +114,10 @@ abstract class ArtemisMessagingComponent(val certificatePath: Path, val config:
* unfortunately Artemis tends to bury the exception when the password is wrong. * unfortunately Artemis tends to bury the exception when the password is wrong.
*/ */
fun checkStorePasswords() { fun checkStorePasswords() {
keyStorePath.use { config.keyStorePath.use {
KeyStore.getInstance("JKS").load(it, config.keyStorePassword.toCharArray()) KeyStore.getInstance("JKS").load(it, config.keyStorePassword.toCharArray())
} }
trustStorePath.use { config.trustStorePath.use {
KeyStore.getInstance("JKS").load(it, config.trustStorePassword.toCharArray()) KeyStore.getInstance("JKS").load(it, config.trustStorePassword.toCharArray())
} }
} }
@ -145,10 +143,10 @@ abstract class ArtemisMessagingComponent(val certificatePath: Path, val config:
// and AES encryption // and AES encryption
TransportConstants.SSL_ENABLED_PROP_NAME to true, TransportConstants.SSL_ENABLED_PROP_NAME to true,
TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS", TransportConstants.KEYSTORE_PROVIDER_PROP_NAME to "JKS",
TransportConstants.KEYSTORE_PATH_PROP_NAME to keyStorePath, TransportConstants.KEYSTORE_PATH_PROP_NAME to config.keyStorePath,
TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to config.keyStorePassword, // TODO proper management of keystores and password TransportConstants.KEYSTORE_PASSWORD_PROP_NAME to config.keyStorePassword, // TODO proper management of keystores and password
TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to "JKS", TransportConstants.TRUSTSTORE_PROVIDER_PROP_NAME to "JKS",
TransportConstants.TRUSTSTORE_PATH_PROP_NAME to trustStorePath, TransportConstants.TRUSTSTORE_PATH_PROP_NAME to config.trustStorePath,
TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to config.trustStorePassword, TransportConstants.TRUSTSTORE_PASSWORD_PROP_NAME to config.trustStorePassword,
TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","), TransportConstants.ENABLED_CIPHER_SUITES_PROP_NAME to CIPHER_SUITES.joinToString(","),
TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to "TLSv1.2", TransportConstants.ENABLED_PROTOCOLS_PROP_NAME to "TLSv1.2",
@ -161,16 +159,16 @@ abstract class ArtemisMessagingComponent(val certificatePath: Path, val config:
* the CA certs in Node resources. Then provision KeyStores into certificates folder under node path. * the CA certs in Node resources. Then provision KeyStores into certificates folder under node path.
*/ */
fun configureWithDevSSLCertificate() { fun configureWithDevSSLCertificate() {
Files.createDirectories(certificatePath) Files.createDirectories(config.certificatesPath)
if (!Files.exists(trustStorePath)) { if (!Files.exists(config.trustStorePath)) {
Files.copy(javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks"), Files.copy(javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordatruststore.jks"),
trustStorePath) config.trustStorePath)
} }
if (!Files.exists(keyStorePath)) { if (!Files.exists(config.keyStorePath)) {
val caKeyStore = X509Utilities.loadKeyStore( val caKeyStore = X509Utilities.loadKeyStore(
javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordadevcakeys.jks"), javaClass.classLoader.getResourceAsStream("com/r3corda/node/internal/certificates/cordadevcakeys.jks"),
"cordacadevpass") "cordacadevpass")
X509Utilities.createKeystoreForSSL(keyStorePath, config.keyStorePassword, config.keyStorePassword, caKeyStore, "cordacadevkeypass") X509Utilities.createKeystoreForSSL(config.keyStorePath, config.keyStorePassword, config.keyStorePassword, caKeyStore, "cordacadevkeypass")
} }
} }
} }

View File

@ -4,9 +4,7 @@ import com.google.common.net.HostAndPort
import com.r3corda.core.ThreadBox import com.r3corda.core.ThreadBox
import com.r3corda.core.crypto.AddressFormatException import com.r3corda.core.crypto.AddressFormatException
import com.r3corda.core.crypto.newSecureRandom import com.r3corda.core.crypto.newSecureRandom
import com.r3corda.core.div
import com.r3corda.core.messaging.SingleMessageRecipient import com.r3corda.core.messaging.SingleMessageRecipient
import com.r3corda.core.node.NodeInfo
import com.r3corda.core.node.services.NetworkMapCache import com.r3corda.core.node.services.NetworkMapCache
import com.r3corda.core.utilities.loggerFor import com.r3corda.core.utilities.loggerFor
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
@ -39,10 +37,9 @@ import javax.annotation.concurrent.ThreadSafe
* a fully connected network, trusted network or on localhost. * a fully connected network, trusted network or on localhost.
*/ */
@ThreadSafe @ThreadSafe
class ArtemisMessagingServer(directory: Path, class ArtemisMessagingServer(config: NodeConfiguration,
config: NodeConfiguration,
val myHostPort: HostAndPort, val myHostPort: HostAndPort,
val networkMapCache: NetworkMapCache) : ArtemisMessagingComponent(directory / "certificates", config) { val networkMapCache: NetworkMapCache) : ArtemisMessagingComponent(config) {
companion object { companion object {
val log = loggerFor<ArtemisMessagingServer>() val log = loggerFor<ArtemisMessagingServer>()
} }
@ -119,7 +116,7 @@ class ArtemisMessagingServer(directory: Path,
} }
private fun configureAndStartServer() { private fun configureAndStartServer() {
val config = createArtemisConfig(certificatePath, myHostPort).apply { val config = createArtemisConfig(config.certificatesPath, myHostPort).apply {
securityRoles = mapOf( securityRoles = mapOf(
"#" to setOf(Role("internal", true, true, true, true, true, true, true)) "#" to setOf(Role("internal", true, true, true, true, true, true, true))
) )

View File

@ -2,7 +2,6 @@ package com.r3corda.node.services.messaging
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.r3corda.core.ThreadBox import com.r3corda.core.ThreadBox
import com.r3corda.core.div
import com.r3corda.core.messaging.* import com.r3corda.core.messaging.*
import com.r3corda.core.serialization.SerializedBytes import com.r3corda.core.serialization.SerializedBytes
import com.r3corda.core.serialization.opaque import com.r3corda.core.serialization.opaque
@ -15,7 +14,6 @@ import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.*
import java.nio.file.FileSystems import java.nio.file.FileSystems
import java.nio.file.Path
import java.security.PublicKey import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
@ -46,14 +44,12 @@ import javax.annotation.concurrent.ThreadSafe
* If false the inbox queue will be transient, which is appropriate for UI clients for example. * If false the inbox queue will be transient, which is appropriate for UI clients for example.
*/ */
@ThreadSafe @ThreadSafe
class NodeMessagingClient(directory: Path, class NodeMessagingClient(config: NodeConfiguration,
config: NodeConfiguration,
val serverHostPort: HostAndPort, val serverHostPort: HostAndPort,
val myIdentity: PublicKey?, val myIdentity: PublicKey?,
val executor: AffinityExecutor, val executor: AffinityExecutor,
val persistentInbox: Boolean = true, val persistentInbox: Boolean = true,
private val rpcOps: CordaRPCOps? = null) private val rpcOps: CordaRPCOps? = null) : ArtemisMessagingComponent(config), MessagingServiceInternal {
: ArtemisMessagingComponent(directory / "certificates", config), MessagingServiceInternal {
companion object { companion object {
val log = loggerFor<NodeMessagingClient>() val log = loggerFor<NodeMessagingClient>()
@ -103,7 +99,7 @@ class NodeMessagingClient(directory: Path,
private val handlers = CopyOnWriteArrayList<Handler>() private val handlers = CopyOnWriteArrayList<Handler>()
init { init {
require(directory.fileSystem == FileSystems.getDefault()) { "Artemis only uses the default file system" } require(config.basedir.fileSystem == FileSystems.getDefault()) { "Artemis only uses the default file system" }
} }
fun start() { fun start() {

View File

@ -11,10 +11,8 @@ import com.r3corda.core.minutes
import com.r3corda.core.utilities.loggerFor import com.r3corda.core.utilities.loggerFor
import com.r3corda.node.services.config.FullNodeConfiguration import com.r3corda.node.services.config.FullNodeConfiguration
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.messaging.ArtemisMessagingComponent
import joptsimple.OptionParser import joptsimple.OptionParser
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.security.KeyPair import java.security.KeyPair
import java.security.cert.Certificate import java.security.cert.Certificate
@ -26,23 +24,25 @@ import kotlin.system.exitProcess
* This process will enter a slow polling loop until the request has been approved, and then * This process will enter a slow polling loop until the request has been approved, and then
* the certificate chain will be downloaded and stored in [KeyStore] reside in [certificatePath]. * the certificate chain will be downloaded and stored in [KeyStore] reside in [certificatePath].
*/ */
class CertificateSigner(certificatePath: Path, val nodeConfig: NodeConfiguration, val certService: CertificateSigningService) : ArtemisMessagingComponent(certificatePath, nodeConfig) { class CertificateSigner(val config: NodeConfiguration, val certService: CertificateSigningService) {
companion object { companion object {
val pollInterval = 1.minutes val pollInterval = 1.minutes
val log = loggerFor<CertificateSigner>() val log = loggerFor<CertificateSigner>()
} }
fun buildKeyStore() { fun buildKeyStore() {
val caKeyStore = X509Utilities.loadOrCreateKeyStore(keyStorePath, config.keyStorePassword) Files.createDirectories(config.certificatesPath)
val caKeyStore = X509Utilities.loadOrCreateKeyStore(config.keyStorePath, config.keyStorePassword)
if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) { if (!caKeyStore.containsAlias(CORDA_CLIENT_CA)) {
// No certificate found in key store, create certificate signing request and post request to signing server. // No certificate found in key store, create certificate signing request and post request to signing server.
log.info("No certificate found in key store, creating certificate signing request...") log.info("No certificate found in key store, creating certificate signing request...")
// Create or load key pair from the key store. // Create or load key pair from the key store.
val keyPair = X509Utilities.loadOrCreateKeyPairFromKeyStore(keyStorePath, config.keyStorePassword, val keyPair = X509Utilities.loadOrCreateKeyPairFromKeyStore(config.keyStorePath, config.keyStorePassword,
config.keyStorePassword, CORDA_CLIENT_CA_PRIVATE_KEY) { config.keyStorePassword, CORDA_CLIENT_CA_PRIVATE_KEY) {
X509Utilities.createSelfSignedCACert(nodeConfig.myLegalName) X509Utilities.createSelfSignedCACert(config.myLegalName)
} }
log.info("Submitting certificate signing request to Corda certificate signing server.") log.info("Submitting certificate signing request to Corda certificate signing server.")
val requestId = submitCertificateSigningRequest(keyPair) val requestId = submitCertificateSigningRequest(keyPair)
@ -58,15 +58,15 @@ class CertificateSigner(certificatePath: Path, val nodeConfig: NodeConfiguration
// Assumes certificate chain always starts with client certificate and end with root certificate. // Assumes certificate chain always starts with client certificate and end with root certificate.
caKeyStore.addOrReplaceCertificate(CORDA_CLIENT_CA, certificates.first()) caKeyStore.addOrReplaceCertificate(CORDA_CLIENT_CA, certificates.first())
X509Utilities.saveKeyStore(caKeyStore, keyStorePath, config.keyStorePassword) X509Utilities.saveKeyStore(caKeyStore, config.keyStorePath, config.keyStorePassword)
// Save certificates to trust store. // Save certificates to trust store.
val trustStore = X509Utilities.loadOrCreateKeyStore(trustStorePath, config.trustStorePassword) val trustStore = X509Utilities.loadOrCreateKeyStore(config.trustStorePath, config.trustStorePassword)
// Assumes certificate chain always starts with client certificate and end with root certificate. // Assumes certificate chain always starts with client certificate and end with root certificate.
trustStore.addOrReplaceCertificate(CORDA_ROOT_CA, certificates.last()) trustStore.addOrReplaceCertificate(CORDA_ROOT_CA, certificates.last())
X509Utilities.saveKeyStore(trustStore, trustStorePath, config.trustStorePassword) X509Utilities.saveKeyStore(trustStore, config.trustStorePath, config.trustStorePassword)
} else { } else {
log.trace("Certificate already exists, exiting certificate signer...") log.trace("Certificate already exists, exiting certificate signer...")
} }
@ -96,10 +96,10 @@ class CertificateSigner(certificatePath: Path, val nodeConfig: NodeConfiguration
* @return Request ID return from the server. * @return Request ID return from the server.
*/ */
private fun submitCertificateSigningRequest(keyPair: KeyPair): String { private fun submitCertificateSigningRequest(keyPair: KeyPair): String {
val requestIdStore = certificatePath / "certificate-request-id.txt" val requestIdStore = config.certificatesPath / "certificate-request-id.txt"
// Retrieve request id from file if exists, else post a request to server. // Retrieve request id from file if exists, else post a request to server.
return if (!Files.exists(requestIdStore)) { return if (!Files.exists(requestIdStore)) {
val request = X509Utilities.createCertificateSigningRequest(nodeConfig.myLegalName, nodeConfig.nearestCity, nodeConfig.emailAddress, keyPair) val request = X509Utilities.createCertificateSigningRequest(config.myLegalName, config.nearestCity, config.emailAddress, keyPair)
// Post request to signing server via http. // Post request to signing server via http.
val requestId = certService.submitRequest(request) val requestId = certService.submitRequest(request)
// Persists request ID to file in case of node shutdown. // Persists request ID to file in case of node shutdown.
@ -128,6 +128,6 @@ fun main(args: Array<String>) {
val configFile = if (cmdlineOptions.has(ParamsSpec.configFileArg)) Paths.get(cmdlineOptions.valueOf(ParamsSpec.configFileArg)) else null val configFile = if (cmdlineOptions.has(ParamsSpec.configFileArg)) Paths.get(cmdlineOptions.valueOf(ParamsSpec.configFileArg)) else null
val conf = FullNodeConfiguration(NodeConfiguration.loadConfig(baseDirectoryPath, configFile, allowMissingConfig = true)) val conf = FullNodeConfiguration(NodeConfiguration.loadConfig(baseDirectoryPath, configFile, allowMissingConfig = true))
// TODO: Use HTTPS instead // TODO: Use HTTPS instead
CertificateSigner(baseDirectoryPath / "certificate", conf, HTTPCertificateSigningService(conf.certificateSigningService)).buildKeyStore() CertificateSigner(conf, HTTPCertificateSigningService(conf.certificateSigningService)).buildKeyStore()
} }

View File

@ -88,9 +88,9 @@ class AttachmentTests {
fun `malicious response`() { fun `malicious response`() {
// Make a node that doesn't do sanity checking at load time. // Make a node that doesn't do sanity checking at load time.
val n0 = network.createNode(null, -1, object : MockNetwork.Factory { val n0 = network.createNode(null, -1, object : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode {
return object : MockNetwork.MockNode(dir, config, network, networkMapAddr, advertisedServices, id, keyPair) { return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, keyPair) {
override fun start(): MockNetwork.MockNode { override fun start(): MockNetwork.MockNode {
super.start() super.start()
(storage.attachments as NodeAttachmentService).checkAttachmentsOnLoad = false (storage.attachments as NodeAttachmentService).checkAttachmentsOnLoad = false

View File

@ -154,9 +154,9 @@ class TwoPartyTradeProtocolTests {
// ... bring the node back up ... the act of constructing the SMM will re-register the message handlers // ... bring the node back up ... the act of constructing the SMM will re-register the message handlers
// that Bob was waiting on before the reboot occurred. // that Bob was waiting on before the reboot occurred.
bobNode = net.createNode(networkMapAddr, bobAddr.id, object : MockNetwork.Factory { bobNode = net.createNode(networkMapAddr, bobAddr.id, object : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode {
return MockNetwork.MockNode(dir, config, network, networkMapAddr, advertisedServices, bobAddr.id, BOB_KEY) return MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, bobAddr.id, BOB_KEY)
} }
}, true, BOB.name, BOB_KEY) }, true, BOB.name, BOB_KEY)
@ -184,9 +184,9 @@ class TwoPartyTradeProtocolTests {
private fun makeNodeWithTracking(networkMapAddr: SingleMessageRecipient?, name: String, keyPair: KeyPair): MockNetwork.MockNode { private fun makeNodeWithTracking(networkMapAddr: SingleMessageRecipient?, name: String, keyPair: KeyPair): MockNetwork.MockNode {
// Create a node in the mock network ... // Create a node in the mock network ...
return net.createNode(networkMapAddr, -1, object : MockNetwork.Factory { return net.createNode(networkMapAddr, -1, object : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode {
return object : MockNetwork.MockNode(dir, config, network, networkMapAddr, advertisedServices, id, keyPair) { return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, keyPair) {
// That constructs the storage service object in a customised way ... // That constructs the storage service object in a customised way ...
override fun constructStorageService(attachments: NodeAttachmentService, override fun constructStorageService(attachments: NodeAttachmentService,
transactionStorage: TransactionStorage, transactionStorage: TransactionStorage,

View File

@ -5,17 +5,19 @@ import com.r3corda.core.crypto.generateKeyPair
import com.r3corda.core.messaging.Message import com.r3corda.core.messaging.Message
import com.r3corda.core.node.services.DEFAULT_SESSION_ID import com.r3corda.core.node.services.DEFAULT_SESSION_ID
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.node.services.messaging.ArtemisMessagingServer import com.r3corda.node.services.messaging.ArtemisMessagingServer
import com.r3corda.node.services.messaging.NodeMessagingClient
import com.r3corda.node.services.network.InMemoryNetworkMapCache import com.r3corda.node.services.network.InMemoryNetworkMapCache
import com.r3corda.node.utilities.AffinityExecutor import com.r3corda.node.utilities.AffinityExecutor
import com.r3corda.testing.freeLocalHostAndPort import com.r3corda.testing.freeLocalHostAndPort
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.After import org.junit.After
import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.net.ServerSocket import java.net.ServerSocket
import java.nio.file.Path
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.TimeUnit.MILLISECONDS
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -29,25 +31,30 @@ class ArtemisMessagingTests {
val topic = "platform.self" val topic = "platform.self"
val identity = generateKeyPair() val identity = generateKeyPair()
// TODO: create a base class that provides a default implementation lateinit var config: NodeConfiguration
val config = object : NodeConfiguration {
override val myLegalName: String = "me"
override val nearestCity: String = "London"
override val emailAddress: String = ""
override val devMode: Boolean = true
override val exportJMXto: String = ""
override val keyStorePassword: String = "testpass"
override val trustStorePassword: String = "trustpass"
override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0)
}
var messagingClient: NodeMessagingClient? = null var messagingClient: NodeMessagingClient? = null
var messagingServer: ArtemisMessagingServer? = null var messagingServer: ArtemisMessagingServer? = null
val networkMapCache = InMemoryNetworkMapCache() val networkMapCache = InMemoryNetworkMapCache()
@Before
fun setUp() {
// TODO: create a base class that provides a default implementation
config = object : NodeConfiguration {
override val basedir: Path = temporaryFolder.newFolder().toPath()
override val myLegalName: String = "me"
override val nearestCity: String = "London"
override val emailAddress: String = ""
override val devMode: Boolean = true
override val exportJMXto: String = ""
override val keyStorePassword: String = "testpass"
override val trustStorePassword: String = "trustpass"
override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0)
}
}
@After @After
fun cleanUp() { fun cleanUp() {
messagingClient?.stop() messagingClient?.stop()
@ -111,14 +118,14 @@ class ArtemisMessagingTests {
} }
private fun createMessagingClient(server: HostAndPort = hostAndPort): NodeMessagingClient { private fun createMessagingClient(server: HostAndPort = hostAndPort): NodeMessagingClient {
return NodeMessagingClient(temporaryFolder.newFolder().toPath(), config, server, identity.public, AffinityExecutor.SAME_THREAD).apply { return NodeMessagingClient(config, server, identity.public, AffinityExecutor.SAME_THREAD).apply {
configureWithDevSSLCertificate() configureWithDevSSLCertificate()
messagingClient = this messagingClient = this
} }
} }
private fun createMessagingServer(local: HostAndPort = hostAndPort): ArtemisMessagingServer { private fun createMessagingServer(local: HostAndPort = hostAndPort): ArtemisMessagingServer {
return ArtemisMessagingServer(temporaryFolder.newFolder().toPath(), config, local, networkMapCache).apply { return ArtemisMessagingServer(config, local, networkMapCache).apply {
configureWithDevSSLCertificate() configureWithDevSSLCertificate()
messagingServer = this messagingServer = this
} }

View File

@ -59,9 +59,9 @@ class PersistentNetworkMapServiceTest : AbstractNetworkMapServiceTest() {
} }
private object NodeFactory : MockNetwork.Factory { private object NodeFactory : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode {
return object : MockNetwork.MockNode(dir, config, network, networkMapAddr, advertisedServices, id, keyPair) { return object : MockNetwork.MockNode(config, network, networkMapAddr, advertisedServices, id, keyPair) {
override fun makeNetworkMapService() { override fun makeNetworkMapService() {
inNodeNetworkMapService = SwizzleNetworkMapService(services) inNodeNetworkMapService = SwizzleNetworkMapService(services)

View File

@ -12,6 +12,7 @@ import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -35,13 +36,9 @@ class CertificateSignerTest {
on { retrieveCertificates(eq(id)) }.then { certs } on { retrieveCertificates(eq(id)) }.then { certs }
} }
val keyStore = tempFolder.root.toPath().resolve("sslkeystore.jks")
val tmpTrustStore = tempFolder.root.toPath().resolve("truststore.jks")
assertFalse(Files.exists(keyStore))
assertFalse(Files.exists(tmpTrustStore))
val config = object : NodeConfiguration { val config = object : NodeConfiguration {
override val basedir: Path = tempFolder.root.toPath()
override val myLegalName: String = "me" override val myLegalName: String = "me"
override val nearestCity: String = "London" override val nearestCity: String = "London"
override val emailAddress: String = "" override val emailAddress: String = ""
@ -52,12 +49,15 @@ class CertificateSignerTest {
override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0) override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0)
} }
CertificateSigner(tempFolder.root.toPath(), config, certService).buildKeyStore() assertFalse(Files.exists(config.keyStorePath))
assertFalse(Files.exists(config.trustStorePath))
assertTrue(Files.exists(keyStore)) CertificateSigner(config, certService).buildKeyStore()
assertTrue(Files.exists(tmpTrustStore))
X509Utilities.loadKeyStore(keyStore, config.keyStorePassword).run { assertTrue(Files.exists(config.keyStorePath))
assertTrue(Files.exists(config.trustStorePath))
X509Utilities.loadKeyStore(config.keyStorePath, config.keyStorePassword).run {
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY)) assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY))
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA)) assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_CA))
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
@ -66,7 +66,7 @@ class CertificateSignerTest {
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY)) assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY))
} }
X509Utilities.loadKeyStore(tmpTrustStore, config.trustStorePassword).run { X509Utilities.loadKeyStore(config.trustStorePath, config.trustStorePassword).run {
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY)) assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY))
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA)) assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_CA))
assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA)) assertFalse(containsAlias(X509Utilities.CORDA_INTERMEDIATE_CA))
@ -75,7 +75,7 @@ class CertificateSignerTest {
assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY)) assertFalse(containsAlias(X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY))
} }
assertEquals(id, Files.readAllLines(tempFolder.root.toPath() / "certificate-request-id.txt").first()) assertEquals(id, Files.readAllLines(config.certificatesPath / "certificate-request-id.txt").first())
} }
} }

View File

@ -321,7 +321,7 @@ private fun setup(params: CliParams.SetupNode): Int {
val configFile = params.dir.resolve("config") val configFile = params.dir.resolve("config")
val config = loadConfigFile(params.dir, configFile, params.defaultLegalName) val config = loadConfigFile(params.dir, configFile, params.defaultLegalName)
if (!Files.exists(params.dir.resolve(AbstractNode.PUBLIC_IDENTITY_FILE_NAME))) { if (!Files.exists(params.dir.resolve(AbstractNode.PUBLIC_IDENTITY_FILE_NAME))) {
createIdentities(params, config) createIdentities(config)
} }
return 0 return 0
} }
@ -407,7 +407,7 @@ private fun startNode(params: CliParams.RunNode, networkMap: SingleMessageRecipi
} }
val node = logElapsedTime("Node startup", log) { val node = logElapsedTime("Node startup", log) {
Node(params.dir, params.networkAddress, params.apiAddress, config, networkMapId, advertisedServices, DemoClock()).setup().start() Node(params.networkAddress, params.apiAddress, config, networkMapId, advertisedServices, DemoClock()).setup().start()
} }
return node return node
@ -459,9 +459,9 @@ private fun loadConfigFile(baseDir: Path, configFile: Path, defaultLegalName: St
return NodeConfigurationFromConfig(NodeConfiguration.loadConfig(baseDir, configFileOverride = configFile)) return NodeConfigurationFromConfig(NodeConfiguration.loadConfig(baseDir, configFileOverride = configFile))
} }
private fun createIdentities(params: CliParams.SetupNode, nodeConf: NodeConfiguration) { private fun createIdentities(nodeConf: NodeConfiguration) {
val mockNetwork = MockNetwork(false) val mockNetwork = MockNetwork(false)
val node = MockNetwork.MockNode(params.dir, nodeConf, mockNetwork, null, setOf(NetworkMapService.Type, SimpleNotaryService.Type), 0, null) val node = MockNetwork.MockNode(nodeConf, mockNetwork, null, setOf(NetworkMapService.Type, SimpleNotaryService.Type), 0, null)
node.start() node.start()
node.stop() node.stop()
} }

View File

@ -18,6 +18,7 @@ import joptsimple.OptionParser
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.math.BigDecimal import java.math.BigDecimal
import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.util.* import java.util.*
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -61,7 +62,7 @@ fun main(args: Array<String>) {
// TODO: create a base class that provides a default implementation // TODO: create a base class that provides a default implementation
val config = object : NodeConfiguration { val config = object : NodeConfiguration {
override val basedir: Path = dir
override val myLegalName: String = "Rate fix demo node" override val myLegalName: String = "Rate fix demo node"
override val nearestCity: String = "Atlantis" override val nearestCity: String = "Atlantis"
override val emailAddress: String = "" override val emailAddress: String = ""
@ -76,7 +77,7 @@ fun main(args: Array<String>) {
val apiAddr = HostAndPort.fromParts(myNetAddr.hostText, myNetAddr.port + 1) val apiAddr = HostAndPort.fromParts(myNetAddr.hostText, myNetAddr.port + 1)
val node = logElapsedTime("Node startup") { Node(dir, myNetAddr, apiAddr, config, networkMapAddr, val node = logElapsedTime("Node startup") { Node(myNetAddr, apiAddr, config, networkMapAddr,
advertisedServices, DemoClock()).setup().start() } advertisedServices, DemoClock()).setup().start() }
node.networkMapRegistrationFuture.get() node.networkMapRegistrationFuture.get()
val notaryNode = node.services.networkMapCache.notaryNodes[0] val notaryNode = node.services.networkMapCache.notaryNodes[0]

View File

@ -140,7 +140,7 @@ fun main(args: Array<String>) {
// And now construct then start the node object. It takes a little while. // And now construct then start the node object. It takes a little while.
val node = logElapsedTime("Node startup", log) { val node = logElapsedTime("Node startup", log) {
Node(directory, myNetAddr, apiNetAddr, config, networkMapId, advertisedServices).setup().start() Node(myNetAddr, apiNetAddr, config, networkMapId, advertisedServices).setup().start()
} }
// What happens next depends on the role. The buyer sits around waiting for a trade to start. The seller role // What happens next depends on the role. The buyer sits around waiting for a trade to start. The seller role

View File

@ -45,22 +45,22 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
// This puts together a mock network of SimulatedNodes. // This puts together a mock network of SimulatedNodes.
open class SimulatedNode(dir: Path, config: NodeConfiguration, mockNet: MockNetwork, networkMapAddress: SingleMessageRecipient?, open class SimulatedNode(config: NodeConfiguration, mockNet: MockNetwork, networkMapAddress: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?) : MockNetwork.MockNode(dir, config, mockNet, networkMapAddress, advertisedServices, id, keyPair) { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?) : MockNetwork.MockNode(config, mockNet, networkMapAddress, advertisedServices, id, keyPair) {
override fun findMyLocation(): PhysicalLocation? = CityDatabase[configuration.nearestCity] override fun findMyLocation(): PhysicalLocation? = CityDatabase[configuration.nearestCity]
} }
inner class BankFactory : MockNetwork.Factory { inner class BankFactory : MockNetwork.Factory {
var counter = 0 var counter = 0
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode {
val letter = 'A' + counter val letter = 'A' + counter
val city = bankLocations[counter++ % bankLocations.size] val city = bankLocations[counter++ % bankLocations.size]
// TODO: create a base class that provides a default implementation // TODO: create a base class that provides a default implementation
val cfg = object : NodeConfiguration { val cfg = object : NodeConfiguration {
override val basedir: Path = config.basedir
// TODO: Set this back to "Bank of $city" after video day. // TODO: Set this back to "Bank of $city" after video day.
override val myLegalName: String = "Bank $letter" override val myLegalName: String = "Bank $letter"
override val nearestCity: String = city override val nearestCity: String = city
@ -71,7 +71,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
override val trustStorePassword: String = "trustpass" override val trustStorePassword: String = "trustpass"
override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0) override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0)
} }
return SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, keyPair)
} }
fun createAll(): List<SimulatedNode> = bankLocations. fun createAll(): List<SimulatedNode> = bankLocations.
@ -81,12 +81,13 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
val bankFactory = BankFactory() val bankFactory = BankFactory()
object NetworkMapNodeFactory : MockNetwork.Factory { object NetworkMapNodeFactory : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, override fun create(config: NodeConfiguration, network: MockNetwork,
networkMapAddr: SingleMessageRecipient?, advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode { networkMapAddr: SingleMessageRecipient?, advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode {
require(advertisedServices.contains(NetworkMapService.Type)) require(advertisedServices.contains(NetworkMapService.Type))
// TODO: create a base class that provides a default implementation // TODO: create a base class that provides a default implementation
val cfg = object : NodeConfiguration { val cfg = object : NodeConfiguration {
override val basedir: Path = config.basedir
override val myLegalName: String = "Network coordination center" override val myLegalName: String = "Network coordination center"
override val nearestCity: String = "Amsterdam" override val nearestCity: String = "Amsterdam"
override val emailAddress: String = "" override val emailAddress: String = ""
@ -97,17 +98,18 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0) override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0)
} }
return object : SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) {} return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, keyPair) {}
} }
} }
object NotaryNodeFactory : MockNetwork.Factory { object NotaryNodeFactory : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode {
require(advertisedServices.contains(SimpleNotaryService.Type)) require(advertisedServices.contains(SimpleNotaryService.Type))
// TODO: create a base class that provides a default implementation // TODO: create a base class that provides a default implementation
val cfg = object : NodeConfiguration { val cfg = object : NodeConfiguration {
override val basedir: Path = config.basedir
override val myLegalName: String = "Notary Service" override val myLegalName: String = "Notary Service"
override val nearestCity: String = "Zurich" override val nearestCity: String = "Zurich"
override val emailAddress: String = "" override val emailAddress: String = ""
@ -117,17 +119,18 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
override val trustStorePassword: String = "trustpass" override val trustStorePassword: String = "trustpass"
override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0) override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0)
} }
return SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) return SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, keyPair)
} }
} }
object RatesOracleFactory : MockNetwork.Factory { object RatesOracleFactory : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode {
require(advertisedServices.contains(NodeInterestRates.Type)) require(advertisedServices.contains(NodeInterestRates.Type))
// TODO: create a base class that provides a default implementation // TODO: create a base class that provides a default implementation
val cfg = object : NodeConfiguration { val cfg = object : NodeConfiguration {
override val basedir: Path = config.basedir
override val myLegalName: String = "Rates Service Provider" override val myLegalName: String = "Rates Service Provider"
override val nearestCity: String = "Madrid" override val nearestCity: String = "Madrid"
override val emailAddress: String = "" override val emailAddress: String = ""
@ -138,7 +141,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0) override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0)
} }
return object : SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) { return object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, keyPair) {
override fun start(): MockNetwork.MockNode { override fun start(): MockNetwork.MockNode {
super.start() super.start()
findService<NodeInterestRates.Service>().upload(javaClass.getResourceAsStream("example.rates.txt")) findService<NodeInterestRates.Service>().upload(javaClass.getResourceAsStream("example.rates.txt"))
@ -149,11 +152,12 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
} }
object RegulatorFactory : MockNetwork.Factory { object RegulatorFactory : MockNetwork.Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNetwork.MockNode {
// TODO: create a base class that provides a default implementation // TODO: create a base class that provides a default implementation
val cfg = object : NodeConfiguration { val cfg = object : NodeConfiguration {
override val basedir: Path = config.basedir
override val myLegalName: String = "Regulator A" override val myLegalName: String = "Regulator A"
override val nearestCity: String = "Paris" override val nearestCity: String = "Paris"
override val emailAddress: String = "" override val emailAddress: String = ""
@ -164,7 +168,7 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0) override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0)
} }
val n = object : SimulatedNode(dir, cfg, network, networkMapAddr, advertisedServices, id, keyPair) { val n = object : SimulatedNode(cfg, network, networkMapAddr, advertisedServices, id, keyPair) {
// TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request. // TODO: Regulatory nodes don't actually exist properly, this is a last minute demo request.
// So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it. // So we just fire a message at a node that doesn't know how to handle it, and it'll ignore it.
// But that's fine for visualisation purposes. // But that's fine for visualisation purposes.

View File

@ -18,6 +18,7 @@ import com.r3corda.core.serialization.deserialize
import com.r3corda.core.testing.InMemoryVaultService import com.r3corda.core.testing.InMemoryVaultService
import com.r3corda.core.utilities.DUMMY_NOTARY_KEY import com.r3corda.core.utilities.DUMMY_NOTARY_KEY
import com.r3corda.core.utilities.loggerFor import com.r3corda.core.utilities.loggerFor
import com.r3corda.node.internal.AbstractNode
import com.r3corda.node.services.config.NodeConfiguration import com.r3corda.node.services.config.NodeConfiguration
import com.r3corda.node.services.keys.E2ETestKeyManagementService import com.r3corda.node.services.keys.E2ETestKeyManagementService
import com.r3corda.node.services.network.InMemoryNetworkMapService import com.r3corda.node.services.network.InMemoryNetworkMapService
@ -61,19 +62,19 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
/** Allows customisation of how nodes are created. */ /** Allows customisation of how nodes are created. */
interface Factory { interface Factory {
fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNode advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNode
} }
object DefaultFactory : Factory { object DefaultFactory : Factory {
override fun create(dir: Path, config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?, override fun create(config: NodeConfiguration, network: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNode { advertisedServices: Set<ServiceType>, id: Int, keyPair: KeyPair?): MockNode {
return MockNode(dir, config, network, networkMapAddr, advertisedServices, id, keyPair) return MockNode(config, network, networkMapAddr, advertisedServices, id, keyPair)
} }
} }
open class MockNode(dir: Path, config: NodeConfiguration, val mockNet: MockNetwork, networkMapAddr: SingleMessageRecipient?, open class MockNode(config: NodeConfiguration, val mockNet: MockNetwork, networkMapAddr: SingleMessageRecipient?,
advertisedServices: Set<ServiceType>, val id: Int, val keyPair: KeyPair?) : com.r3corda.node.internal.AbstractNode(dir, config, networkMapAddr, advertisedServices, TestClock()) { advertisedServices: Set<ServiceType>, val id: Int, val keyPair: KeyPair?) : AbstractNode(config, networkMapAddr, advertisedServices, TestClock()) {
override val log: Logger = loggerFor<MockNode>() override val log: Logger = loggerFor<MockNode>()
override val serverThread: com.r3corda.node.utilities.AffinityExecutor = override val serverThread: com.r3corda.node.utilities.AffinityExecutor =
if (mockNet.threadPerNode) if (mockNet.threadPerNode)
@ -167,7 +168,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
// TODO: create a base class that provides a default implementation // TODO: create a base class that provides a default implementation
val config = object : NodeConfiguration { val config = object : NodeConfiguration {
override val basedir: Path = path
override val myLegalName: String = legalName ?: "Mock Company $id" override val myLegalName: String = legalName ?: "Mock Company $id"
override val nearestCity: String = "Atlantis" override val nearestCity: String = "Atlantis"
override val emailAddress: String = "" override val emailAddress: String = ""
@ -178,7 +179,7 @@ class MockNetwork(private val networkSendManuallyPumped: Boolean = false,
override val dataSourceProperties: Properties get() = if (databasePersistence) makeTestDataSourceProperties("node_$id") else Properties() override val dataSourceProperties: Properties get() = if (databasePersistence) makeTestDataSourceProperties("node_$id") else Properties()
override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0) override val certificateSigningService: HostAndPort = HostAndPort.fromParts("localhost", 0)
} }
val node = nodeFactory.create(path, config, this, networkMapAddress, advertisedServices.toSet(), id, keyPair) val node = nodeFactory.create(config, this, networkMapAddress, advertisedServices.toSet(), id, keyPair)
if (start) { if (start) {
node.setup().start() node.setup().start()
if (threadPerNode && networkMapAddress != null) if (threadPerNode && networkMapAddress != null)