diff --git a/constants.properties b/constants.properties index 3b0e9df855..5782e87e76 100644 --- a/constants.properties +++ b/constants.properties @@ -1,4 +1,4 @@ -gradlePluginsVersion=3.0.1-NETWORKMAP +gradlePluginsVersion=3.0.2-NETWORKMAP kotlinVersion=1.1.60 platformVersion=2 guavaVersion=21.0 diff --git a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/NetworkParametersGenerator.java b/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/NetworkParametersGenerator.java deleted file mode 100644 index 702e154ed3..0000000000 --- a/gradle-plugins/cordform-common/src/main/java/net/corda/cordform/NetworkParametersGenerator.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.corda.cordform; - -import java.nio.file.Path; -import java.util.List; - -public interface NetworkParametersGenerator { - /** - * Run generation of network parameters for [Cordformation]. Nodes need to have already their own [NodeInfo] files in their - * base directories, these files will be used to extract notary identities. - * - * @param nodesDirs - nodes directories that will be used for network parameters generation. Network parameters - * file will be dropped into each directory on this list. - */ - void run(List nodesDirs); -} \ No newline at end of file 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 3058b052c9..cffc411f7f 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 @@ -3,7 +3,6 @@ package net.corda.plugins import groovy.lang.Closure import net.corda.cordform.CordformDefinition import net.corda.cordform.CordformNode -import net.corda.cordform.NetworkParametersGenerator import org.apache.tools.ant.filters.FixCrLfFilter import org.gradle.api.DefaultTask import org.gradle.api.GradleException @@ -33,7 +32,6 @@ open class Cordform : DefaultTask() { */ @Suppress("MemberVisibilityCanPrivate") var definitionClass: String? = null - private val networkParametersGenClass: String = "net.corda.nodeapi.internal.TestNetworkParametersGenerator" private var directory = defaultDirectory private val nodes = mutableListOf() @@ -122,14 +120,11 @@ 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. */ - private fun loadParametersGenerator(): NetworkParametersGenerator { + private fun loadNetworkParamsGenClass(): 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, NetworkParametersGenerator::class.java.classLoader) - .loadClass(networkParametersGenClass) - .asSubclass(NetworkParametersGenerator::class.java) - .newInstance() + return URLClassLoader(urls, javaClass.classLoader).loadClass("net.corda.nodeapi.internal.NetworkParametersGenerator") } /** @@ -171,8 +166,12 @@ open class Cordform : DefaultTask() { private fun generateAndInstallNetworkParameters() { project.logger.info("Generating and installing network parameters") - val networkParamsGenerator = loadParametersGenerator() - networkParamsGenerator.run(nodes.map { it.fullPath() }) + 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 CordformDefinition.getMatchingCordapps(): List { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/TestNetworkParametersGenerator.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersGenerator.kt similarity index 58% rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/TestNetworkParametersGenerator.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersGenerator.kt index fe54652c14..185e8bda8f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/TestNetworkParametersGenerator.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/NetworkParametersGenerator.kt @@ -1,10 +1,8 @@ package net.corda.nodeapi.internal import com.typesafe.config.ConfigFactory -import net.corda.cordform.CordformNode -import net.corda.cordform.NetworkParametersGenerator import net.corda.core.crypto.SignedData -import net.corda.core.identity.CordaX500Name +import net.corda.core.identity.Party import net.corda.core.internal.div import net.corda.core.internal.list import net.corda.core.internal.readAll @@ -16,26 +14,30 @@ 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.* +import net.corda.nodeapi.internal.serialization.AMQP_P2P_CONTEXT +import net.corda.nodeapi.internal.serialization.KRYO_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 -import kotlin.streams.toList -// This class is used by deployNodes task to generate NetworkParameters in [Cordformation]. +/** + * 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 TestNetworkParametersGenerator : NetworkParametersGenerator { +class NetworkParametersGenerator { companion object { private val logger = contextLogger() } - override fun run(nodesDirs: List) { + fun run(nodesDirs: List) { logger.info("NetworkParameters generation using node directories: $nodesDirs") try { initialiseSerialization() - val notaryInfos = loadAndGatherNotaryIdentities(nodesDirs) + val notaryInfos = gatherNotaryIdentities(nodesDirs) val copier = NetworkParametersCopier(NetworkParameters( minimumPlatformVersion = 1, notaries = notaryInfos, @@ -45,40 +47,34 @@ class TestNetworkParametersGenerator : NetworkParametersGenerator { maxTransactionSize = 40000, epoch = 1 )) - nodesDirs.forEach { copier.install(it) } + nodesDirs.forEach(copier::install) } finally { _contextSerializationEnv.set(null) } } - private fun loadAndGatherNotaryIdentities(nodesDirs: List): List { - val infos = getAllNodeInfos(nodesDirs) - val configs = nodesDirs.map { ConfigFactory.parseFile((it / "node.conf").toFile()) } - val notaryConfigs = configs.filter { it.hasPath("notary") } - val notaries = notaryConfigs.associateBy( - { CordaX500Name.parse(it.getString("myLegalName")) }, - { it.getConfig("notary").getBoolean("validating") } - ) - // Now get the notary identities based on names passed from configs. There is one problem, for distributed notaries - // in config we specify only node's main name, the notary identity isn't passed there. It's read from keystore on - // node startup, so we have to look it up from node info as a second identity, which is ugly. - return infos.mapNotNull { - info -> notaries[info.legalIdentities[0].name]?.let { NotaryInfo(info.notaryIdentity(), it) } - }.distinct() + 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 } - /** - * Loads latest NodeInfo files stored in node's base directory. - * Scans main directory and [CordformNode.NODE_INFO_DIRECTORY]. - * Signatures are checked before returning a value. The latest value stored for a given name is returned. - * - * @return list of latest [NodeInfo]s - */ - private fun getAllNodeInfos(nodesDirs: List): List { - val nodeInfoFiles = nodesDirs.map { dir -> - dir.list { it.filter { "nodeInfo-" in it.toString() }.findFirst().get() } + 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") } - return nodeInfoFiles.mapNotNull { processFile(it) } } private fun processFile(file: Path): NodeInfo? { @@ -92,8 +88,6 @@ class TestNetworkParametersGenerator : NetworkParametersGenerator { } } - private fun NodeInfo.notaryIdentity() = if (legalIdentities.size == 2) legalIdentities[1] else legalIdentities[0] - // We need to to set serialization env, because generation of parameters is run from Cordform. // KryoServerSerializationScheme is not accessible from nodeapi. private fun initialiseSerialization() { @@ -103,14 +97,14 @@ class TestNetworkParametersGenerator : NetworkParametersGenerator { registerScheme(KryoParametersSerializationScheme) registerScheme(AMQPServerSerializationScheme()) }, - context)) + 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() }