From 00a5e3db6bef77e600839af658f35ffb5dd33dcf Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Thu, 7 Dec 2017 14:48:31 +0000 Subject: [PATCH] CORDA-830 Introducing the network bootstrapper Copying of the node-info files moved out of Cordform and into NetworkParametersGenerator (which is now called NetworkBootstrapper). This class becomes an external tool to enable deployment of nodes in a test setup on a single filesystem. --- .idea/compiler.xml | 2 + constants.properties | 2 +- docs/source/changelog.rst | 6 +- docs/source/network-map.rst | 4 +- docs/source/setting-up-a-corda-network.rst | 37 +++- .../main/kotlin/net/corda/plugins/Cordform.kt | 118 ++----------- .../src/main/kotlin/net/corda/plugins/Node.kt | 5 - .../internal/NetworkParametersCopier.kt | 31 ---- .../internal/NetworkParametersGenerator.kt | 107 ----------- .../internal/network/NetworkBootstrapper.kt | 167 ++++++++++++++++++ .../internal/{ => network}/NetworkMap.kt | 4 +- .../network/NetworkParametersCopier.kt | 35 ++++ .../{ => network}/NodeInfoFilesCopier.kt | 3 +- .../network}/NodeInfoFilesCopierTest.kt | 20 +-- .../node/services/BFTNotaryServiceTests.kt | 4 +- .../node/services/network/NetworkMapTest.kt | 21 +-- .../services/network/NodeInfoWatcherTest.kt | 2 +- .../net/corda/node/internal/AbstractNode.kt | 52 +++--- .../node/services/network/NetworkMapClient.kt | 6 +- .../node/services/network/NodeInfoWatcher.kt | 2 +- .../network/PersistentNetworkMapCache.kt | 2 +- .../node/internal/NetworkParametersTest.kt | 14 +- .../services/network/NetworkMapUpdaterTest.kt | 2 +- settings.gradle | 1 + .../kotlin/net/corda/testing/node/MockNode.kt | 14 +- .../testing/node/internal/DriverDSLImpl.kt | 10 +- .../testing/node/internal/NodeBasedTest.kt | 2 +- .../node/internal/network/NetworkMapServer.kt | 4 + .../net/corda/smoketesting/NodeProcess.kt | 2 +- .../common/internal/ParametersUtilities.kt | 4 +- tools/bootstrapper/build.gradle | 27 +++ .../model/DemoBenchNodeInfoFilesCopier.kt | 2 +- .../corda/demobench/model/NodeController.kt | 7 +- 33 files changed, 378 insertions(+), 341 deletions(-) delete mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersCopier.kt delete mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersGenerator.kt create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt rename node-api/src/main/kotlin/net/corda/nodeapi/internal/{ => network}/NetworkMap.kt (96%) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkParametersCopier.kt rename node-api/src/main/kotlin/net/corda/nodeapi/internal/{ => network}/NodeInfoFilesCopier.kt (98%) rename node-api/src/test/kotlin/net/corda/nodeapi/{ => internal/network}/NodeInfoFilesCopierTest.kt (95%) create mode 100644 tools/bootstrapper/build.gradle diff --git a/.idea/compiler.xml b/.idea/compiler.xml index d5523e52b5..a33ce1e0f0 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -10,6 +10,8 @@ + + diff --git a/constants.properties b/constants.properties index a5b866aef0..5613b91279 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=3.0.1 +gradlePluginsVersion=3.0.2 kotlinVersion=1.1.60 platformVersion=2 guavaVersion=21.0 diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 6dfb168cc2..7d5422dee8 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -6,13 +6,11 @@ from the previous milestone release. UNRELEASED ---------- -* Support for external user credentials data source and password encryption [CORDA-827]. - * The network map service concept has been re-designed. More information can be found in :doc:`network-map`. * The previous design was never intended to be final but was rather a quick implementation in the earliest days of the - Corda project to unblock higher priority items. It sufffers from numerous disadvantages including lack of scalability, + Corda project to unblock higher priority items. It suffers from numerous disadvantages including lack of scalability, as one node is expected to hold open and manage connections to every node on the network; not reliable; hard to defend against DoS attacks; etc. @@ -50,6 +48,8 @@ UNRELEASED * Moved ``NodeInfoSchema`` to internal package as the node info's database schema is not part of the public API. This was needed to allow changes to the schema. +* Support for external user credentials data source and password encryption [CORDA-827]. + * Exporting additional JMX metrics (artemis, hibernate statistics) and loading Jolokia agent at JVM startup when using DriverDSL and/or cordformation node runner. diff --git a/docs/source/network-map.rst b/docs/source/network-map.rst index 8ec565a95d..efe8110843 100644 --- a/docs/source/network-map.rst +++ b/docs/source/network-map.rst @@ -13,7 +13,7 @@ Protocol Design --------------- The node info publishing protocol: -* Create a ``NodeInfo`` object, and sign it to create a ``SignedData`` object. TODO: We will need list of signatures in ``SignedData`` to support multiple node identities in the future. +* Create a ``NodeInfo`` object, and sign it to create a ``SignedNodeInfo`` object. * Serialise the signed data and POST the data to the network map server. @@ -61,7 +61,7 @@ The ``additional-node-infos`` directory --------------------------------------- Each Corda node reads, and continuously polls, the files contained in a directory named ``additional-node-infos`` inside the node base directory. -Nodes expect to find a serialized ``SignedData`` object, the same object which is sent to network map server. +Nodes expect to find a serialized ``SignedNodeInfo`` object, the same object which is sent to network map server. Whenever a node starts it writes on disk a file containing its own ``NodeInfo``, this file is called ``nodeInfo-XXX`` where ``XXX`` is a long string. diff --git a/docs/source/setting-up-a-corda-network.rst b/docs/source/setting-up-a-corda-network.rst index 4b610ef104..4ae763dce2 100644 --- a/docs/source/setting-up-a-corda-network.rst +++ b/docs/source/setting-up-a-corda-network.rst @@ -6,11 +6,9 @@ Creating a Corda network A Corda network consists of a number of machines running nodes. These nodes communicate using persistent protocols in order to create and validate transactions. -There are four broader categories of functionality one such node may have. These pieces of functionality are provided +There are three broader categories of functionality one such node may have. These pieces of functionality are provided as services, and one node may run several of them. -* Network map: The node running the network map provides a way to resolve identities to physical node addresses and - associated public keys * Notary: Nodes running a notary service witness state spends and have the final say in whether a transaction is a double-spend or not * Oracle: Network services that link the ledger to the outside world by providing facts that affect the validity of @@ -46,12 +44,38 @@ The most important fields regarding network configuration are: * ``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. +Bootstrapping the network +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The nodes see each other using the network map. This is a collection of statically signed node-info files, one for each +node in the network. Most production deployments will use a highly available, secure distribution of the network map via HTTP. + +For test deployments where the nodes (at least initially) reside on the same filesystem, these node-info files can be +placed directly in the node's ``additional-node-infos`` directory from where the node will pick them up and store them +in its local network map cache. The node generates its own node-info file on startup. + +In addition to the network map, all the nodes on a network must use the same set of network parameters. These are a set +of constants which guarantee interoperability between nodes. The HTTP network map distributes the network parameters +which the node downloads automatically. In the absence of this the network parameters must be generated locally. This can +be done with the network bootstrapper. This a tool that scans all the node configurations from a common directory to +generate the network parameters file which is copied to the nodes' directories. It also copies each node's node-info file +to every other node. + +The bootstrapper tool can be built with the command: + +``gradlew buildBootstrapperJar`` + +The resulting jar can be found in ``tools/bootstrapper/build/libs/``. + +To use it, run the following command, specifying the root directory which hosts all the node directories as the argument: + +``java -jar network-bootstrapper.jar `` + Starting the nodes ~~~~~~~~~~~~~~~~~~ -You may now start the nodes in any order. Note that the node is not fully started until it has successfully registered with the network map! - -You should see a banner, some log lines and eventually ``Node started up and registered``, indicating that the node is fully started. +You may now start the nodes in any order. You should see a banner, some log lines and eventually ``Node started up and registered``, +indicating that the node is fully started. .. TODO: Add a better way of polling for startup. A programmatic way of determining whether a node is up is to check whether it's ``webAddress`` is bound. @@ -66,7 +90,6 @@ details/diagnosing problems check the logs. Logging is standard log4j2_ and may be configured accordingly. Logs are by default redirected to files in ``NODE_DIRECTORY/logs/``. - Connecting to the nodes ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt index cffc411f7f..dc131cc8b5 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Cordform.kt @@ -2,18 +2,16 @@ package net.corda.plugins import groovy.lang.Closure import net.corda.cordform.CordformDefinition -import net.corda.cordform.CordformNode import org.apache.tools.ant.filters.FixCrLfFilter import org.gradle.api.DefaultTask -import org.gradle.api.GradleException import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME import org.gradle.api.tasks.TaskAction import java.io.File +import java.lang.reflect.InvocationTargetException import java.net.URLClassLoader import java.nio.file.Path import java.nio.file.Paths -import java.util.concurrent.TimeUnit import java.util.jar.JarInputStream /** @@ -118,13 +116,13 @@ open class Cordform : DefaultTask() { } /** - * The parametersGenerator needn't be compiled until just before our build method, so we load it manually via sourceSets.main.runtimeClasspath. + * The NetworkBootstrapper needn't be compiled until just before our build method, so we load it manually via sourceSets.main.runtimeClasspath. */ - private fun loadNetworkParamsGenClass(): Class<*> { + private fun loadNetworkBootstrapperClass(): Class<*> { val plugin = project.convention.getPlugin(JavaPluginConvention::class.java) val classpath = plugin.sourceSets.getByName(MAIN_SOURCE_SET_NAME).runtimeClasspath val urls = classpath.files.map { it.toURI().toURL() }.toTypedArray() - return URLClassLoader(urls, javaClass.classLoader).loadClass("net.corda.nodeapi.internal.NetworkParametersGenerator") + return URLClassLoader(urls, javaClass.classLoader).loadClass("net.corda.nodeapi.internal.network.NetworkBootstrapper") } /** @@ -136,8 +134,7 @@ open class Cordform : DefaultTask() { initializeConfiguration() installRunScript() nodes.forEach(Node::build) - generateAndInstallNodeInfos() - generateAndInstallNetworkParameters() + bootstrapNetwork() } private fun initializeConfiguration() { @@ -164,14 +161,17 @@ open class Cordform : DefaultTask() { } } - private fun generateAndInstallNetworkParameters() { - project.logger.info("Generating and installing network parameters") - val networkParamsGenClass = loadNetworkParamsGenClass() - val nodeDirs = nodes.map(Node::fullPath) - val networkParamsGenObject = networkParamsGenClass.newInstance() - val runMethod = networkParamsGenClass.getMethod("run", List::class.java).apply { isAccessible = true } - // Call NetworkParametersGenerator.run - runMethod.invoke(networkParamsGenObject, nodeDirs) + private fun bootstrapNetwork() { + val networkBootstrapperClass = loadNetworkBootstrapperClass() + val networkBootstrapper = networkBootstrapperClass.newInstance() + val bootstrapMethod = networkBootstrapperClass.getMethod("bootstrap", Path::class.java).apply { isAccessible = true } + // Call NetworkBootstrapper.bootstrap + try { + val rootDir = project.projectDir.toPath().resolve(directory).toAbsolutePath().normalize() + bootstrapMethod.invoke(networkBootstrapper, rootDir) + } catch (e: InvocationTargetException) { + throw e.cause!! + } } private fun CordformDefinition.getMatchingCordapps(): List { @@ -197,90 +197,4 @@ open class Cordform : DefaultTask() { return false } } - - private fun generateAndInstallNodeInfos() { - generateNodeInfos() - installNodeInfos() - } - - private fun generateNodeInfos() { - project.logger.info("Generating node infos") - val nodeProcesses = buildNodeProcesses() - try { - validateNodeProcessess(nodeProcesses) - } finally { - destroyNodeProcesses(nodeProcesses) - } - } - - private fun buildNodeProcesses(): Map { - val command = generateNodeInfoCommand() - return nodes.map { - it.makeLogDirectory() - buildProcess(it, command, "generate-info.log") }.toMap() - } - - private fun validateNodeProcessess(nodeProcesses: Map) { - nodeProcesses.forEach { (node, process) -> - validateNodeProcess(node, process) - } - } - - private fun destroyNodeProcesses(nodeProcesses: Map) { - nodeProcesses.forEach { (_, process) -> - process.destroyForcibly() - } - } - - private fun buildProcess(node: Node, command: List, logFile: String): Pair { - val process = ProcessBuilder(command) - .directory(node.fullPath().toFile()) - .redirectErrorStream(true) - // InheritIO causes hangs on windows due the gradle buffer also not being flushed. - // Must redirect to output or logger (node log is still written, this is just startup banner) - .redirectOutput(node.logFile(logFile).toFile()) - .addEnvironment("CAPSULE_CACHE_DIR", Node.capsuleCacheDir) - .start() - return Pair(node, process) - } - - private fun generateNodeInfoCommand(): List = listOf( - "java", - "-Dcapsule.log=verbose", - "-Dcapsule.dir=${Node.capsuleCacheDir}", - "-jar", - Node.nodeJarName, - "--just-generate-node-info" - ) - - private fun validateNodeProcess(node: Node, process: Process) { - val generateTimeoutSeconds = 60L - if (!process.waitFor(generateTimeoutSeconds, TimeUnit.SECONDS)) { - throw GradleException("Node took longer $generateTimeoutSeconds seconds than too to generate node info - see node log at ${node.fullPath()}/logs") - } - if (process.exitValue() != 0) { - throw GradleException("Node exited with ${process.exitValue()} when generating node infos - see node log at ${node.fullPath()}/logs") - } - project.logger.info("Generated node info for ${node.fullPath()}") - } - - private fun installNodeInfos() { - project.logger.info("Installing node infos") - for (source in nodes) { - for (destination in nodes) { - if (source.nodeDir != destination.nodeDir) { - project.copy { - it.apply { - from(source.fullPath().toString()) - include("nodeInfo-*") - into(destination.fullPath().resolve(CordformNode.NODE_INFO_DIRECTORY).toString()) - } - } - } - } - } - } - - private fun Node.logFile(name: String): Path = this.logDirectory().resolve(name) - private fun ProcessBuilder.addEnvironment(key: String, value: String) = this.apply { environment().put(key, value) } } diff --git a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt index 356da1179f..bd3842d3fa 100644 --- a/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt +++ b/gradle-plugins/cordformation/src/main/kotlin/net/corda/plugins/Node.kt @@ -20,13 +20,8 @@ class Node(private val project: Project) : CordformNode() { @JvmStatic val webJarName = "corda-webserver.jar" private val configFileProperty = "configFile" - val capsuleCacheDir: String = "./cache" } - fun fullPath(): Path = project.projectDir.toPath().resolve(nodeDir.toPath()) - fun logDirectory(): Path = fullPath().resolve("logs") - fun makeLogDirectory() = Files.createDirectories(logDirectory()) - /** * Set the list of CorDapps to install to the plugins directory. Each cordapp is a fully qualified Maven * dependency name, eg: com.example:product-name:0.1 diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersCopier.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersCopier.kt deleted file mode 100644 index 954dac862a..0000000000 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersCopier.kt +++ /dev/null @@ -1,31 +0,0 @@ -package net.corda.nodeapi.internal - -import net.corda.core.crypto.SignedData -import net.corda.core.crypto.entropyToKeyPair -import net.corda.core.crypto.sign -import net.corda.core.internal.copyTo -import net.corda.core.internal.div -import net.corda.core.serialization.serialize -import java.math.BigInteger -import java.nio.file.FileAlreadyExistsException -import java.nio.file.Path - -class NetworkParametersCopier(networkParameters: NetworkParameters) { - private companion object { - val DUMMY_MAP_KEY = entropyToKeyPair(BigInteger.valueOf(123)) - } - - private val serializedNetworkParameters = networkParameters.let { - val serialize = it.serialize() - val signature = DUMMY_MAP_KEY.sign(serialize) - SignedData(serialize, signature).serialize() - } - - fun install(dir: Path) { - try { - serializedNetworkParameters.open().copyTo(dir / NETWORK_PARAMS_FILE_NAME) - } catch (e: FileAlreadyExistsException) { - // Leave the file untouched if it already exists - } - } -} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersGenerator.kt deleted file mode 100644 index 6e0f5c4c40..0000000000 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersGenerator.kt +++ /dev/null @@ -1,107 +0,0 @@ -package net.corda.nodeapi.internal - -import com.typesafe.config.ConfigFactory -import net.corda.core.identity.Party -import net.corda.core.internal.div -import net.corda.core.internal.list -import net.corda.core.internal.readAll -import net.corda.core.node.NodeInfo -import net.corda.core.serialization.SerializationContext -import net.corda.core.serialization.deserialize -import net.corda.core.serialization.internal.SerializationEnvironmentImpl -import net.corda.core.serialization.internal._contextSerializationEnv -import net.corda.core.utilities.ByteSequence -import net.corda.core.utilities.contextLogger -import net.corda.core.utilities.days -import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT -import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl -import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme -import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme -import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 -import java.nio.file.Path -import java.time.Instant - -/** - * This class is loaded by Cordform using reflection to generate the network parameters. It is assumed that Cordform has - * already asked each node to generate its node info file. - */ -@Suppress("UNUSED") -class NetworkParametersGenerator { - companion object { - private val logger = contextLogger() - } - - fun run(nodesDirs: List) { - logger.info("NetworkParameters generation using node directories: $nodesDirs") - try { - initialiseSerialization() - val notaryInfos = gatherNotaryIdentities(nodesDirs) - val copier = NetworkParametersCopier(NetworkParameters( - minimumPlatformVersion = 1, - notaries = notaryInfos, - modifiedTime = Instant.now(), - maxMessageSize = 10485760, - maxTransactionSize = 40000, - epoch = 1 - )) - nodesDirs.forEach(copier::install) - } finally { - _contextSerializationEnv.set(null) - } - } - - private fun gatherNotaryIdentities(nodesDirs: List): List { - return nodesDirs.mapNotNull { nodeDir -> - val nodeConfig = ConfigFactory.parseFile((nodeDir / "node.conf").toFile()) - if (nodeConfig.hasPath("notary")) { - val validating = nodeConfig.getConfig("notary").getBoolean("validating") - val nodeInfoFile = nodeDir.list { paths -> paths.filter { it.fileName.toString().startsWith("nodeInfo-") }.findFirst().get() } - processFile(nodeInfoFile)?.let { NotaryInfo(it.notaryIdentity(), validating) } - } else { - null - } - }.distinct() // We need distinct as nodes part of a distributed notary share the same notary identity - } - - private fun NodeInfo.notaryIdentity(): Party { - return when (legalIdentities.size) { - // Single node notaries have just one identity like all other nodes. This identity is the notary identity - 1 -> legalIdentities[0] - // Nodes which are part of a distributed notary have a second identity which is the composite identity of the - // cluster and is shared by all the other members. This is the notary identity. - 2 -> legalIdentities[1] - else -> throw IllegalArgumentException("Not sure how to get the notary identity in this scenerio: $this") - } - } - - private fun processFile(file: Path): NodeInfo? { - return try { - logger.info("Reading NodeInfo from file: $file") - val signedData = file.readAll().deserialize() - signedData.verified() - } catch (e: Exception) { - logger.warn("Exception parsing NodeInfo from file. $file", e) - null - } - } - - // We need to to set serialization env, because generation of parameters is run from Cordform. - // KryoServerSerializationScheme is not accessible from nodeapi. - private fun initialiseSerialization() { - _contextSerializationEnv.set(SerializationEnvironmentImpl( - SerializationFactoryImpl().apply { - registerScheme(KryoParametersSerializationScheme) - registerScheme(AMQPServerSerializationScheme()) - }, - AMQP_P2P_CONTEXT) - ) - } - - private object KryoParametersSerializationScheme : AbstractKryoSerializationScheme() { - override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean { - return byteSequence == KryoHeaderV0_1 && target == SerializationContext.UseCase.P2P - } - override fun rpcClientKryoPool(context: SerializationContext) = throw UnsupportedOperationException() - override fun rpcServerKryoPool(context: SerializationContext) = throw UnsupportedOperationException() - } -} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt new file mode 100644 index 0000000000..34ee05851a --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -0,0 +1,167 @@ +package net.corda.nodeapi.internal.network + +import com.typesafe.config.ConfigFactory +import net.corda.cordform.CordformNode +import net.corda.core.identity.Party +import net.corda.core.internal.* +import net.corda.core.node.NodeInfo +import net.corda.core.serialization.SerializationContext +import net.corda.core.serialization.deserialize +import net.corda.core.serialization.internal.SerializationEnvironmentImpl +import net.corda.core.serialization.internal._contextSerializationEnv +import net.corda.core.utilities.ByteSequence +import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT +import net.corda.nodeapi.internal.serialization.SerializationFactoryImpl +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.AbstractKryoSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.KryoHeaderV0_1 +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.StandardCopyOption +import java.time.Instant +import java.util.concurrent.TimeUnit.SECONDS +import kotlin.streams.toList + +/** + * Class to bootstrap a local network of Corda nodes on the same filesystem. + */ +class NetworkBootstrapper { + companion object { + // TODO This will probably need to change once we start using a bundled JVM + private val nodeInfoGenCmd = listOf( + "java", + "-jar", + "corda.jar", + "--just-generate-node-info" + ) + + private const val LOGS_DIR_NAME = "logs" + + @JvmStatic + fun main(args: Array) { + val arg = args.singleOrNull() ?: throw IllegalArgumentException("Expecting single argument which is the nodes' parent directory") + NetworkBootstrapper().bootstrap(Paths.get(arg).toAbsolutePath().normalize()) + } + } + + fun bootstrap(directory: Path) { + directory.createDirectories() + println("Bootstrapping local network in $directory") + val nodeDirs = directory.list { paths -> paths.filter { (it / "corda.jar").exists() }.toList() } + require(nodeDirs.isNotEmpty()) { "No nodes found" } + println("Nodes found in the following sub-directories: ${nodeDirs.map { it.fileName }}") + val processes = startNodeInfoGeneration(nodeDirs) + initialiseSerialization() + try { + println("Waiting for all nodes to generate their node-info files") + val nodeInfoFiles = gatherNodeInfoFiles(processes, nodeDirs) + println("Distributing all node info-files to all nodes") + distributeNodeInfos(nodeDirs, nodeInfoFiles) + println("Gathering notary identities") + val notaryInfos = gatherNotaryInfos(nodeInfoFiles) + println("Notary identities to be used in network-parameters file: ${notaryInfos.joinToString("; ") { it.prettyPrint() }}") + installNetworkParameters(notaryInfos, nodeDirs) + println("Bootstrapping complete!") + } finally { + _contextSerializationEnv.set(null) + processes.forEach { if (it.isAlive) it.destroyForcibly() } + } + } + + private fun startNodeInfoGeneration(nodeDirs: List): List { + return nodeDirs.map { nodeDir -> + val logsDir = (nodeDir / LOGS_DIR_NAME).createDirectories() + ProcessBuilder(nodeInfoGenCmd) + .directory(nodeDir.toFile()) + .redirectErrorStream(true) + .redirectOutput((logsDir / "node-info-gen.log").toFile()) + .apply { environment()["CAPSULE_CACHE_DIR"] = "../.cache" } + .start() + } + } + + private fun gatherNodeInfoFiles(processes: List, nodeDirs: List): List { + val timeOutInSeconds = 60L + return processes.zip(nodeDirs).map { (process, nodeDir) -> + check(process.waitFor(timeOutInSeconds, SECONDS)) { + "Node in ${nodeDir.fileName} took longer than ${timeOutInSeconds}s to generate its node-info - see logs in ${nodeDir / LOGS_DIR_NAME}" + } + check(process.exitValue() == 0) { + "Node in ${nodeDir.fileName} exited with ${process.exitValue()} when generating its node-info - see logs in ${nodeDir / LOGS_DIR_NAME}" + } + nodeDir.list { paths -> paths.filter { it.fileName.toString().startsWith("nodeInfo-") }.findFirst().get() } + } + } + + private fun distributeNodeInfos(nodeDirs: List, nodeInfoFiles: List) { + for (nodeDir in nodeDirs) { + val additionalNodeInfosDir = (nodeDir / CordformNode.NODE_INFO_DIRECTORY).createDirectories() + for (nodeInfoFile in nodeInfoFiles) { + nodeInfoFile.copyToDirectory(additionalNodeInfosDir, StandardCopyOption.REPLACE_EXISTING) + } + } + } + + private fun gatherNotaryInfos(nodeInfoFiles: List): List { + return nodeInfoFiles.mapNotNull { nodeInfoFile -> + // The config contains the notary type + val nodeConfig = ConfigFactory.parseFile((nodeInfoFile.parent / "node.conf").toFile()) + if (nodeConfig.hasPath("notary")) { + val validating = nodeConfig.getConfig("notary").getBoolean("validating") + // And the node-info file contains the notary's identity + val nodeInfo = nodeInfoFile.readAll().deserialize().verified() + NotaryInfo(nodeInfo.notaryIdentity(), validating) + } else { + null + } + }.distinct() // We need distinct as nodes part of a distributed notary share the same notary identity + } + + private fun installNetworkParameters(notaryInfos: List, nodeDirs: List) { + // TODO Add config for minimumPlatformVersion, maxMessageSize and maxTransactionSize + val copier = NetworkParametersCopier(NetworkParameters( + minimumPlatformVersion = 1, + notaries = notaryInfos, + modifiedTime = Instant.now(), + maxMessageSize = 10485760, + maxTransactionSize = 40000, + epoch = 1 + ), overwriteFile = true) + + nodeDirs.forEach(copier::install) + } + + private fun NotaryInfo.prettyPrint(): String = "${identity.name} (${if (validating) "" else "non-"}validating)" + + private fun NodeInfo.notaryIdentity(): Party { + return when (legalIdentities.size) { + // Single node notaries have just one identity like all other nodes. This identity is the notary identity + 1 -> legalIdentities[0] + // Nodes which are part of a distributed notary have a second identity which is the composite identity of the + // cluster and is shared by all the other members. This is the notary identity. + 2 -> legalIdentities[1] + else -> throw IllegalArgumentException("Not sure how to get the notary identity in this scenerio: $this") + } + } + + // We need to to set serialization env, because generation of parameters is run from Cordform. + // KryoServerSerializationScheme is not accessible from nodeapi. + private fun initialiseSerialization() { + _contextSerializationEnv.set(SerializationEnvironmentImpl( + SerializationFactoryImpl().apply { + registerScheme(KryoParametersSerializationScheme) + registerScheme(AMQPServerSerializationScheme()) + }, + AMQP_P2P_CONTEXT) + ) + } + + private object KryoParametersSerializationScheme : AbstractKryoSerializationScheme() { + override fun canDeserializeVersion(byteSequence: ByteSequence, target: SerializationContext.UseCase): Boolean { + return byteSequence == KryoHeaderV0_1 && target == SerializationContext.UseCase.P2P + } + override fun rpcClientKryoPool(context: SerializationContext) = throw UnsupportedOperationException() + override fun rpcServerKryoPool(context: SerializationContext) = throw UnsupportedOperationException() + } +} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkMap.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt similarity index 96% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkMap.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt index a9bf3ca97b..f784a4297b 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkMap.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal +package net.corda.nodeapi.internal.network import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.SecureHash @@ -15,7 +15,7 @@ import java.security.cert.X509Certificate import java.time.Instant const val NETWORK_PARAMS_FILE_NAME = "network-parameters" -// TODO: Need more discussion on rather we should move this class out of internal. + /** * Data class containing hash of [NetworkParameters] and network participant's [NodeInfo] hashes. */ diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkParametersCopier.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkParametersCopier.kt new file mode 100644 index 0000000000..fe9b88a24e --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkParametersCopier.kt @@ -0,0 +1,35 @@ +package net.corda.nodeapi.internal.network + +import net.corda.core.crypto.Crypto +import net.corda.core.crypto.SignedData +import net.corda.core.crypto.sign +import net.corda.core.internal.copyTo +import net.corda.core.internal.div +import net.corda.core.serialization.serialize +import net.corda.nodeapi.internal.crypto.X509Utilities +import java.nio.file.FileAlreadyExistsException +import java.nio.file.Path +import java.nio.file.StandardCopyOption +import java.security.KeyPair + +class NetworkParametersCopier( + networkParameters: NetworkParameters, + signingKeyPair: KeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME), + overwriteFile: Boolean = false +) { + private val copyOptions = if (overwriteFile) arrayOf(StandardCopyOption.REPLACE_EXISTING) else emptyArray() + private val serializedNetworkParameters = networkParameters.let { + val serialize = it.serialize() + val signature = signingKeyPair.sign(serialize) + SignedData(serialize, signature).serialize() + } + + fun install(nodeDir: Path) { + try { + serializedNetworkParameters.open().copyTo(nodeDir / NETWORK_PARAMS_FILE_NAME, *copyOptions) + } catch (e: FileAlreadyExistsException) { + // This is only thrown if the file already exists and we didn't specify to overwrite it. In that case we + // ignore this exception as we're happy with the existing file. + } + } +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NodeInfoFilesCopier.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopier.kt similarity index 98% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/NodeInfoFilesCopier.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopier.kt index 00786c8da1..f11fa2265e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/NodeInfoFilesCopier.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopier.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal +package net.corda.nodeapi.internal.network import net.corda.cordform.CordformNode import net.corda.core.internal.ThreadBox @@ -65,7 +65,6 @@ class NodeInfoFilesCopier(scheduler: Scheduler = Schedulers.io()) : AutoCloseabl } /** - * @param nodeConfig the configuration to be removed. * Remove the configuration of a node which is about to be stopped or already stopped. * No files written by that node will be copied to other nodes, nor files from other nodes will be copied to this * one. diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/NodeInfoFilesCopierTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopierTest.kt similarity index 95% rename from node-api/src/test/kotlin/net/corda/nodeapi/NodeInfoFilesCopierTest.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopierTest.kt index d2a18c7191..d4d95ba6fd 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/NodeInfoFilesCopierTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NodeInfoFilesCopierTest.kt @@ -1,7 +1,7 @@ -package net.corda.nodeapi +package net.corda.nodeapi.internal.network import net.corda.cordform.CordformNode -import net.corda.nodeapi.internal.NodeInfoFilesCopier +import net.corda.nodeapi.eventually import org.junit.Before import org.junit.Rule import org.junit.Test @@ -14,14 +14,7 @@ import java.util.concurrent.TimeUnit import kotlin.streams.toList import kotlin.test.assertEquals -/** - * tests for [NodeInfoFilesCopier] - */ class NodeInfoFilesCopierTest { - - @Rule @JvmField var folder = TemporaryFolder() - private val rootPath get() = folder.root.toPath() - private val scheduler = TestScheduler() companion object { private const val ORGANIZATION = "Organization" private const val NODE_1_PATH = "node1" @@ -33,6 +26,13 @@ class NodeInfoFilesCopierTest { private val BAD_NODE_INFO_NAME = "something" } + @Rule + @JvmField + val folder = TemporaryFolder() + + private val rootPath get() = folder.root.toPath() + private val scheduler = TestScheduler() + private fun nodeDir(nodeBaseDir : String) = rootPath.resolve(nodeBaseDir).resolve(ORGANIZATION.toLowerCase()) private val node1RootPath by lazy { nodeDir(NODE_1_PATH) } @@ -40,7 +40,7 @@ class NodeInfoFilesCopierTest { private val node1AdditionalNodeInfoPath by lazy { node1RootPath.resolve(CordformNode.NODE_INFO_DIRECTORY) } private val node2AdditionalNodeInfoPath by lazy { node2RootPath.resolve(CordformNode.NODE_INFO_DIRECTORY) } - lateinit var nodeInfoFilesCopier: NodeInfoFilesCopier + private lateinit var nodeInfoFilesCopier: NodeInfoFilesCopier @Before fun setUp() { diff --git a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt index 6073713271..ef762be725 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/BFTNotaryServiceTests.kt @@ -25,9 +25,9 @@ import net.corda.node.services.config.NotaryConfig import net.corda.node.services.transactions.minClusterSize import net.corda.node.services.transactions.minCorrectReplicas import net.corda.nodeapi.internal.ServiceIdentityGenerator -import net.corda.nodeapi.internal.NotaryInfo +import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.testing.chooseIdentity -import net.corda.nodeapi.internal.NetworkParametersCopier +import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract import net.corda.testing.dummyCommand diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt index 251034a035..a46ae6f378 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NetworkMapTest.kt @@ -1,19 +1,20 @@ package net.corda.node.services.network import net.corda.core.crypto.SignedData +import net.corda.core.internal.list import net.corda.core.internal.readAll import net.corda.core.node.NodeInfo import net.corda.core.serialization.deserialize import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds -import net.corda.nodeapi.internal.NETWORK_PARAMS_FILE_NAME -import net.corda.nodeapi.internal.NetworkParameters -import net.corda.testing.node.internal.CompatibilityZoneParams +import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME +import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.testing.ALICE_NAME -import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.BOB_NAME +import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.PortAllocation +import net.corda.testing.node.internal.CompatibilityZoneParams import net.corda.testing.node.internal.internalDriver import net.corda.testing.node.internal.network.NetworkMapServer import org.assertj.core.api.Assertions.assertThat @@ -22,8 +23,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import java.net.URL -import java.nio.file.Files -import kotlin.streams.toList import kotlin.test.assertEquals class NetworkMapTest { @@ -51,10 +50,12 @@ class NetworkMapTest { @Test fun `node correctly downloads and saves network parameters file on startup`() { internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone, initialiseSerialization = false) { - val aliceDir = baseDirectory(ALICE_NAME) - startNode(providedName = ALICE_NAME).getOrThrow() - val networkParameters = Files.list(aliceDir).toList().single { NETWORK_PARAMS_FILE_NAME == it.fileName.toString() } - .readAll().deserialize>().verified() + val alice = startNode(providedName = ALICE_NAME).getOrThrow() + val networkParameters = alice.configuration.baseDirectory + .list { paths -> paths.filter { it.fileName.toString() == NETWORK_PARAMS_FILE_NAME }.findFirst().get() } + .readAll() + .deserialize>() + .verified() assertEquals(NetworkMapServer.stubNetworkParameter, networkParameters) } } diff --git a/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt index cac6163282..b813558eca 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt @@ -7,8 +7,8 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.node.NodeInfo import net.corda.core.node.services.KeyManagementService -import net.corda.nodeapi.internal.NodeInfoFilesCopier import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import net.corda.testing.ALICE_NAME import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.internal.createNodeInfoAndSigned diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 58432c2cd2..ab435d9e58 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -59,10 +59,10 @@ import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.VaultSoftLockManager import net.corda.node.shell.InteractiveShell import net.corda.node.utilities.AffinityExecutor -import net.corda.nodeapi.internal.NETWORK_PARAMS_FILE_NAME -import net.corda.nodeapi.internal.NetworkParameters import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.crypto.* +import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME +import net.corda.nodeapi.internal.network.NetworkParameters import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.HibernateConfiguration @@ -73,7 +73,6 @@ import rx.Observable import rx.Scheduler import java.io.IOException import java.lang.reflect.InvocationTargetException -import java.nio.file.Files import java.security.KeyPair import java.security.KeyStoreException import java.security.PublicKey @@ -87,7 +86,6 @@ import java.util.concurrent.ExecutorService import java.util.concurrent.TimeUnit.SECONDS import kotlin.collections.set import kotlin.reflect.KClass -import kotlin.streams.toList import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair /** @@ -210,10 +208,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val schemaService = NodeSchemaService(cordappLoader.cordappSchemas) val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null) val identityService = makeIdentityService(identity.certificate) - networkMapClient = configuration.compatibilityZoneURL?.let { - NetworkMapClient(it, identityService.trustRoot) - } - readNetworkParameters() + networkMapClient = configuration.compatibilityZoneURL?.let { NetworkMapClient(it, identityService.trustRoot) } + retrieveNetworkParameters() // Do all of this in a database transaction so anything that might need a connection has one. val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database -> val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries), identityService) @@ -648,29 +644,31 @@ abstract class AbstractNode(val configuration: NodeConfiguration, return PersistentKeyManagementService(identityService, keyPairs) } - private fun readNetworkParameters() { - val files = Files.list(configuration.baseDirectory).filter { NETWORK_PARAMS_FILE_NAME == it.fileName.toString() }.toList() - val paramsFromFile = try { - // It's fine at this point if we don't have network parameters or have corrupted file, later we check if parameters can be downloaded from network map server. - files[0].readAll().deserialize>().verified() - } catch (t: Exception) { - log.warn("Couldn't find correct network parameters file in the base directory") - null + private fun retrieveNetworkParameters() { + val networkParamsFile = configuration.baseDirectory.list { paths -> + paths.filter { it.fileName.toString() == NETWORK_PARAMS_FILE_NAME }.findFirst().orElse(null) } - networkParameters = if (paramsFromFile != null) { - paramsFromFile - } else if (networkMapClient != null) { - log.info("Requesting network parameters from network map server...") - val (networkMap, _) = networkMapClient!!.getNetworkMap() - val signedParams = networkMapClient!!.getNetworkParameter(networkMap.networkParameterHash) ?: throw IllegalArgumentException("Failed loading network parameters from network map server") - val verifiedParams = signedParams.verified() // Verify before saving. + + networkParameters = if (networkParamsFile != null) { + networkParamsFile.readAll().deserialize>().verified() + } else { + log.info("No network-parameters file found. Expecting network parameters to be available from the network map.") + val networkMapClient = checkNotNull(networkMapClient) { + "Node hasn't been configured to connect to a network map from which to get the network parameters" + } + val (networkMap, _) = networkMapClient.getNetworkMap() + val signedParams = checkNotNull(networkMapClient.getNetworkParameter(networkMap.networkParameterHash)) { + "Failed loading network parameters from network map server" + } + val verifiedParams = signedParams.verified() signedParams.serialize().open().copyTo(configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME) verifiedParams - } else { - throw IllegalArgumentException("Couldn't load network parameters file") } - log.info("Loaded network parameters $networkParameters") - check(networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) { "Node's platform version is lower than network's required minimumPlatformVersion" } + + log.info("Loaded network parameters: $networkParameters") + check(networkParameters.minimumPlatformVersion <= versionInfo.platformVersion) { + "Node's platform version is lower than network's required minimumPlatformVersion" + } } private fun makeCoreNotaryService(notaryConfig: NotaryConfig, database: CordaPersistence): NotaryService { diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt index d7a76cb008..db7bea9480 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt @@ -12,9 +12,9 @@ import net.corda.core.utilities.minutes import net.corda.core.utilities.seconds import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.utilities.NamedThreadFactory -import net.corda.nodeapi.internal.NetworkMap -import net.corda.nodeapi.internal.NetworkParameters -import net.corda.nodeapi.internal.SignedNetworkMap +import net.corda.nodeapi.internal.network.NetworkMap +import net.corda.nodeapi.internal.network.NetworkParameters +import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.SignedNodeInfo import okhttp3.CacheControl import okhttp3.Headers diff --git a/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt b/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt index c19f1a2195..3031349d84 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NodeInfoWatcher.kt @@ -8,7 +8,7 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.contextLogger import net.corda.core.utilities.seconds -import net.corda.nodeapi.internal.NodeInfoFilesCopier +import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import net.corda.nodeapi.internal.SignedNodeInfo import rx.Observable import rx.Scheduler diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index 1d2e9d1a36..8cb6bd68b5 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -25,7 +25,7 @@ import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction -import net.corda.nodeapi.internal.NotaryInfo +import net.corda.nodeapi.internal.network.NotaryInfo import org.hibernate.Session import rx.Observable import rx.subjects.PublishSubject diff --git a/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt b/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt index 83bd00b96e..0b9e42640c 100644 --- a/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/NetworkParametersTest.kt @@ -7,17 +7,21 @@ import net.corda.core.utilities.getOrThrow import net.corda.finance.DOLLARS import net.corda.finance.flows.CashIssueFlow import net.corda.node.services.config.NotaryConfig -import net.corda.nodeapi.internal.NetworkParameters -import net.corda.nodeapi.internal.NetworkParametersCopier -import net.corda.nodeapi.internal.NotaryInfo -import net.corda.testing.* +import net.corda.nodeapi.internal.network.NetworkParameters +import net.corda.nodeapi.internal.network.NetworkParametersCopier +import net.corda.nodeapi.internal.network.NotaryInfo +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME +import net.corda.testing.DUMMY_NOTARY_NAME +import net.corda.testing.chooseIdentity import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.node.* +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After import org.junit.Test import java.nio.file.Path import kotlin.test.assertFails -import org.assertj.core.api.Assertions.* class NetworkParametersTest { private val mockNet = MockNetwork( diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt index 98dbb96415..b76c7d59d9 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt @@ -16,8 +16,8 @@ import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.core.utilities.millis import net.corda.node.services.api.NetworkMapCacheInternal -import net.corda.nodeapi.internal.NetworkMap import net.corda.nodeapi.internal.SignedNodeInfo +import net.corda.nodeapi.internal.network.NetworkMap import net.corda.testing.ALICE_NAME import net.corda.testing.SerializationEnvironmentRule import net.corda.testing.internal.TestNodeInfoBuilder diff --git a/settings.gradle b/settings.gradle index 14c2ffd469..a4f87dee28 100644 --- a/settings.gradle +++ b/settings.gradle @@ -33,6 +33,7 @@ include 'tools:explorer:capsule' include 'tools:demobench' include 'tools:loadtest' include 'tools:graphs' +include 'tools:bootstrapper' include 'example-code' project(':example-code').projectDir = file("$settingsDir/docs/source/example-code") include 'samples:attachment-demo' diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt index dde3cc1224..2ed3d9b65d 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockNode.kt @@ -22,8 +22,8 @@ import net.corda.core.node.services.KeyManagementService import net.corda.core.serialization.SerializationWhitelist import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger -import net.corda.node.VersionInfo import net.corda.core.utilities.seconds +import net.corda.node.VersionInfo import net.corda.node.internal.AbstractNode import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader @@ -37,18 +37,18 @@ import net.corda.node.services.transactions.BFTSMaRt import net.corda.node.services.transactions.InMemoryTransactionVerifierService import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor -import net.corda.nodeapi.internal.NetworkParametersCopier -import net.corda.nodeapi.internal.NotaryInfo import net.corda.nodeapi.internal.ServiceIdentityGenerator import net.corda.nodeapi.internal.config.User +import net.corda.nodeapi.internal.network.NetworkParametersCopier +import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.DUMMY_NOTARY_NAME import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.testThreadFactory import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties -import net.corda.testing.internal.rigorousMock import net.corda.testing.setGlobalSerialization import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.sshd.common.util.security.SecurityUtils @@ -229,12 +229,12 @@ class MockNetwork(private val cordappPackages: List, } private fun generateNotaryIdentities(): List { - return notarySpecs.mapIndexed { index, spec -> + return notarySpecs.mapIndexed { index, (name, validating) -> val identity = ServiceIdentityGenerator.generateToDisk( dirs = listOf(baseDirectory(nextNodeId + index)), - serviceName = spec.name, + serviceName = name, serviceId = "identity") - NotaryInfo(identity, spec.validating) + NotaryInfo(identity, validating) } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 288c63ef4c..df1cb0ac03 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -35,7 +35,8 @@ import net.corda.node.services.transactions.RaftNonValidatingNotaryService import net.corda.node.services.transactions.RaftValidatingNotaryService import net.corda.node.utilities.registration.HTTPNetworkRegistrationService import net.corda.node.utilities.registration.NetworkRegistrationHelper -import net.corda.nodeapi.internal.* +import net.corda.nodeapi.internal.ServiceIdentityGenerator +import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.parseAs import net.corda.nodeapi.internal.config.toConfig @@ -43,8 +44,13 @@ import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.addOrReplaceCertificate import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore import net.corda.nodeapi.internal.crypto.save +import net.corda.nodeapi.internal.network.NetworkParametersCopier +import net.corda.nodeapi.internal.network.NodeInfoFilesCopier +import net.corda.nodeapi.internal.network.NotaryInfo +import net.corda.testing.ALICE_NAME +import net.corda.testing.BOB_NAME +import net.corda.testing.DUMMY_BANK_A_NAME import net.corda.testing.common.internal.testNetworkParameters -import net.corda.testing.* import net.corda.testing.driver.* import net.corda.testing.node.ClusterSpec import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index 0cb88acbc0..4c5772b308 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -12,9 +12,9 @@ import net.corda.node.internal.Node import net.corda.node.internal.StartedNode import net.corda.node.internal.cordapp.CordappLoader import net.corda.node.services.config.* -import net.corda.nodeapi.internal.NetworkParametersCopier import net.corda.nodeapi.internal.config.User import net.corda.testing.SerializationEnvironmentRule +import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.getFreeLocalPorts import net.corda.testing.internal.testThreadFactory diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt index e704357863..cf7cb48fa0 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/network/NetworkMapServer.kt @@ -7,6 +7,10 @@ import net.corda.core.node.NodeInfo import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.internal.network.DigitalSignatureWithCert +import net.corda.nodeapi.internal.network.NetworkMap +import net.corda.nodeapi.internal.network.NetworkParameters +import net.corda.nodeapi.internal.network.SignedNetworkMap import net.corda.nodeapi.internal.* import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt index 2211c68b86..ffe478998d 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt @@ -8,7 +8,7 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger -import net.corda.nodeapi.internal.NetworkParametersCopier +import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.asContextEnv import java.nio.file.Path diff --git a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt index 938bf66976..3d448d9188 100644 --- a/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt +++ b/testing/test-common/src/main/kotlin/net/corda/testing/common/internal/ParametersUtilities.kt @@ -1,7 +1,7 @@ package net.corda.testing.common.internal -import net.corda.nodeapi.internal.NetworkParameters -import net.corda.nodeapi.internal.NotaryInfo +import net.corda.nodeapi.internal.network.NetworkParameters +import net.corda.nodeapi.internal.network.NotaryInfo import java.time.Instant fun testNetworkParameters( diff --git a/tools/bootstrapper/build.gradle b/tools/bootstrapper/build.gradle new file mode 100644 index 0000000000..dc578b2c76 --- /dev/null +++ b/tools/bootstrapper/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'us.kirchmeier.capsule' + +description 'Network bootstrapper' + +configurations { + runtimeArtifacts +} + +// TODO Fix SLF4J warnings that occur when running the bootstrapper +task buildBootstrapperJar(type: FatCapsule, dependsOn: project(':node-api').compileJava) { + applicationClass 'net.corda.nodeapi.internal.network.NetworkBootstrapper' + archiveName "network-bootstrapper.jar" + capsuleManifest { + applicationVersion = corda_release_version + systemProperties['visualvm.display.name'] = 'Network Bootstrapper' + minJavaVersion = '1.8.0' + jvmArgs = ['-XX:+UseG1GC'] + } + applicationSource = files( + project(':node-api').configurations.runtime, + project(':node-api').jar + ) +} + +artifacts { + runtimeArtifacts buildBootstrapperJar +} diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/DemoBenchNodeInfoFilesCopier.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/DemoBenchNodeInfoFilesCopier.kt index 3b856ef5ac..a3d8586d26 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/DemoBenchNodeInfoFilesCopier.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/DemoBenchNodeInfoFilesCopier.kt @@ -1,6 +1,6 @@ package net.corda.demobench.model -import net.corda.nodeapi.internal.NodeInfoFilesCopier +import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import rx.Scheduler import rx.schedulers.Schedulers import tornadofx.* diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt index 81ddfe8881..37e019b367 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/NodeController.kt @@ -8,12 +8,11 @@ import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.noneOrSingle import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.days import net.corda.demobench.plugin.CordappController import net.corda.demobench.pty.R3Pty -import net.corda.nodeapi.internal.NetworkParameters -import net.corda.nodeapi.internal.NetworkParametersCopier -import net.corda.nodeapi.internal.NotaryInfo +import net.corda.nodeapi.internal.network.NetworkParameters +import net.corda.nodeapi.internal.network.NetworkParametersCopier +import net.corda.nodeapi.internal.network.NotaryInfo import net.corda.nodeapi.internal.ServiceIdentityGenerator import tornadofx.* import java.io.IOException