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:
Andrzej Cichocki 2017-05-18 15:58:35 +01:00 committed by GitHub
parent edf269dbe5
commit c8d71a96f5
28 changed files with 477 additions and 251 deletions

2
.gitignore vendored
View File

@ -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
View File

@ -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" />

View File

@ -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.
} }
} }

View File

@ -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

View 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"
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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));
}
}

View File

@ -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}"

View File

@ -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 {

View File

@ -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'

View File

@ -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]) {

View File

@ -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()
} }
} }
} }

View File

@ -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()

View File

@ -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()

View File

@ -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")

View File

@ -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"

View File

@ -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,

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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'
} }

View File

@ -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()
}

View File

@ -0,0 +1,9 @@
package net.corda.notarydemo
import net.corda.demorun.clean
fun main(args: Array<String>) {
listOf(SingleNotaryCordform, RaftNotaryCordform).forEach {
it.clean()
}
}

View File

@ -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()
}
}

View File

@ -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!!)
}
}

View File

@ -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) {}
}

View File

@ -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'