mirror of
https://github.com/corda/corda.git
synced 2025-05-31 22:50:53 +00:00
One demo definition for both IntelliJ and gradle (#697)
* Raft notary demo now defined in one place that both IntelliJ/driver and gradle/runnodes can run * New module cordform-common for code common to cordformation and corda * Add single notary demo
This commit is contained in:
parent
edf269dbe5
commit
c8d71a96f5
2
.gitignore
vendored
2
.gitignore
vendored
@ -32,6 +32,7 @@ lib/dokka.jar
|
|||||||
.idea/libraries
|
.idea/libraries
|
||||||
.idea/shelf
|
.idea/shelf
|
||||||
.idea/dataSources
|
.idea/dataSources
|
||||||
|
/gradle-plugins/.idea
|
||||||
|
|
||||||
# Include the -parameters compiler option by default in IntelliJ required for serialization.
|
# Include the -parameters compiler option by default in IntelliJ required for serialization.
|
||||||
!.idea/compiler.xml
|
!.idea/compiler.xml
|
||||||
@ -53,6 +54,7 @@ lib/dokka.jar
|
|||||||
# Gradle:
|
# Gradle:
|
||||||
# .idea/gradle.xml
|
# .idea/gradle.xml
|
||||||
# .idea/libraries
|
# .idea/libraries
|
||||||
|
/gradle-plugins/gradle*
|
||||||
|
|
||||||
# Mongo Explorer plugin:
|
# Mongo Explorer plugin:
|
||||||
# .idea/mongoSettings.xml
|
# .idea/mongoSettings.xml
|
||||||
|
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@ -17,6 +17,8 @@
|
|||||||
<module name="corda-webserver_integrationTest" target="1.8" />
|
<module name="corda-webserver_integrationTest" target="1.8" />
|
||||||
<module name="corda-webserver_main" target="1.8" />
|
<module name="corda-webserver_main" target="1.8" />
|
||||||
<module name="corda-webserver_test" target="1.8" />
|
<module name="corda-webserver_test" target="1.8" />
|
||||||
|
<module name="cordform-common_main" target="1.8" />
|
||||||
|
<module name="cordform-common_test" target="1.8" />
|
||||||
<module name="core_main" target="1.8" />
|
<module name="core_main" target="1.8" />
|
||||||
<module name="core_test" target="1.8" />
|
<module name="core_test" target="1.8" />
|
||||||
<module name="demobench_main" target="1.8" />
|
<module name="demobench_main" target="1.8" />
|
||||||
|
@ -41,6 +41,7 @@ buildscript {
|
|||||||
ext.rxjava_version = '1.2.4'
|
ext.rxjava_version = '1.2.4'
|
||||||
ext.requery_version = '1.2.1'
|
ext.requery_version = '1.2.1'
|
||||||
ext.dokka_version = '0.9.13'
|
ext.dokka_version = '0.9.13'
|
||||||
|
ext.eddsa_version = '0.2.0'
|
||||||
|
|
||||||
// Update 121 is required for ObjectInputFilter and at time of writing 131 was latest:
|
// Update 121 is required for ObjectInputFilter and at time of writing 131 was latest:
|
||||||
ext.java8_minUpdateVersion = '131'
|
ext.java8_minUpdateVersion = '131'
|
||||||
@ -60,6 +61,7 @@ buildscript {
|
|||||||
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
|
||||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}"
|
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}"
|
||||||
classpath "org.ajoberstar:grgit:1.1.0"
|
classpath "org.ajoberstar:grgit:1.1.0"
|
||||||
|
classpath "net.i2p.crypto:eddsa:$eddsa_version" // Needed for ServiceIdentityGenerator in the build environment.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
gradlePluginsVersion=0.12.0
|
gradlePluginsVersion=0.12.1
|
||||||
kotlinVersion=1.1.2
|
kotlinVersion=1.1.2
|
||||||
guavaVersion=21.0
|
guavaVersion=21.0
|
||||||
bouncycastleVersion=1.56
|
bouncycastleVersion=1.56
|
||||||
|
15
cordform-common/build.gradle
Normal file
15
cordform-common/build.gradle
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'maven-publish'
|
||||||
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// TypeSafe Config: for simple and human friendly config files.
|
||||||
|
compile "com.typesafe:config:$typesafe_config_version"
|
||||||
|
|
||||||
|
// Bouncy Castle: for X.500 distinguished name manipulation
|
||||||
|
compile "org.bouncycastle:bcprov-jdk15on:$bouncycastle_version"
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package net.corda.cordform;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public interface CordformContext {
|
||||||
|
Path baseDirectory(X500Name nodeName);
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package net.corda.cordform;
|
||||||
|
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public abstract class CordformDefinition {
|
||||||
|
public final Path driverDirectory;
|
||||||
|
public final ArrayList<Consumer<? super CordformNode>> nodeConfigurers = new ArrayList<>();
|
||||||
|
public final X500Name networkMapNodeName;
|
||||||
|
|
||||||
|
public CordformDefinition(Path driverDirectory, X500Name networkMapNodeName) {
|
||||||
|
this.driverDirectory = driverDirectory;
|
||||||
|
this.networkMapNodeName = networkMapNodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNode(Consumer<? super CordformNode> configurer) {
|
||||||
|
nodeConfigurers.add(configurer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make arbitrary changes to the node directories before they are started.
|
||||||
|
* @param context Lookup of node directory by node name.
|
||||||
|
*/
|
||||||
|
public abstract void setup(CordformContext context);
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package net.corda.cordform;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import com.typesafe.config.Config;
|
||||||
|
import com.typesafe.config.ConfigFactory;
|
||||||
|
import com.typesafe.config.ConfigValueFactory;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CordformNode {
|
||||||
|
protected static final String DEFAULT_HOST = "localhost";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the node.
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of advertised services ID strings.
|
||||||
|
*/
|
||||||
|
public List<String> advertisedServices = emptyList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If running a distributed notary, a list of node addresses for joining the Raft cluster
|
||||||
|
*/
|
||||||
|
public List<String> notaryClusterAddresses = emptyList();
|
||||||
|
/**
|
||||||
|
* Set the RPC users for this node. This configuration block allows arbitrary configuration.
|
||||||
|
* The recommended current structure is:
|
||||||
|
* [[['username': "username_here", 'password': "password_here", 'permissions': ["permissions_here"]]]
|
||||||
|
* The above is a list to a map of keys to values using Groovy map and list shorthands.
|
||||||
|
*
|
||||||
|
* Incorrect configurations will not cause a DSL error.
|
||||||
|
*/
|
||||||
|
public List<Map<String, Object>> rpcUsers = emptyList();
|
||||||
|
|
||||||
|
protected Config config = ConfigFactory.empty();
|
||||||
|
|
||||||
|
public Config getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the node.
|
||||||
|
*
|
||||||
|
* @param name The node name.
|
||||||
|
*/
|
||||||
|
public void name(String name) {
|
||||||
|
this.name = name;
|
||||||
|
config = config.withValue("myLegalName", ConfigValueFactory.fromAnyRef(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the nearest city to the node.
|
||||||
|
*
|
||||||
|
* @param nearestCity The name of the nearest city to the node.
|
||||||
|
*/
|
||||||
|
public void nearestCity(String nearestCity) {
|
||||||
|
config = config.withValue("nearestCity", ConfigValueFactory.fromAnyRef(nearestCity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Artemis P2P port for this node.
|
||||||
|
*
|
||||||
|
* @param p2pPort The Artemis messaging queue port.
|
||||||
|
*/
|
||||||
|
public void p2pPort(Integer p2pPort) {
|
||||||
|
config = config.withValue("p2pAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + p2pPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Artemis RPC port for this node.
|
||||||
|
*
|
||||||
|
* @param rpcPort The Artemis RPC queue port.
|
||||||
|
*/
|
||||||
|
public void rpcPort(Integer rpcPort) {
|
||||||
|
config = config.withValue("rpcAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + rpcPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the port which to bind the Copycat (Raft) node to
|
||||||
|
*
|
||||||
|
* @param notaryPort The Raft port.
|
||||||
|
*/
|
||||||
|
public void notaryNodePort(Integer notaryPort) {
|
||||||
|
config = config.withValue("notaryNodeAddress", ConfigValueFactory.fromAnyRef(DEFAULT_HOST + ':' + notaryPort));
|
||||||
|
}
|
||||||
|
}
|
@ -63,7 +63,7 @@ dependencies {
|
|||||||
compile "com.fasterxml.jackson.core:jackson-databind:${jackson_version}"
|
compile "com.fasterxml.jackson.core:jackson-databind:${jackson_version}"
|
||||||
|
|
||||||
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
|
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
|
||||||
compile 'net.i2p.crypto:eddsa:0.2.0'
|
compile "net.i2p.crypto:eddsa:$eddsa_version"
|
||||||
|
|
||||||
// Bouncy castle support needed for X509 certificate manipulation
|
// Bouncy castle support needed for X509 certificate manipulation
|
||||||
compile "org.bouncycastle:bcprov-jdk15on:${bouncycastle_version}"
|
compile "org.bouncycastle:bcprov-jdk15on:${bouncycastle_version}"
|
||||||
|
@ -113,8 +113,17 @@ infix fun <T> ListenableFuture<T>.success(body: (T) -> Unit): ListenableFuture<T
|
|||||||
infix fun <T> ListenableFuture<T>.failure(body: (Throwable) -> Unit): ListenableFuture<T> = apply { failure(RunOnCallerThread, body) }
|
infix fun <T> ListenableFuture<T>.failure(body: (Throwable) -> Unit): ListenableFuture<T> = apply { failure(RunOnCallerThread, body) }
|
||||||
@Suppress("UNCHECKED_CAST") // We need the awkward cast because otherwise F cannot be nullable, even though it's safe.
|
@Suppress("UNCHECKED_CAST") // We need the awkward cast because otherwise F cannot be nullable, even though it's safe.
|
||||||
infix fun <F, T> ListenableFuture<F>.map(mapper: (F) -> T): ListenableFuture<T> = Futures.transform(this, { (mapper as (F?) -> T)(it) })
|
infix fun <F, T> ListenableFuture<F>.map(mapper: (F) -> T): ListenableFuture<T> = Futures.transform(this, { (mapper as (F?) -> T)(it) })
|
||||||
|
|
||||||
infix fun <F, T> ListenableFuture<F>.flatMap(mapper: (F) -> ListenableFuture<T>): ListenableFuture<T> = Futures.transformAsync(this) { mapper(it!!) }
|
infix fun <F, T> ListenableFuture<F>.flatMap(mapper: (F) -> ListenableFuture<T>): ListenableFuture<T> = Futures.transformAsync(this) { mapper(it!!) }
|
||||||
|
|
||||||
|
inline fun <T, reified R> Collection<T>.mapToArray(transform: (T) -> R) = run {
|
||||||
|
val iterator = iterator()
|
||||||
|
var expected = 0
|
||||||
|
Array(size) {
|
||||||
|
expected++ == it || throw UnsupportedOperationException("Array constructor is non-sequential!")
|
||||||
|
transform(iterator.next())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Executes the given block and sets the future to either the result, or any exception that was thrown. */
|
/** Executes the given block and sets the future to either the result, or any exception that was thrown. */
|
||||||
inline fun <T> SettableFuture<T>.catch(block: () -> T) {
|
inline fun <T> SettableFuture<T>.catch(block: () -> T) {
|
||||||
try {
|
try {
|
||||||
|
@ -8,6 +8,8 @@ buildscript {
|
|||||||
|
|
||||||
// If you bump this version you must re-bootstrap the codebase. See the README for more information.
|
// If you bump this version you must re-bootstrap the codebase. See the README for more information.
|
||||||
ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion")
|
ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion")
|
||||||
|
ext.bouncycastle_version = constants.getProperty("bouncycastleVersion")
|
||||||
|
ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion")
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
@ -37,7 +39,7 @@ bintrayConfig {
|
|||||||
projectUrl = 'https://github.com/corda/corda'
|
projectUrl = 'https://github.com/corda/corda'
|
||||||
gpgSign = true
|
gpgSign = true
|
||||||
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
|
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
|
||||||
publications = ['cordformation', 'quasar-utils']
|
publications = ['cordformation', 'quasar-utils', 'cordform-common']
|
||||||
license {
|
license {
|
||||||
name = 'Apache-2.0'
|
name = 'Apache-2.0'
|
||||||
url = 'https://www.apache.org/licenses/LICENSE-2.0'
|
url = 'https://www.apache.org/licenses/LICENSE-2.0'
|
||||||
|
@ -4,8 +4,6 @@ buildscript {
|
|||||||
file("$projectDir/../../constants.properties").withInputStream { constants.load(it) }
|
file("$projectDir/../../constants.properties").withInputStream { constants.load(it) }
|
||||||
|
|
||||||
ext.kotlin_version = constants.getProperty("kotlinVersion")
|
ext.kotlin_version = constants.getProperty("kotlinVersion")
|
||||||
ext.bouncycastle_version = constants.getProperty("bouncycastleVersion")
|
|
||||||
ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion")
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -46,11 +44,7 @@ dependencies {
|
|||||||
|
|
||||||
noderunner "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
noderunner "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
|
||||||
|
|
||||||
// TypeSafe Config: for simple and human friendly config files.
|
compile project(':cordform-common')
|
||||||
compile "com.typesafe:config:$typesafe_config_version"
|
|
||||||
|
|
||||||
// Bouncy Castle: for X.500 distinguished name manipulation
|
|
||||||
compile "org.bouncycastle:bcprov-jdk15on:${bouncycastle_version}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task createNodeRunner(type: Jar, dependsOn: [classes]) {
|
task createNodeRunner(type: Jar, dependsOn: [classes]) {
|
||||||
|
@ -1,19 +1,28 @@
|
|||||||
package net.corda.plugins
|
package net.corda.plugins
|
||||||
|
|
||||||
|
import static org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME
|
||||||
|
import net.corda.cordform.CordformContext
|
||||||
|
import net.corda.cordform.CordformDefinition
|
||||||
import org.apache.tools.ant.filters.FixCrLfFilter
|
import org.apache.tools.ant.filters.FixCrLfFilter
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.plugins.JavaPluginConvention
|
||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates nodes based on the configuration of this task in the gradle configuration DSL.
|
* Creates nodes based on the configuration of this task in the gradle configuration DSL.
|
||||||
*
|
*
|
||||||
* See documentation for examples.
|
* See documentation for examples.
|
||||||
*/
|
*/
|
||||||
class Cordform extends DefaultTask {
|
class Cordform extends DefaultTask {
|
||||||
protected Path directory = Paths.get("./build/nodes")
|
/**
|
||||||
protected List<Node> nodes = new ArrayList<Node>()
|
* Optionally the name of a CordformDefinition subclass to which all configuration will be delegated.
|
||||||
|
*/
|
||||||
|
String definitionClass
|
||||||
|
protected def directory = Paths.get("build", "nodes")
|
||||||
|
private def nodes = new ArrayList<Node>()
|
||||||
protected String networkMapNodeName
|
protected String networkMapNodeName
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +51,7 @@ class Cordform extends DefaultTask {
|
|||||||
* @param configureClosure A node configuration that will be deployed.
|
* @param configureClosure A node configuration that will be deployed.
|
||||||
*/
|
*/
|
||||||
void node(Closure configureClosure) {
|
void node(Closure configureClosure) {
|
||||||
nodes << project.configure(new Node(project), configureClosure)
|
nodes << (Node) project.configure(new Node(project), configureClosure)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,7 +60,7 @@ class Cordform extends DefaultTask {
|
|||||||
* @param name The name of the node as specified in the node configuration DSL.
|
* @param name The name of the node as specified in the node configuration DSL.
|
||||||
* @return A node instance.
|
* @return A node instance.
|
||||||
*/
|
*/
|
||||||
protected Node getNodeByName(String name) {
|
private Node getNodeByName(String name) {
|
||||||
for(Node node : nodes) {
|
for(Node node : nodes) {
|
||||||
if(node.name == name) {
|
if(node.name == name) {
|
||||||
return node
|
return node
|
||||||
@ -64,7 +73,7 @@ class Cordform extends DefaultTask {
|
|||||||
/**
|
/**
|
||||||
* Installs the run script into the nodes directory.
|
* Installs the run script into the nodes directory.
|
||||||
*/
|
*/
|
||||||
protected void installRunScript() {
|
private void installRunScript() {
|
||||||
project.copy {
|
project.copy {
|
||||||
from Cordformation.getPluginFile(project, "net/corda/plugins/runnodes.jar")
|
from Cordformation.getPluginFile(project, "net/corda/plugins/runnodes.jar")
|
||||||
fileMode 0755
|
fileMode 0755
|
||||||
@ -85,19 +94,49 @@ class Cordform extends DefaultTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The definitionClass needn't be compiled until just before our build method, so we load it manually via sourceSets.main.runtimeClasspath.
|
||||||
|
*/
|
||||||
|
private CordformDefinition loadCordformDefinition() {
|
||||||
|
def plugin = project.convention.getPlugin(JavaPluginConvention.class)
|
||||||
|
def classpath = plugin.sourceSets.getByName(MAIN_SOURCE_SET_NAME).runtimeClasspath
|
||||||
|
URL[] urls = classpath.files.collect { it.toURI().toURL() }
|
||||||
|
(CordformDefinition) new URLClassLoader(urls, CordformDefinition.classLoader).loadClass(definitionClass).newInstance()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This task action will create and install the nodes based on the node configurations added.
|
* This task action will create and install the nodes based on the node configurations added.
|
||||||
*/
|
*/
|
||||||
@TaskAction
|
@TaskAction
|
||||||
void build() {
|
void build() {
|
||||||
|
String networkMapNodeName
|
||||||
|
if (null != definitionClass) {
|
||||||
|
def cd = loadCordformDefinition()
|
||||||
|
networkMapNodeName = cd.networkMapNodeName.toString()
|
||||||
|
cd.nodeConfigurers.each { nc ->
|
||||||
|
node { Node it ->
|
||||||
|
nc.accept it
|
||||||
|
it.rootDir directory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cd.setup new CordformContext() {
|
||||||
|
Path baseDirectory(X500Name nodeName) {
|
||||||
|
project.projectDir.toPath().resolve(getNodeByName(nodeName.toString()).nodeDir.toPath())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
networkMapNodeName = this.networkMapNodeName
|
||||||
|
nodes.each {
|
||||||
|
it.rootDir directory
|
||||||
|
}
|
||||||
|
}
|
||||||
installRunScript()
|
installRunScript()
|
||||||
Node networkMapNode = getNodeByName(networkMapNodeName)
|
def networkMapNode = getNodeByName(networkMapNodeName)
|
||||||
nodes.each {
|
nodes.each {
|
||||||
if(it != networkMapNode) {
|
if(it != networkMapNode) {
|
||||||
it.networkMapAddress(networkMapNode.getP2PAddress(), networkMapNodeName)
|
it.networkMapAddress(networkMapNode.getP2PAddress(), networkMapNodeName)
|
||||||
}
|
}
|
||||||
it.build(directory.toFile())
|
it.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ class Cordformation implements Plugin<Project> {
|
|||||||
* @param filePathInJar The file in the JAR, relative to root, you wish to access.
|
* @param filePathInJar The file in the JAR, relative to root, you wish to access.
|
||||||
* @return A file handle to the file in the JAR.
|
* @return A file handle to the file in the JAR.
|
||||||
*/
|
*/
|
||||||
static File getPluginFile(Project project, String filePathInJar) {
|
protected static File getPluginFile(Project project, String filePathInJar) {
|
||||||
return project.rootProject.resources.text.fromArchiveEntry(project.rootProject.buildscript.configurations.classpath.find {
|
return project.rootProject.resources.text.fromArchiveEntry(project.rootProject.buildscript.configurations.classpath.find {
|
||||||
it.name.contains('cordformation')
|
it.name.contains('cordformation')
|
||||||
}, filePathInJar).asFile()
|
}, filePathInJar).asFile()
|
||||||
|
@ -1,33 +1,21 @@
|
|||||||
package net.corda.plugins
|
package net.corda.plugins
|
||||||
|
|
||||||
import com.typesafe.config.*
|
import com.typesafe.config.*
|
||||||
|
import net.corda.cordform.CordformNode
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a node that will be installed.
|
* Represents a node that will be installed.
|
||||||
*/
|
*/
|
||||||
class Node {
|
class Node extends CordformNode {
|
||||||
static final String NODEJAR_NAME = 'corda.jar'
|
static final String NODEJAR_NAME = 'corda.jar'
|
||||||
static final String WEBJAR_NAME = 'corda-webserver.jar'
|
static final String WEBJAR_NAME = 'corda-webserver.jar'
|
||||||
static final String DEFAULT_HOST = 'localhost'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Name of the node.
|
|
||||||
*/
|
|
||||||
public String name
|
|
||||||
/**
|
|
||||||
* A list of advertised services ID strings.
|
|
||||||
*/
|
|
||||||
protected List<String> advertisedServices = []
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If running a distributed notary, a list of node addresses for joining the Raft cluster
|
|
||||||
*/
|
|
||||||
protected List<String> notaryClusterAddresses = []
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@ -35,39 +23,10 @@ class Node {
|
|||||||
* @note Your app will be installed by default and does not need to be included here.
|
* @note Your app will be installed by default and does not need to be included here.
|
||||||
*/
|
*/
|
||||||
protected List<String> cordapps = []
|
protected List<String> cordapps = []
|
||||||
/**
|
|
||||||
* Set the RPC users for this node. This configuration block allows arbitrary configuration.
|
|
||||||
* The recommended current structure is:
|
|
||||||
* [[['username': "username_here", 'password': "password_here", 'permissions': ["permissions_here"]]]
|
|
||||||
* The above is a list to a map of keys to values using Groovy map and list shorthands.
|
|
||||||
*
|
|
||||||
* @note Incorrect configurations will not cause a DSL error.
|
|
||||||
*/
|
|
||||||
protected List<Map<String, Object>> rpcUsers = []
|
|
||||||
|
|
||||||
private Config config = ConfigFactory.empty()
|
protected File nodeDir
|
||||||
private File nodeDir
|
|
||||||
private Project project
|
private Project project
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the name of the node.
|
|
||||||
*
|
|
||||||
* @param name The node name.
|
|
||||||
*/
|
|
||||||
void name(String name) {
|
|
||||||
this.name = name
|
|
||||||
config = config.withValue("myLegalName", ConfigValueFactory.fromAnyRef(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the nearest city to the node.
|
|
||||||
*
|
|
||||||
* @param nearestCity The name of the nearest city to the node.
|
|
||||||
*/
|
|
||||||
void nearestCity(String nearestCity) {
|
|
||||||
config = config.withValue("nearestCity", ConfigValueFactory.fromAnyRef(nearestCity))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether this node will use HTTPS communication.
|
* Sets whether this node will use HTTPS communication.
|
||||||
*
|
*
|
||||||
@ -88,26 +47,6 @@ class Node {
|
|||||||
config = config.withValue("useTestClock", ConfigValueFactory.fromAnyRef(useTestClock))
|
config = config.withValue("useTestClock", ConfigValueFactory.fromAnyRef(useTestClock))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the Artemis P2P port for this node.
|
|
||||||
*
|
|
||||||
* @param p2pPort The Artemis messaging queue port.
|
|
||||||
*/
|
|
||||||
void p2pPort(Integer p2pPort) {
|
|
||||||
config = config.withValue("p2pAddress",
|
|
||||||
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$p2pPort".toString()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the Artemis RPC port for this node.
|
|
||||||
*
|
|
||||||
* @param rpcPort The Artemis RPC queue port.
|
|
||||||
*/
|
|
||||||
void rpcPort(Integer rpcPort) {
|
|
||||||
config = config.withValue("rpcAddress",
|
|
||||||
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$rpcPort".toString()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the HTTP web server port for this node.
|
* Set the HTTP web server port for this node.
|
||||||
*
|
*
|
||||||
@ -118,14 +57,6 @@ class Node {
|
|||||||
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$webPort".toString()))
|
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$webPort".toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the port which to bind the Copycat (Raft) node to
|
|
||||||
*/
|
|
||||||
void notaryNodePort(Integer notaryPort) {
|
|
||||||
config = config.withValue("notaryNodeAddress",
|
|
||||||
ConfigValueFactory.fromAnyRef("$DEFAULT_HOST:$notaryPort".toString()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the network map address for this node.
|
* Set the network map address for this node.
|
||||||
*
|
*
|
||||||
@ -155,16 +86,19 @@ class Node {
|
|||||||
this.project = project
|
this.project = project
|
||||||
}
|
}
|
||||||
|
|
||||||
void build(File rootDir) {
|
protected void rootDir(Path rootDir) {
|
||||||
def dirName
|
def dirName
|
||||||
try {
|
try {
|
||||||
X500Name x500Name = new X500Name(name)
|
X500Name x500Name = new X500Name(name)
|
||||||
dirName = x500Name.getRDNs(BCStyle.CN).getAt(0).getFirst().getValue().toString()
|
dirName = x500Name.getRDNs(BCStyle.CN).getAt(0).getFirst().getValue().toString()
|
||||||
} catch(IllegalArgumentException ex) {
|
} catch(IllegalArgumentException ignore) {
|
||||||
// Can't parse as an X500 name, use the full string
|
// Can't parse as an X500 name, use the full string
|
||||||
dirName = name
|
dirName = name
|
||||||
}
|
}
|
||||||
nodeDir = new File(rootDir, dirName.replaceAll("\\s",""))
|
nodeDir = new File(rootDir.toFile(), dirName.replaceAll("\\s",""))
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void build() {
|
||||||
configureRpcUsers()
|
configureRpcUsers()
|
||||||
installCordaJar()
|
installCordaJar()
|
||||||
installWebserverJar()
|
installWebserverJar()
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
rootProject.name = 'corda-gradle-plugins'
|
rootProject.name = 'corda-gradle-plugins'
|
||||||
include 'publish-utils'
|
include 'publish-utils'
|
||||||
include 'quasar-utils'
|
include 'quasar-utils'
|
||||||
include 'cordformation'
|
include 'cordformation'
|
||||||
|
include 'cordform-common'
|
||||||
|
project(':cordform-common').projectDir = new File("$settingsDir/../cordform-common")
|
||||||
|
@ -54,6 +54,7 @@ dependencies {
|
|||||||
compile project(':node-schemas')
|
compile project(':node-schemas')
|
||||||
compile project(':node-api')
|
compile project(':node-api')
|
||||||
compile project(':client:rpc')
|
compile project(':client:rpc')
|
||||||
|
compile project(':cordform-common')
|
||||||
|
|
||||||
compile "com.google.code.findbugs:jsr305:3.0.1"
|
compile "com.google.code.findbugs:jsr305:3.0.1"
|
||||||
|
|
||||||
|
@ -19,6 +19,10 @@ import net.corda.core.node.services.ServiceType
|
|||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.*
|
||||||
import net.corda.node.LOGS_DIRECTORY_NAME
|
import net.corda.node.LOGS_DIRECTORY_NAME
|
||||||
import net.corda.node.services.config.*
|
import net.corda.node.services.config.*
|
||||||
|
import net.corda.node.services.config.ConfigHelper
|
||||||
|
import net.corda.node.services.config.FullNodeConfiguration
|
||||||
|
import net.corda.node.services.config.VerifierType
|
||||||
|
import net.corda.node.services.config.configOf
|
||||||
import net.corda.node.services.network.NetworkMapService
|
import net.corda.node.services.network.NetworkMapService
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
import net.corda.node.utilities.ServiceIdentityGenerator
|
import net.corda.node.utilities.ServiceIdentityGenerator
|
||||||
@ -26,6 +30,8 @@ import net.corda.nodeapi.ArtemisMessagingComponent
|
|||||||
import net.corda.nodeapi.User
|
import net.corda.nodeapi.User
|
||||||
import net.corda.nodeapi.config.SSLConfiguration
|
import net.corda.nodeapi.config.SSLConfiguration
|
||||||
import net.corda.nodeapi.config.parseAs
|
import net.corda.nodeapi.config.parseAs
|
||||||
|
import net.corda.cordform.CordformNode
|
||||||
|
import net.corda.cordform.CordformContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
@ -57,7 +63,7 @@ private val log: Logger = loggerFor<DriverDSL>()
|
|||||||
/**
|
/**
|
||||||
* This is the interface that's exposed to DSL users.
|
* This is the interface that's exposed to DSL users.
|
||||||
*/
|
*/
|
||||||
interface DriverDSLExposedInterface {
|
interface DriverDSLExposedInterface : CordformContext {
|
||||||
/**
|
/**
|
||||||
* Starts a [net.corda.node.internal.Node] in a separate process.
|
* Starts a [net.corda.node.internal.Node] in a separate process.
|
||||||
*
|
*
|
||||||
@ -74,6 +80,8 @@ interface DriverDSLExposedInterface {
|
|||||||
verifierType: VerifierType = VerifierType.InMemory,
|
verifierType: VerifierType = VerifierType.InMemory,
|
||||||
customOverrides: Map<String, Any?> = emptyMap()): ListenableFuture<NodeHandle>
|
customOverrides: Map<String, Any?> = emptyMap()): ListenableFuture<NodeHandle>
|
||||||
|
|
||||||
|
fun startNodes(nodes: List<CordformNode>): List<ListenableFuture<NodeHandle>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a distributed notary cluster.
|
* Starts a distributed notary cluster.
|
||||||
*
|
*
|
||||||
@ -100,7 +108,7 @@ interface DriverDSLExposedInterface {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a network map service node. Note that only a single one should ever be running, so you will probably want
|
* Starts a network map service node. Note that only a single one should ever be running, so you will probably want
|
||||||
* to set networkMapStrategy to FalseNetworkMap in your [driver] call.
|
* to set networkMapStartStrategy to Dedicated(false) in your [driver] call.
|
||||||
*/
|
*/
|
||||||
fun startDedicatedNetworkMapService(): ListenableFuture<Unit>
|
fun startDedicatedNetworkMapService(): ListenableFuture<Unit>
|
||||||
|
|
||||||
@ -166,11 +174,6 @@ sealed class PortAllocation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class NetworkMapStartStrategy {
|
|
||||||
data class Dedicated(val startAutomatically: Boolean) : NetworkMapStartStrategy()
|
|
||||||
data class Nominated(val legalName: X500Name, val address: HostAndPort) : NetworkMapStartStrategy()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [driver] allows one to start up nodes like this:
|
* [driver] allows one to start up nodes like this:
|
||||||
* driver {
|
* driver {
|
||||||
@ -418,7 +421,6 @@ class DriverDSL(
|
|||||||
val networkMapStartStrategy: NetworkMapStartStrategy
|
val networkMapStartStrategy: NetworkMapStartStrategy
|
||||||
) : DriverDSLInternalInterface {
|
) : DriverDSLInternalInterface {
|
||||||
private val dedicatedNetworkMapAddress = portAllocation.nextHostAndPort()
|
private val dedicatedNetworkMapAddress = portAllocation.nextHostAndPort()
|
||||||
private val dedicatedNetworkMapLegalName = DUMMY_MAP.name
|
|
||||||
var _executorService: ListeningScheduledExecutorService? = null
|
var _executorService: ListeningScheduledExecutorService? = null
|
||||||
val executorService get() = _executorService!!
|
val executorService get() = _executorService!!
|
||||||
var _shutdownManager: ShutdownManager? = null
|
var _shutdownManager: ShutdownManager? = null
|
||||||
@ -471,57 +473,59 @@ class DriverDSL(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move to cmopanion
|
private fun networkMapServiceConfigLookup(networkMapCandidates: List<CordformNode>): (X500Name) -> Map<String, String>? {
|
||||||
private fun toServiceConfig(address: HostAndPort, legalName: X500Name) = mapOf(
|
return networkMapStartStrategy.run {
|
||||||
"address" to address.toString(),
|
when (this) {
|
||||||
"legalName" to legalName.toString()
|
is NetworkMapStartStrategy.Dedicated -> {
|
||||||
)
|
serviceConfig(dedicatedNetworkMapAddress).let {
|
||||||
|
{ _: X500Name -> it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is NetworkMapStartStrategy.Nominated -> {
|
||||||
|
serviceConfig(HostAndPort.fromString(networkMapCandidates.filter {
|
||||||
|
it.name == legalName.toString()
|
||||||
|
}.single().config.getString("p2pAddress"))).let {
|
||||||
|
{ nodeName: X500Name -> if (nodeName == legalName) null else it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun startNode(
|
override fun startNode(
|
||||||
providedName: X500Name?,
|
providedName: X500Name?,
|
||||||
advertisedServices: Set<ServiceInfo>,
|
advertisedServices: Set<ServiceInfo>,
|
||||||
rpcUsers: List<User>,
|
rpcUsers: List<User>,
|
||||||
verifierType: VerifierType,
|
verifierType: VerifierType,
|
||||||
customOverrides: Map<String, Any?>
|
customOverrides: Map<String, Any?>): ListenableFuture<NodeHandle> {
|
||||||
): ListenableFuture<NodeHandle> {
|
|
||||||
val p2pAddress = portAllocation.nextHostAndPort()
|
val p2pAddress = portAllocation.nextHostAndPort()
|
||||||
val rpcAddress = portAllocation.nextHostAndPort()
|
val rpcAddress = portAllocation.nextHostAndPort()
|
||||||
val webAddress = portAllocation.nextHostAndPort()
|
val webAddress = portAllocation.nextHostAndPort()
|
||||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
|
||||||
// TODO: Derive name from the full picked name, don't just wrap the common name
|
// TODO: Derive name from the full picked name, don't just wrap the common name
|
||||||
val name = providedName ?: X509Utilities.getDevX509Name("${oneOf(names).commonName}-${p2pAddress.port}")
|
val name = providedName ?: X509Utilities.getDevX509Name("${oneOf(names).commonName}-${p2pAddress.port}")
|
||||||
val baseDirectory = driverDirectory / name.commonName
|
return startNode(p2pAddress, webAddress, name, configOf(
|
||||||
val configOverrides = configOf(
|
|
||||||
"myLegalName" to name.toString(),
|
"myLegalName" to name.toString(),
|
||||||
"p2pAddress" to p2pAddress.toString(),
|
"p2pAddress" to p2pAddress.toString(),
|
||||||
"rpcAddress" to rpcAddress.toString(),
|
"rpcAddress" to rpcAddress.toString(),
|
||||||
"webAddress" to webAddress.toString(),
|
"webAddress" to webAddress.toString(),
|
||||||
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
|
"extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
|
||||||
"networkMapService" to when (networkMapStartStrategy) {
|
"networkMapService" to networkMapServiceConfigLookup(emptyList())(name),
|
||||||
is NetworkMapStartStrategy.Dedicated -> toServiceConfig(dedicatedNetworkMapAddress, dedicatedNetworkMapLegalName)
|
|
||||||
is NetworkMapStartStrategy.Nominated -> networkMapStartStrategy.run {
|
|
||||||
if (name != legalName) {
|
|
||||||
toServiceConfig(address, legalName)
|
|
||||||
} else {
|
|
||||||
p2pAddress == address || throw IllegalArgumentException("Passed-in address $address of nominated network map $legalName is wrong, it should be: $p2pAddress")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"useTestClock" to useTestClock,
|
"useTestClock" to useTestClock,
|
||||||
"rpcUsers" to rpcUsers.map { it.toMap() },
|
"rpcUsers" to rpcUsers.map { it.toMap() },
|
||||||
"verifierType" to verifierType.name
|
"verifierType" to verifierType.name
|
||||||
) + customOverrides
|
) + customOverrides)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startNode(p2pAddress: HostAndPort, webAddress: HostAndPort, nodeName: X500Name, configOverrides: Config) = run {
|
||||||
|
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||||
val config = ConfigHelper.loadConfig(
|
val config = ConfigHelper.loadConfig(
|
||||||
baseDirectory = baseDirectory,
|
baseDirectory = baseDirectory(nodeName),
|
||||||
allowMissingConfig = true,
|
allowMissingConfig = true,
|
||||||
configOverrides = configOverrides)
|
configOverrides = configOverrides)
|
||||||
val configuration = config.parseAs<FullNodeConfiguration>()
|
val configuration = config.parseAs<FullNodeConfiguration>()
|
||||||
|
|
||||||
val processFuture = startNode(executorService, configuration, config, quasarJarPath, debugPort, systemProperties)
|
val processFuture = startNode(executorService, configuration, config, quasarJarPath, debugPort, systemProperties)
|
||||||
registerProcess(processFuture)
|
registerProcess(processFuture)
|
||||||
return processFuture.flatMap { process ->
|
processFuture.flatMap { process ->
|
||||||
// We continue to use SSL enabled port for RPC when its for node user.
|
// We continue to use SSL enabled port for RPC when its for node user.
|
||||||
establishRpc(p2pAddress, configuration).flatMap { rpc ->
|
establishRpc(p2pAddress, configuration).flatMap { rpc ->
|
||||||
rpc.waitUntilRegisteredWithNetworkMap().map {
|
rpc.waitUntilRegisteredWithNetworkMap().map {
|
||||||
@ -531,6 +535,22 @@ class DriverDSL(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun startNodes(nodes: List<CordformNode>): List<ListenableFuture<NodeHandle>> {
|
||||||
|
val networkMapServiceConfigLookup = networkMapServiceConfigLookup(nodes)
|
||||||
|
return nodes.map {
|
||||||
|
val p2pAddress = HostAndPort.fromString(it.config.getString("p2pAddress")); portAllocation.nextHostAndPort()
|
||||||
|
portAllocation.nextHostAndPort() // rpcAddress
|
||||||
|
val webAddress = portAllocation.nextHostAndPort()
|
||||||
|
val name = X500Name(it.name)
|
||||||
|
startNode(p2pAddress, webAddress, name, it.config + mapOf(
|
||||||
|
"extraAdvertisedServiceIds" to it.advertisedServices,
|
||||||
|
"networkMapService" to networkMapServiceConfigLookup(name),
|
||||||
|
"rpcUsers" to it.rpcUsers,
|
||||||
|
"notaryClusterAddresses" to it.notaryClusterAddresses
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun startNotaryCluster(
|
override fun startNotaryCluster(
|
||||||
notaryName: X500Name,
|
notaryName: X500Name,
|
||||||
clusterSize: Int,
|
clusterSize: Int,
|
||||||
@ -539,7 +559,7 @@ class DriverDSL(
|
|||||||
rpcUsers: List<User>
|
rpcUsers: List<User>
|
||||||
): ListenableFuture<Pair<Party, List<NodeHandle>>> {
|
): ListenableFuture<Pair<Party, List<NodeHandle>>> {
|
||||||
val nodeNames = (1..clusterSize).map { DUMMY_NOTARY.name.appendToCommonName(it.toString()) }
|
val nodeNames = (1..clusterSize).map { DUMMY_NOTARY.name.appendToCommonName(it.toString()) }
|
||||||
val paths = nodeNames.map { driverDirectory / it.commonName }
|
val paths = nodeNames.map { baseDirectory(it) }
|
||||||
ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName)
|
ServiceIdentityGenerator.generateToDisk(paths, type.id, notaryName)
|
||||||
|
|
||||||
val serviceInfo = ServiceInfo(type, notaryName)
|
val serviceInfo = ServiceInfo(type, notaryName)
|
||||||
@ -592,20 +612,22 @@ class DriverDSL(
|
|||||||
Executors.newScheduledThreadPool(2, ThreadFactoryBuilder().setNameFormat("driver-pool-thread-%d").build())
|
Executors.newScheduledThreadPool(2, ThreadFactoryBuilder().setNameFormat("driver-pool-thread-%d").build())
|
||||||
)
|
)
|
||||||
_shutdownManager = ShutdownManager(executorService)
|
_shutdownManager = ShutdownManager(executorService)
|
||||||
if (networkMapStartStrategy is NetworkMapStartStrategy.Dedicated && networkMapStartStrategy.startAutomatically) {
|
if (networkMapStartStrategy.startDedicated) {
|
||||||
startDedicatedNetworkMapService()
|
startDedicatedNetworkMapService()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun baseDirectory(nodeName: X500Name) = driverDirectory / nodeName.commonName.replace(WHITESPACE, "")
|
||||||
|
|
||||||
override fun startDedicatedNetworkMapService(): ListenableFuture<Unit> {
|
override fun startDedicatedNetworkMapService(): ListenableFuture<Unit> {
|
||||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||||
val apiAddress = portAllocation.nextHostAndPort().toString()
|
val apiAddress = portAllocation.nextHostAndPort().toString()
|
||||||
val baseDirectory = driverDirectory / dedicatedNetworkMapLegalName.commonName
|
val networkMapLegalName = networkMapStartStrategy.legalName
|
||||||
val config = ConfigHelper.loadConfig(
|
val config = ConfigHelper.loadConfig(
|
||||||
baseDirectory = baseDirectory,
|
baseDirectory = baseDirectory(networkMapLegalName),
|
||||||
allowMissingConfig = true,
|
allowMissingConfig = true,
|
||||||
configOverrides = configOf(
|
configOverrides = configOf(
|
||||||
"myLegalName" to dedicatedNetworkMapLegalName.toString(),
|
"myLegalName" to networkMapLegalName.toString(),
|
||||||
// TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all
|
// TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all
|
||||||
// node port numbers to be shifted, so all demos and docs need to be updated accordingly.
|
// node port numbers to be shifted, so all demos and docs need to be updated accordingly.
|
||||||
"webAddress" to apiAddress,
|
"webAddress" to apiAddress,
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package net.corda.node.driver
|
||||||
|
|
||||||
|
import com.google.common.net.HostAndPort
|
||||||
|
import net.corda.core.utilities.DUMMY_MAP
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
|
|
||||||
|
sealed class NetworkMapStartStrategy {
|
||||||
|
internal abstract val startDedicated: Boolean
|
||||||
|
internal abstract val legalName: X500Name
|
||||||
|
internal fun serviceConfig(address: HostAndPort) = mapOf(
|
||||||
|
"address" to address.toString(),
|
||||||
|
"legalName" to legalName.toString()
|
||||||
|
)
|
||||||
|
|
||||||
|
class Dedicated(startAutomatically: Boolean) : NetworkMapStartStrategy() {
|
||||||
|
override val startDedicated = startAutomatically
|
||||||
|
override val legalName = DUMMY_MAP.name
|
||||||
|
}
|
||||||
|
|
||||||
|
class Nominated(override val legalName: X500Name) : NetworkMapStartStrategy() {
|
||||||
|
override val startDedicated = false
|
||||||
|
}
|
||||||
|
}
|
@ -4,12 +4,12 @@ import net.corda.core.crypto.CompositeKey
|
|||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
|
import net.corda.core.serialization.storageKryo
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
|
||||||
|
|
||||||
object ServiceIdentityGenerator {
|
object ServiceIdentityGenerator {
|
||||||
private val log = loggerFor<ServiceIdentityGenerator>()
|
private val log = loggerFor<ServiceIdentityGenerator>()
|
||||||
@ -36,17 +36,8 @@ object ServiceIdentityGenerator {
|
|||||||
val privateKeyFile = "$serviceId-private-key"
|
val privateKeyFile = "$serviceId-private-key"
|
||||||
val publicKeyFile = "$serviceId-public"
|
val publicKeyFile = "$serviceId-public"
|
||||||
notaryParty.writeToFile(dir.resolve(publicKeyFile))
|
notaryParty.writeToFile(dir.resolve(publicKeyFile))
|
||||||
keyPair.serialize().writeToFile(dir.resolve(privateKeyFile))
|
// Use storageKryo as our whitelist is not available in the gradle build environment:
|
||||||
|
keyPair.serialize(storageKryo()).writeToFile(dir.resolve(privateKeyFile))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
|
||||||
val dirs = args[0].split("|").map { Paths.get(it) }
|
|
||||||
val serviceId = args[1]
|
|
||||||
val serviceName = X500Name(args[2])
|
|
||||||
val quorumSize = args.getOrNull(3)?.toInt() ?: 1
|
|
||||||
|
|
||||||
println("Generating service identity for \"$serviceName\"")
|
|
||||||
ServiceIdentityGenerator.generateToDisk(dirs, serviceId, serviceName, quorumSize)
|
|
||||||
}
|
|
@ -8,13 +8,6 @@ apply plugin: 'net.corda.plugins.publish-utils'
|
|||||||
apply plugin: 'net.corda.plugins.cordformation'
|
apply plugin: 'net.corda.plugins.cordformation'
|
||||||
apply plugin: 'maven-publish'
|
apply plugin: 'maven-publish'
|
||||||
|
|
||||||
ext {
|
|
||||||
deployTo = "build/nodes"
|
|
||||||
notaryType = "corda.notary.validating.raft"
|
|
||||||
notaryName = "CN=Raft,O=R3,OU=corda,L=Zurich,C=CH"
|
|
||||||
advertisedNotary = "$notaryType|$notaryName"
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
integrationTestCompile.extendsFrom testCompile
|
integrationTestCompile.extendsFrom testCompile
|
||||||
integrationTestRuntime.extendsFrom testRuntime
|
integrationTestRuntime.extendsFrom testRuntime
|
||||||
@ -31,6 +24,7 @@ dependencies {
|
|||||||
compile project(':client:jfx')
|
compile project(':client:jfx')
|
||||||
compile project(':client:rpc')
|
compile project(':client:rpc')
|
||||||
compile project(':test-utils')
|
compile project(':test-utils')
|
||||||
|
compile project(':cordform-common')
|
||||||
|
|
||||||
// Javax is required for webapis
|
// Javax is required for webapis
|
||||||
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
||||||
@ -55,76 +49,15 @@ publishing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task cleanNodes {
|
task deployNodesSingle(type: Cordform, dependsOn: 'jar') {
|
||||||
doLast {
|
definitionClass = 'net.corda.notarydemo.SingleNotaryCordform'
|
||||||
delete deployTo
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task generateNotaryIdentity(type: JavaExec, dependsOn: 'cleanNodes') {
|
task deployNodesRaft(type: Cordform, dependsOn: 'jar') {
|
||||||
classpath = sourceSets.main.runtimeClasspath
|
definitionClass = 'net.corda.notarydemo.RaftNotaryCordform'
|
||||||
main = "net.corda.node.utilities.ServiceIdentityGeneratorKt"
|
|
||||||
def nodeDirs = ["$deployTo/Notary1",
|
|
||||||
"$deployTo/Notary2",
|
|
||||||
"$deployTo/Notary3"].join("|")
|
|
||||||
args = [nodeDirs, notaryType, notaryName]
|
|
||||||
}
|
|
||||||
|
|
||||||
task deployNodes(type: Cordform, dependsOn: ['jar', generateNotaryIdentity]) {
|
|
||||||
directory deployTo
|
|
||||||
networkMap "CN=Notary 1,O=R3,OU=corda,L=London,C=UK"
|
|
||||||
node {
|
|
||||||
name "CN=Alice Corp,O=Alice Corp,L=London,C=UK"
|
|
||||||
nearestCity "London"
|
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10002
|
|
||||||
rpcPort 10003
|
|
||||||
cordapps = []
|
|
||||||
rpcUsers = [['username': "demo", 'password': "demo", 'permissions': [
|
|
||||||
'StartFlow.net.corda.notarydemo.flows.DummyIssueAndMove',
|
|
||||||
'StartFlow.net.corda.notarydemo.flows.RPCStartableNotaryFlowClient'
|
|
||||||
]]]
|
|
||||||
}
|
|
||||||
node {
|
|
||||||
name "CN=Bob Plc,O=Bob Plc,L=London,C=UK"
|
|
||||||
nearestCity "New York"
|
|
||||||
advertisedServices = []
|
|
||||||
p2pPort 10005
|
|
||||||
rpcPort 10006
|
|
||||||
cordapps = []
|
|
||||||
}
|
|
||||||
node {
|
|
||||||
name "CN=Notary 1,O=R3,OU=corda,L=London,C=UK"
|
|
||||||
nearestCity "London"
|
|
||||||
advertisedServices = [advertisedNotary]
|
|
||||||
p2pPort 10009
|
|
||||||
rpcPort 10010
|
|
||||||
cordapps = []
|
|
||||||
notaryNodePort 10008
|
|
||||||
}
|
|
||||||
node {
|
|
||||||
name "CN=Notary 2,O=R3,OU=corda,L=London,C=UK"
|
|
||||||
nearestCity "London"
|
|
||||||
advertisedServices = [advertisedNotary]
|
|
||||||
p2pPort 10013
|
|
||||||
rpcPort 10014
|
|
||||||
cordapps = []
|
|
||||||
notaryNodePort 10012
|
|
||||||
notaryClusterAddresses = ["localhost:10008"]
|
|
||||||
}
|
|
||||||
node {
|
|
||||||
name "CN=Notary 3,O=R3,OU=corda,L=London,C=UK"
|
|
||||||
nearestCity "London"
|
|
||||||
advertisedServices = [advertisedNotary]
|
|
||||||
p2pPort 10017
|
|
||||||
rpcPort 10018
|
|
||||||
cordapps = []
|
|
||||||
notaryNodePort 10016
|
|
||||||
notaryClusterAddresses = ["localhost:10008"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task notarise(type: JavaExec) {
|
task notarise(type: JavaExec) {
|
||||||
classpath = sourceSets.main.runtimeClasspath
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
main = 'net.corda.notarydemo.NotaryDemoKt'
|
main = 'net.corda.notarydemo.NotariseKt'
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package net.corda.demorun
|
||||||
|
|
||||||
|
import net.corda.node.driver.NetworkMapStartStrategy
|
||||||
|
import net.corda.node.driver.PortAllocation
|
||||||
|
import net.corda.node.driver.driver
|
||||||
|
import net.corda.cordform.CordformDefinition
|
||||||
|
import net.corda.cordform.CordformNode
|
||||||
|
|
||||||
|
fun CordformDefinition.node(configure: CordformNode.() -> Unit) = addNode { cordformNode -> cordformNode.configure() }
|
||||||
|
|
||||||
|
fun CordformDefinition.clean() {
|
||||||
|
System.err.println("Deleting: $driverDirectory")
|
||||||
|
driverDirectory.toFile().deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and starts all nodes required for the demo.
|
||||||
|
*/
|
||||||
|
fun CordformDefinition.runNodes() = driver(
|
||||||
|
isDebug = true,
|
||||||
|
driverDirectory = driverDirectory,
|
||||||
|
networkMapStartStrategy = NetworkMapStartStrategy.Nominated(networkMapNodeName),
|
||||||
|
portAllocation = PortAllocation.Incremental(10001)
|
||||||
|
) {
|
||||||
|
setup(this)
|
||||||
|
startNodes(nodeConfigurers.map { configurer -> CordformNode().also { configurer.accept(it) } })
|
||||||
|
waitForAllNodesToFinish()
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package net.corda.notarydemo
|
||||||
|
|
||||||
|
import net.corda.demorun.clean
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
listOf(SingleNotaryCordform, RaftNotaryCordform).forEach {
|
||||||
|
it.clean()
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +0,0 @@
|
|||||||
package net.corda.notarydemo
|
|
||||||
|
|
||||||
import com.google.common.net.HostAndPort
|
|
||||||
import net.corda.core.crypto.appendToCommonName
|
|
||||||
import net.corda.core.div
|
|
||||||
import net.corda.core.utilities.ALICE
|
|
||||||
import net.corda.core.utilities.BOB
|
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
|
||||||
import net.corda.node.driver.NetworkMapStartStrategy
|
|
||||||
import net.corda.node.driver.PortAllocation
|
|
||||||
import net.corda.node.driver.driver
|
|
||||||
import net.corda.node.services.startFlowPermission
|
|
||||||
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
|
||||||
import net.corda.nodeapi.User
|
|
||||||
import net.corda.notarydemo.flows.DummyIssueAndMove
|
|
||||||
import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient
|
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
|
|
||||||
/** Creates and starts all nodes required for the demo. */
|
|
||||||
fun main(args: Array<String>) {
|
|
||||||
val demoUser = listOf(User("demo", "demo", setOf(startFlowPermission<DummyIssueAndMove>(), startFlowPermission<RPCStartableNotaryFlowClient>())))
|
|
||||||
val networkMap = NetworkMapStartStrategy.Nominated(DUMMY_NOTARY.name.appendToCommonName("1"), HostAndPort.fromParts("localhost", 10009))
|
|
||||||
driver(isDebug = true, driverDirectory = "build" / "notary-demo-nodes", networkMapStartStrategy = networkMap, portAllocation = PortAllocation.Incremental(10001)) {
|
|
||||||
startNode(ALICE.name, rpcUsers = demoUser)
|
|
||||||
startNode(BOB.name)
|
|
||||||
startNotaryCluster(X500Name("CN=Raft,O=R3,OU=corda,L=Zurich,C=CH"), clusterSize = 3, type = RaftValidatingNotaryService.type)
|
|
||||||
waitForAllNodesToFinish()
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,73 @@
|
|||||||
|
package net.corda.notarydemo
|
||||||
|
|
||||||
|
import net.corda.core.crypto.appendToCommonName
|
||||||
|
import net.corda.core.div
|
||||||
|
import net.corda.core.node.services.ServiceInfo
|
||||||
|
import net.corda.core.utilities.ALICE
|
||||||
|
import net.corda.core.utilities.BOB
|
||||||
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
|
import net.corda.demorun.node
|
||||||
|
import net.corda.demorun.runNodes
|
||||||
|
import net.corda.node.services.startFlowPermission
|
||||||
|
import net.corda.node.services.transactions.RaftValidatingNotaryService
|
||||||
|
import net.corda.node.utilities.ServiceIdentityGenerator
|
||||||
|
import net.corda.nodeapi.User
|
||||||
|
import net.corda.notarydemo.flows.DummyIssueAndMove
|
||||||
|
import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient
|
||||||
|
import net.corda.cordform.CordformDefinition
|
||||||
|
import net.corda.cordform.CordformContext
|
||||||
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
|
|
||||||
|
fun main(args: Array<String>) = RaftNotaryCordform.runNodes()
|
||||||
|
|
||||||
|
private val notaryNames = (1..3).map { DUMMY_NOTARY.name.appendToCommonName(" $it") }
|
||||||
|
|
||||||
|
object RaftNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", notaryNames[0]) {
|
||||||
|
private val advertisedNotary = ServiceInfo(RaftValidatingNotaryService.type, X500Name("CN=Raft,O=R3,OU=corda,L=Zurich,C=CH"))
|
||||||
|
|
||||||
|
init {
|
||||||
|
node {
|
||||||
|
name(ALICE.name.toString())
|
||||||
|
nearestCity("London")
|
||||||
|
p2pPort(10002)
|
||||||
|
rpcPort(10003)
|
||||||
|
rpcUsers = listOf(User("demo", "demo", setOf(startFlowPermission<DummyIssueAndMove>(), startFlowPermission<RPCStartableNotaryFlowClient>())).toMap())
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
name(BOB.name.toString())
|
||||||
|
nearestCity("New York")
|
||||||
|
p2pPort(10005)
|
||||||
|
rpcPort(10006)
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
name(notaryNames[0].toString())
|
||||||
|
nearestCity("London")
|
||||||
|
advertisedServices = listOf(advertisedNotary.toString())
|
||||||
|
p2pPort(10009)
|
||||||
|
rpcPort(10010)
|
||||||
|
notaryNodePort(10008)
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
name(notaryNames[1].toString())
|
||||||
|
nearestCity("London")
|
||||||
|
advertisedServices = listOf(advertisedNotary.toString())
|
||||||
|
p2pPort(10013)
|
||||||
|
rpcPort(10014)
|
||||||
|
notaryNodePort(10012)
|
||||||
|
notaryClusterAddresses = listOf("localhost:10008")
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
name(notaryNames[2].toString())
|
||||||
|
nearestCity("London")
|
||||||
|
advertisedServices = listOf(advertisedNotary.toString())
|
||||||
|
p2pPort(10017)
|
||||||
|
rpcPort(10018)
|
||||||
|
notaryNodePort(10016)
|
||||||
|
notaryClusterAddresses = listOf("localhost:10008")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setup(context: CordformContext) {
|
||||||
|
ServiceIdentityGenerator.generateToDisk(notaryNames.map { context.baseDirectory(it) }, advertisedNotary.type.id, advertisedNotary.name!!)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package net.corda.notarydemo
|
||||||
|
|
||||||
|
import net.corda.core.div
|
||||||
|
import net.corda.core.node.services.ServiceInfo
|
||||||
|
import net.corda.core.utilities.ALICE
|
||||||
|
import net.corda.core.utilities.BOB
|
||||||
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
|
import net.corda.demorun.node
|
||||||
|
import net.corda.demorun.runNodes
|
||||||
|
import net.corda.node.services.startFlowPermission
|
||||||
|
import net.corda.node.services.transactions.ValidatingNotaryService
|
||||||
|
import net.corda.nodeapi.User
|
||||||
|
import net.corda.notarydemo.flows.DummyIssueAndMove
|
||||||
|
import net.corda.notarydemo.flows.RPCStartableNotaryFlowClient
|
||||||
|
import net.corda.cordform.CordformDefinition
|
||||||
|
import net.corda.cordform.CordformContext
|
||||||
|
|
||||||
|
fun main(args: Array<String>) = SingleNotaryCordform.runNodes()
|
||||||
|
|
||||||
|
object SingleNotaryCordform : CordformDefinition("build" / "notary-demo-nodes", DUMMY_NOTARY.name) {
|
||||||
|
init {
|
||||||
|
node {
|
||||||
|
name(ALICE.name.toString())
|
||||||
|
nearestCity("London")
|
||||||
|
p2pPort(10002)
|
||||||
|
rpcPort(10003)
|
||||||
|
rpcUsers = listOf(User("demo", "demo", setOf(startFlowPermission<DummyIssueAndMove>(), startFlowPermission<RPCStartableNotaryFlowClient>())).toMap())
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
name(BOB.name.toString())
|
||||||
|
nearestCity("New York")
|
||||||
|
p2pPort(10005)
|
||||||
|
rpcPort(10006)
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
name(DUMMY_NOTARY.name.toString())
|
||||||
|
nearestCity("London")
|
||||||
|
advertisedServices = listOf(ServiceInfo(ValidatingNotaryService.type).toString())
|
||||||
|
p2pPort(10009)
|
||||||
|
rpcPort(10010)
|
||||||
|
notaryNodePort(10008)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setup(context: CordformContext) {}
|
||||||
|
}
|
@ -31,4 +31,5 @@ include 'samples:irs-demo'
|
|||||||
include 'samples:network-visualiser'
|
include 'samples:network-visualiser'
|
||||||
include 'samples:simm-valuation-demo'
|
include 'samples:simm-valuation-demo'
|
||||||
include 'samples:raft-notary-demo'
|
include 'samples:raft-notary-demo'
|
||||||
include 'samples:bank-of-corda-demo'
|
include 'samples:bank-of-corda-demo'
|
||||||
|
include 'cordform-common'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user