mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
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.
This commit is contained in:
parent
e9cead9055
commit
00a5e3db6b
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@ -10,6 +10,8 @@
|
|||||||
<module name="bank-of-corda-demo_integrationTest" target="1.8" />
|
<module name="bank-of-corda-demo_integrationTest" target="1.8" />
|
||||||
<module name="bank-of-corda-demo_main" target="1.8" />
|
<module name="bank-of-corda-demo_main" target="1.8" />
|
||||||
<module name="bank-of-corda-demo_test" target="1.8" />
|
<module name="bank-of-corda-demo_test" target="1.8" />
|
||||||
|
<module name="bootstrapper_main" target="1.8" />
|
||||||
|
<module name="bootstrapper_test" target="1.8" />
|
||||||
<module name="buildSrc_main" target="1.8" />
|
<module name="buildSrc_main" target="1.8" />
|
||||||
<module name="buildSrc_test" target="1.8" />
|
<module name="buildSrc_test" target="1.8" />
|
||||||
<module name="client_main" target="1.8" />
|
<module name="client_main" target="1.8" />
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
gradlePluginsVersion=3.0.1
|
gradlePluginsVersion=3.0.2
|
||||||
kotlinVersion=1.1.60
|
kotlinVersion=1.1.60
|
||||||
platformVersion=2
|
platformVersion=2
|
||||||
guavaVersion=21.0
|
guavaVersion=21.0
|
||||||
|
@ -6,13 +6,11 @@ from the previous milestone release.
|
|||||||
|
|
||||||
UNRELEASED
|
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 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
|
* 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
|
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.
|
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
|
* 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.
|
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
|
* Exporting additional JMX metrics (artemis, hibernate statistics) and loading Jolokia agent at JVM startup when using
|
||||||
DriverDSL and/or cordformation node runner.
|
DriverDSL and/or cordformation node runner.
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ Protocol Design
|
|||||||
---------------
|
---------------
|
||||||
The node info publishing protocol:
|
The node info publishing protocol:
|
||||||
|
|
||||||
* Create a ``NodeInfo`` object, and sign it to create a ``SignedData<NodeInfo>`` 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.
|
* 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.
|
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<NodeInfo>`` 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.
|
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.
|
||||||
|
|
||||||
|
@ -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
|
A Corda network consists of a number of machines running nodes. These nodes communicate using persistent protocols in
|
||||||
order to create and validate transactions.
|
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.
|
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
|
* Notary: Nodes running a notary service witness state spends and have the final say in whether a transaction is a
|
||||||
double-spend or not
|
double-spend or not
|
||||||
* Oracle: Network services that link the ledger to the outside world by providing facts that affect the validity of
|
* 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.
|
* ``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.
|
* ``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 <nodes-root-dir>``
|
||||||
|
|
||||||
Starting the nodes
|
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 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.
|
||||||
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.
|
.. 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
|
Logging is standard log4j2_ and may be configured accordingly. Logs
|
||||||
are by default redirected to files in ``NODE_DIRECTORY/logs/``.
|
are by default redirected to files in ``NODE_DIRECTORY/logs/``.
|
||||||
|
|
||||||
|
|
||||||
Connecting to the nodes
|
Connecting to the nodes
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -2,18 +2,16 @@ package net.corda.plugins
|
|||||||
|
|
||||||
import groovy.lang.Closure
|
import groovy.lang.Closure
|
||||||
import net.corda.cordform.CordformDefinition
|
import net.corda.cordform.CordformDefinition
|
||||||
import net.corda.cordform.CordformNode
|
|
||||||
import org.apache.tools.ant.filters.FixCrLfFilter
|
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
import org.gradle.api.GradleException
|
|
||||||
import org.gradle.api.plugins.JavaPluginConvention
|
import org.gradle.api.plugins.JavaPluginConvention
|
||||||
import org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME
|
import org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME
|
||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import java.util.jar.JarInputStream
|
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 plugin = project.convention.getPlugin(JavaPluginConvention::class.java)
|
||||||
val classpath = plugin.sourceSets.getByName(MAIN_SOURCE_SET_NAME).runtimeClasspath
|
val classpath = plugin.sourceSets.getByName(MAIN_SOURCE_SET_NAME).runtimeClasspath
|
||||||
val urls = classpath.files.map { it.toURI().toURL() }.toTypedArray()
|
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()
|
initializeConfiguration()
|
||||||
installRunScript()
|
installRunScript()
|
||||||
nodes.forEach(Node::build)
|
nodes.forEach(Node::build)
|
||||||
generateAndInstallNodeInfos()
|
bootstrapNetwork()
|
||||||
generateAndInstallNetworkParameters()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeConfiguration() {
|
private fun initializeConfiguration() {
|
||||||
@ -164,14 +161,17 @@ open class Cordform : DefaultTask() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateAndInstallNetworkParameters() {
|
private fun bootstrapNetwork() {
|
||||||
project.logger.info("Generating and installing network parameters")
|
val networkBootstrapperClass = loadNetworkBootstrapperClass()
|
||||||
val networkParamsGenClass = loadNetworkParamsGenClass()
|
val networkBootstrapper = networkBootstrapperClass.newInstance()
|
||||||
val nodeDirs = nodes.map(Node::fullPath)
|
val bootstrapMethod = networkBootstrapperClass.getMethod("bootstrap", Path::class.java).apply { isAccessible = true }
|
||||||
val networkParamsGenObject = networkParamsGenClass.newInstance()
|
// Call NetworkBootstrapper.bootstrap
|
||||||
val runMethod = networkParamsGenClass.getMethod("run", List::class.java).apply { isAccessible = true }
|
try {
|
||||||
// Call NetworkParametersGenerator.run
|
val rootDir = project.projectDir.toPath().resolve(directory).toAbsolutePath().normalize()
|
||||||
runMethod.invoke(networkParamsGenObject, nodeDirs)
|
bootstrapMethod.invoke(networkBootstrapper, rootDir)
|
||||||
|
} catch (e: InvocationTargetException) {
|
||||||
|
throw e.cause!!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CordformDefinition.getMatchingCordapps(): List<File> {
|
private fun CordformDefinition.getMatchingCordapps(): List<File> {
|
||||||
@ -197,90 +197,4 @@ open class Cordform : DefaultTask() {
|
|||||||
return false
|
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<Node, Process> {
|
|
||||||
val command = generateNodeInfoCommand()
|
|
||||||
return nodes.map {
|
|
||||||
it.makeLogDirectory()
|
|
||||||
buildProcess(it, command, "generate-info.log") }.toMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun validateNodeProcessess(nodeProcesses: Map<Node, Process>) {
|
|
||||||
nodeProcesses.forEach { (node, process) ->
|
|
||||||
validateNodeProcess(node, process)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun destroyNodeProcesses(nodeProcesses: Map<Node, Process>) {
|
|
||||||
nodeProcesses.forEach { (_, process) ->
|
|
||||||
process.destroyForcibly()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildProcess(node: Node, command: List<String>, logFile: String): Pair<Node, Process> {
|
|
||||||
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<String> = 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) }
|
|
||||||
}
|
}
|
||||||
|
@ -20,13 +20,8 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
val webJarName = "corda-webserver.jar"
|
val webJarName = "corda-webserver.jar"
|
||||||
private val configFileProperty = "configFile"
|
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
|
* 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
|
* dependency name, eg: com.example:product-name:0.1
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<Path>) {
|
|
||||||
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<Path>): List<NotaryInfo> {
|
|
||||||
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<SignedNodeInfo>()
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<String>) {
|
||||||
|
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<Path>): List<Process> {
|
||||||
|
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<Process>, nodeDirs: List<Path>): List<Path> {
|
||||||
|
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<Path>, nodeInfoFiles: List<Path>) {
|
||||||
|
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<Path>): List<NotaryInfo> {
|
||||||
|
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<SignedNodeInfo>().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<NotaryInfo>, nodeDirs: List<Path>) {
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
@ -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.DigitalSignature
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
@ -15,7 +15,7 @@ import java.security.cert.X509Certificate
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
const val NETWORK_PARAMS_FILE_NAME = "network-parameters"
|
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.
|
* Data class containing hash of [NetworkParameters] and network participant's [NodeInfo] hashes.
|
||||||
*/
|
*/
|
@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package net.corda.nodeapi.internal
|
package net.corda.nodeapi.internal.network
|
||||||
|
|
||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.core.internal.ThreadBox
|
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.
|
* 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
|
* No files written by that node will be copied to other nodes, nor files from other nodes will be copied to this
|
||||||
* one.
|
* one.
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.nodeapi
|
package net.corda.nodeapi.internal.network
|
||||||
|
|
||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import net.corda.nodeapi.internal.NodeInfoFilesCopier
|
import net.corda.nodeapi.eventually
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -14,14 +14,7 @@ import java.util.concurrent.TimeUnit
|
|||||||
import kotlin.streams.toList
|
import kotlin.streams.toList
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
/**
|
|
||||||
* tests for [NodeInfoFilesCopier]
|
|
||||||
*/
|
|
||||||
class NodeInfoFilesCopierTest {
|
class NodeInfoFilesCopierTest {
|
||||||
|
|
||||||
@Rule @JvmField var folder = TemporaryFolder()
|
|
||||||
private val rootPath get() = folder.root.toPath()
|
|
||||||
private val scheduler = TestScheduler()
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ORGANIZATION = "Organization"
|
private const val ORGANIZATION = "Organization"
|
||||||
private const val NODE_1_PATH = "node1"
|
private const val NODE_1_PATH = "node1"
|
||||||
@ -33,6 +26,13 @@ class NodeInfoFilesCopierTest {
|
|||||||
private val BAD_NODE_INFO_NAME = "something"
|
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 fun nodeDir(nodeBaseDir : String) = rootPath.resolve(nodeBaseDir).resolve(ORGANIZATION.toLowerCase())
|
||||||
|
|
||||||
private val node1RootPath by lazy { nodeDir(NODE_1_PATH) }
|
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 node1AdditionalNodeInfoPath by lazy { node1RootPath.resolve(CordformNode.NODE_INFO_DIRECTORY) }
|
||||||
private val node2AdditionalNodeInfoPath by lazy { node2RootPath.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
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
@ -25,9 +25,9 @@ import net.corda.node.services.config.NotaryConfig
|
|||||||
import net.corda.node.services.transactions.minClusterSize
|
import net.corda.node.services.transactions.minClusterSize
|
||||||
import net.corda.node.services.transactions.minCorrectReplicas
|
import net.corda.node.services.transactions.minCorrectReplicas
|
||||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
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.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.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.contracts.DummyContract
|
import net.corda.testing.contracts.DummyContract
|
||||||
import net.corda.testing.dummyCommand
|
import net.corda.testing.dummyCommand
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package net.corda.node.services.network
|
package net.corda.node.services.network
|
||||||
|
|
||||||
import net.corda.core.crypto.SignedData
|
import net.corda.core.crypto.SignedData
|
||||||
|
import net.corda.core.internal.list
|
||||||
import net.corda.core.internal.readAll
|
import net.corda.core.internal.readAll
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.nodeapi.internal.NETWORK_PARAMS_FILE_NAME
|
import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
|
||||||
import net.corda.nodeapi.internal.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.testing.node.internal.CompatibilityZoneParams
|
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
|
||||||
import net.corda.testing.BOB_NAME
|
import net.corda.testing.BOB_NAME
|
||||||
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.PortAllocation
|
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.internalDriver
|
||||||
import net.corda.testing.node.internal.network.NetworkMapServer
|
import net.corda.testing.node.internal.network.NetworkMapServer
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
@ -22,8 +23,6 @@ import org.junit.Before
|
|||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Files
|
|
||||||
import kotlin.streams.toList
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class NetworkMapTest {
|
class NetworkMapTest {
|
||||||
@ -51,10 +50,12 @@ class NetworkMapTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `node correctly downloads and saves network parameters file on startup`() {
|
fun `node correctly downloads and saves network parameters file on startup`() {
|
||||||
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone, initialiseSerialization = false) {
|
internalDriver(portAllocation = portAllocation, compatibilityZone = compatibilityZone, initialiseSerialization = false) {
|
||||||
val aliceDir = baseDirectory(ALICE_NAME)
|
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||||
startNode(providedName = ALICE_NAME).getOrThrow()
|
val networkParameters = alice.configuration.baseDirectory
|
||||||
val networkParameters = Files.list(aliceDir).toList().single { NETWORK_PARAMS_FILE_NAME == it.fileName.toString() }
|
.list { paths -> paths.filter { it.fileName.toString() == NETWORK_PARAMS_FILE_NAME }.findFirst().get() }
|
||||||
.readAll().deserialize<SignedData<NetworkParameters>>().verified()
|
.readAll()
|
||||||
|
.deserialize<SignedData<NetworkParameters>>()
|
||||||
|
.verified()
|
||||||
assertEquals(NetworkMapServer.stubNetworkParameter, networkParameters)
|
assertEquals(NetworkMapServer.stubNetworkParameter, networkParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ import net.corda.core.internal.createDirectories
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.KeyManagementService
|
import net.corda.core.node.services.KeyManagementService
|
||||||
import net.corda.nodeapi.internal.NodeInfoFilesCopier
|
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
|
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.internal.createNodeInfoAndSigned
|
import net.corda.testing.internal.createNodeInfoAndSigned
|
||||||
|
@ -59,10 +59,10 @@ import net.corda.node.services.vault.NodeVaultService
|
|||||||
import net.corda.node.services.vault.VaultSoftLockManager
|
import net.corda.node.services.vault.VaultSoftLockManager
|
||||||
import net.corda.node.shell.InteractiveShell
|
import net.corda.node.shell.InteractiveShell
|
||||||
import net.corda.node.utilities.AffinityExecutor
|
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.SignedNodeInfo
|
||||||
import net.corda.nodeapi.internal.crypto.*
|
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.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
import net.corda.nodeapi.internal.persistence.HibernateConfiguration
|
||||||
@ -73,7 +73,6 @@ import rx.Observable
|
|||||||
import rx.Scheduler
|
import rx.Scheduler
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.nio.file.Files
|
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.KeyStoreException
|
import java.security.KeyStoreException
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -87,7 +86,6 @@ import java.util.concurrent.ExecutorService
|
|||||||
import java.util.concurrent.TimeUnit.SECONDS
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.streams.toList
|
|
||||||
import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -210,10 +208,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas)
|
||||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||||
val identityService = makeIdentityService(identity.certificate)
|
val identityService = makeIdentityService(identity.certificate)
|
||||||
networkMapClient = configuration.compatibilityZoneURL?.let {
|
networkMapClient = configuration.compatibilityZoneURL?.let { NetworkMapClient(it, identityService.trustRoot) }
|
||||||
NetworkMapClient(it, identityService.trustRoot)
|
retrieveNetworkParameters()
|
||||||
}
|
|
||||||
readNetworkParameters()
|
|
||||||
// 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.
|
||||||
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
|
val (startedImpl, schedulerService) = initialiseDatabasePersistence(schemaService, identityService) { database ->
|
||||||
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries), identityService)
|
val networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, networkParameters.notaries), identityService)
|
||||||
@ -648,29 +644,31 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
|||||||
return PersistentKeyManagementService(identityService, keyPairs)
|
return PersistentKeyManagementService(identityService, keyPairs)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readNetworkParameters() {
|
private fun retrieveNetworkParameters() {
|
||||||
val files = Files.list(configuration.baseDirectory).filter { NETWORK_PARAMS_FILE_NAME == it.fileName.toString() }.toList()
|
val networkParamsFile = configuration.baseDirectory.list { paths ->
|
||||||
val paramsFromFile = try {
|
paths.filter { it.fileName.toString() == NETWORK_PARAMS_FILE_NAME }.findFirst().orElse(null)
|
||||||
// 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<SignedData<NetworkParameters>>().verified()
|
|
||||||
} catch (t: Exception) {
|
|
||||||
log.warn("Couldn't find correct network parameters file in the base directory")
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
networkParameters = if (paramsFromFile != null) {
|
|
||||||
paramsFromFile
|
networkParameters = if (networkParamsFile != null) {
|
||||||
} else if (networkMapClient != null) {
|
networkParamsFile.readAll().deserialize<SignedData<NetworkParameters>>().verified()
|
||||||
log.info("Requesting network parameters from network map server...")
|
} else {
|
||||||
val (networkMap, _) = networkMapClient!!.getNetworkMap()
|
log.info("No network-parameters file found. Expecting network parameters to be available from the network map.")
|
||||||
val signedParams = networkMapClient!!.getNetworkParameter(networkMap.networkParameterHash) ?: throw IllegalArgumentException("Failed loading network parameters from network map server")
|
val networkMapClient = checkNotNull(networkMapClient) {
|
||||||
val verifiedParams = signedParams.verified() // Verify before saving.
|
"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)
|
signedParams.serialize().open().copyTo(configuration.baseDirectory / NETWORK_PARAMS_FILE_NAME)
|
||||||
verifiedParams
|
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 {
|
private fun makeCoreNotaryService(notaryConfig: NotaryConfig, database: CordaPersistence): NotaryService {
|
||||||
|
@ -12,9 +12,9 @@ import net.corda.core.utilities.minutes
|
|||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
import net.corda.node.services.api.NetworkMapCacheInternal
|
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||||
import net.corda.node.utilities.NamedThreadFactory
|
import net.corda.node.utilities.NamedThreadFactory
|
||||||
import net.corda.nodeapi.internal.NetworkMap
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.nodeapi.internal.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.SignedNetworkMap
|
import net.corda.nodeapi.internal.network.SignedNetworkMap
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import okhttp3.CacheControl
|
import okhttp3.CacheControl
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
|
@ -8,7 +8,7 @@ import net.corda.core.serialization.deserialize
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.seconds
|
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 net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Scheduler
|
import rx.Scheduler
|
||||||
|
@ -25,7 +25,7 @@ import net.corda.node.services.api.NetworkMapCacheInternal
|
|||||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit
|
import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit
|
||||||
import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction
|
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 org.hibernate.Session
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
|
@ -7,17 +7,21 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.finance.DOLLARS
|
import net.corda.finance.DOLLARS
|
||||||
import net.corda.finance.flows.CashIssueFlow
|
import net.corda.finance.flows.CashIssueFlow
|
||||||
import net.corda.node.services.config.NotaryConfig
|
import net.corda.node.services.config.NotaryConfig
|
||||||
import net.corda.nodeapi.internal.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
import net.corda.testing.*
|
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.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.node.*
|
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.After
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.test.assertFails
|
import kotlin.test.assertFails
|
||||||
import org.assertj.core.api.Assertions.*
|
|
||||||
|
|
||||||
class NetworkParametersTest {
|
class NetworkParametersTest {
|
||||||
private val mockNet = MockNetwork(
|
private val mockNet = MockNetwork(
|
||||||
|
@ -16,8 +16,8 @@ import net.corda.core.node.NodeInfo
|
|||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
import net.corda.node.services.api.NetworkMapCacheInternal
|
import net.corda.node.services.api.NetworkMapCacheInternal
|
||||||
import net.corda.nodeapi.internal.NetworkMap
|
|
||||||
import net.corda.nodeapi.internal.SignedNodeInfo
|
import net.corda.nodeapi.internal.SignedNodeInfo
|
||||||
|
import net.corda.nodeapi.internal.network.NetworkMap
|
||||||
import net.corda.testing.ALICE_NAME
|
import net.corda.testing.ALICE_NAME
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
import net.corda.testing.internal.TestNodeInfoBuilder
|
import net.corda.testing.internal.TestNodeInfoBuilder
|
||||||
|
@ -33,6 +33,7 @@ include 'tools:explorer:capsule'
|
|||||||
include 'tools:demobench'
|
include 'tools:demobench'
|
||||||
include 'tools:loadtest'
|
include 'tools:loadtest'
|
||||||
include 'tools:graphs'
|
include 'tools:graphs'
|
||||||
|
include 'tools:bootstrapper'
|
||||||
include 'example-code'
|
include 'example-code'
|
||||||
project(':example-code').projectDir = file("$settingsDir/docs/source/example-code")
|
project(':example-code').projectDir = file("$settingsDir/docs/source/example-code")
|
||||||
include 'samples:attachment-demo'
|
include 'samples:attachment-demo'
|
||||||
|
@ -22,8 +22,8 @@ import net.corda.core.node.services.KeyManagementService
|
|||||||
import net.corda.core.serialization.SerializationWhitelist
|
import net.corda.core.serialization.SerializationWhitelist
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.node.VersionInfo
|
|
||||||
import net.corda.core.utilities.seconds
|
import net.corda.core.utilities.seconds
|
||||||
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.internal.AbstractNode
|
import net.corda.node.internal.AbstractNode
|
||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.internal.cordapp.CordappLoader
|
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.services.transactions.InMemoryTransactionVerifierService
|
||||||
import net.corda.node.utilities.AffinityExecutor
|
import net.corda.node.utilities.AffinityExecutor
|
||||||
import net.corda.node.utilities.AffinityExecutor.ServiceAffinityExecutor
|
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.ServiceIdentityGenerator
|
||||||
import net.corda.nodeapi.internal.config.User
|
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.CordaPersistence
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||||
import net.corda.testing.DUMMY_NOTARY_NAME
|
import net.corda.testing.DUMMY_NOTARY_NAME
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
|
import net.corda.testing.internal.rigorousMock
|
||||||
import net.corda.testing.internal.testThreadFactory
|
import net.corda.testing.internal.testThreadFactory
|
||||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||||
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
|
||||||
import net.corda.testing.internal.rigorousMock
|
|
||||||
import net.corda.testing.setGlobalSerialization
|
import net.corda.testing.setGlobalSerialization
|
||||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||||
import org.apache.sshd.common.util.security.SecurityUtils
|
import org.apache.sshd.common.util.security.SecurityUtils
|
||||||
@ -229,12 +229,12 @@ class MockNetwork(private val cordappPackages: List<String>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
private fun generateNotaryIdentities(): List<NotaryInfo> {
|
||||||
return notarySpecs.mapIndexed { index, spec ->
|
return notarySpecs.mapIndexed { index, (name, validating) ->
|
||||||
val identity = ServiceIdentityGenerator.generateToDisk(
|
val identity = ServiceIdentityGenerator.generateToDisk(
|
||||||
dirs = listOf(baseDirectory(nextNodeId + index)),
|
dirs = listOf(baseDirectory(nextNodeId + index)),
|
||||||
serviceName = spec.name,
|
serviceName = name,
|
||||||
serviceId = "identity")
|
serviceId = "identity")
|
||||||
NotaryInfo(identity, spec.validating)
|
NotaryInfo(identity, validating)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,8 @@ import net.corda.node.services.transactions.RaftNonValidatingNotaryService
|
|||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
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.User
|
||||||
import net.corda.nodeapi.internal.config.parseAs
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
import net.corda.nodeapi.internal.config.toConfig
|
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.addOrReplaceCertificate
|
||||||
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
|
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
|
||||||
import net.corda.nodeapi.internal.crypto.save
|
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.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.*
|
|
||||||
import net.corda.testing.driver.*
|
import net.corda.testing.driver.*
|
||||||
import net.corda.testing.node.ClusterSpec
|
import net.corda.testing.node.ClusterSpec
|
||||||
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
import net.corda.testing.node.MockServices.Companion.MOCK_VERSION_INFO
|
||||||
|
@ -12,9 +12,9 @@ import net.corda.node.internal.Node
|
|||||||
import net.corda.node.internal.StartedNode
|
import net.corda.node.internal.StartedNode
|
||||||
import net.corda.node.internal.cordapp.CordappLoader
|
import net.corda.node.internal.cordapp.CordappLoader
|
||||||
import net.corda.node.services.config.*
|
import net.corda.node.services.config.*
|
||||||
import net.corda.nodeapi.internal.NetworkParametersCopier
|
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.SerializationEnvironmentRule
|
import net.corda.testing.SerializationEnvironmentRule
|
||||||
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.testing.common.internal.testNetworkParameters
|
import net.corda.testing.common.internal.testNetworkParameters
|
||||||
import net.corda.testing.getFreeLocalPorts
|
import net.corda.testing.getFreeLocalPorts
|
||||||
import net.corda.testing.internal.testThreadFactory
|
import net.corda.testing.internal.testThreadFactory
|
||||||
|
@ -7,6 +7,10 @@ import net.corda.core.node.NodeInfo
|
|||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
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.*
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
|
@ -8,7 +8,7 @@ import net.corda.core.internal.createDirectories
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.contextLogger
|
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.testNetworkParameters
|
||||||
import net.corda.testing.common.internal.asContextEnv
|
import net.corda.testing.common.internal.asContextEnv
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.testing.common.internal
|
package net.corda.testing.common.internal
|
||||||
|
|
||||||
import net.corda.nodeapi.internal.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
fun testNetworkParameters(
|
fun testNetworkParameters(
|
||||||
|
27
tools/bootstrapper/build.gradle
Normal file
27
tools/bootstrapper/build.gradle
Normal file
@ -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
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.demobench.model
|
package net.corda.demobench.model
|
||||||
|
|
||||||
import net.corda.nodeapi.internal.NodeInfoFilesCopier
|
import net.corda.nodeapi.internal.network.NodeInfoFilesCopier
|
||||||
import rx.Scheduler
|
import rx.Scheduler
|
||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
@ -8,12 +8,11 @@ import net.corda.core.internal.createDirectories
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.noneOrSingle
|
import net.corda.core.internal.noneOrSingle
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.days
|
|
||||||
import net.corda.demobench.plugin.CordappController
|
import net.corda.demobench.plugin.CordappController
|
||||||
import net.corda.demobench.pty.R3Pty
|
import net.corda.demobench.pty.R3Pty
|
||||||
import net.corda.nodeapi.internal.NetworkParameters
|
import net.corda.nodeapi.internal.network.NetworkParameters
|
||||||
import net.corda.nodeapi.internal.NetworkParametersCopier
|
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||||
import net.corda.nodeapi.internal.NotaryInfo
|
import net.corda.nodeapi.internal.network.NotaryInfo
|
||||||
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
import net.corda.nodeapi.internal.ServiceIdentityGenerator
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
Loading…
Reference in New Issue
Block a user