diff --git a/blacklist/.idea/runConfigurations/Debug_CorDapp.xml b/blacklist/.idea/runConfigurations/Debug_CorDapp.xml
deleted file mode 100644
index 99ec0b2da..000000000
--- a/blacklist/.idea/runConfigurations/Debug_CorDapp.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/blacklist/.idea/runConfigurations/Run_Contract_Tests.xml b/blacklist/.idea/runConfigurations/Run_Contract_Tests.xml
deleted file mode 100644
index ccd8a0cd7..000000000
--- a/blacklist/.idea/runConfigurations/Run_Contract_Tests.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/blacklist/.idea/runConfigurations/Run_Flow_Tests.xml b/blacklist/.idea/runConfigurations/Run_Flow_Tests.xml
deleted file mode 100644
index a859aa081..000000000
--- a/blacklist/.idea/runConfigurations/Run_Flow_Tests.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/blacklist/.idea/runConfigurations/Unit_tests.xml b/blacklist/.idea/runConfigurations/Unit_tests.xml
deleted file mode 100644
index 0d8cb29ee..000000000
--- a/blacklist/.idea/runConfigurations/Unit_tests.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/blacklist/.idea/runConfigurations/Upload_blacklist.xml b/blacklist/.idea/runConfigurations/Upload_blacklist.xml
deleted file mode 100644
index 5f121c8e3..000000000
--- a/blacklist/.idea/runConfigurations/Upload_blacklist.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/blacklist/README.md b/blacklist/README.md
index 29d0a6cd4..c8720fd1a 100644
--- a/blacklist/README.md
+++ b/blacklist/README.md
@@ -29,14 +29,36 @@ See https://docs.corda.net/getting-set-up.html.
See https://docs.corda.net/tutorial-cordapp.html#running-the-example-cordapp.
+Java nodes
+```
+./gradlew workflows-java:deployNodes
+```
+```
+./workflows-java/build/nodes/runnodes
+```
+
+Kotlin nodes
+```
+./gradlew workflows-kotlin:deployNodes
+```
+```
+./workflows-kotlin/build/nodes/runnodes
+```
+
## Uploading the blacklist:
+Note: The nodes must be running before attempting this step
Before attempting to reach any agreements, you must upload the blacklist as an attachment to each node that you want to
be able to *initiate* an agreement. The blacklist can be uploaded via RPC by running the following command from the
project's root folder:
-* Unix/Mac OSX: `./gradlew uploadBlacklist`
-* Windows: `gradlew uploadBlacklist`
+Java version
+* Unix/Mac OSX: ` ./gradlew clients-java:uploadBlacklist`
+* Windows: `gradlew clients-java:uploadBlacklist`
+
+Kotlin version
+* Unix/Mac OSX: `./gradlew clients-kotlin:uploadBlacklist`
+* Windows: `gradlew clinets-kotlin:uploadBlacklist`
Or by running the `Upload blacklist` run configuration from IntelliJ.
@@ -52,7 +74,7 @@ the shell of Monogram Bank:
start ProposeFlow agreementTxt: "A and B agree Y", counterparty: "Hiseville Deposit Bank", untrustedPartiesAttachment: "4CEC607599723D7E0393EB5F05F24562732CD1B217DEAEDEABD4C25AFE5B333A"
-If you now run `run vaultQuery contractStateType: net.corda.examples.attachments.state.AgreementState` on either the
+If you now run `run vaultQuery contractStateType: net.corda.examples.attachments.states.AgreementState` on either the
Monogram Bank or Hiseville Deposit Bank node, you should see the agreement stored:
data: !
diff --git a/blacklist/build.gradle b/blacklist/build.gradle
index 7b59bda44..f438347eb 100644
--- a/blacklist/build.gradle
+++ b/blacklist/build.gradle
@@ -1,4 +1,5 @@
buildscript {
+ ext.kotlin_version = '1.3.31'
Properties constants = new Properties()
file("$projectDir/../constants.properties").withInputStream { constants.load(it) }
@@ -12,6 +13,8 @@ buildscript {
log4j_version = constants.getProperty("log4jVersion")
slf4j_version = constants.getProperty("slf4jVersion")
corda_platform_version = constants.getProperty("platformVersion")
+ spring_boot_version = '2.0.2.RELEASE'
+ ext.spring_boot_gradle_plugin_version = '2.0.2.RELEASE'
}
repositories {
@@ -26,114 +29,39 @@ buildscript {
classpath "net.corda.plugins:cordapp:$corda_gradle_plugins_version"
classpath "net.corda.plugins:cordformation:$corda_gradle_plugins_version"
classpath "net.corda.plugins:quasar-utils:$corda_gradle_plugins_version"
+ classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_gradle_plugin_version"
}
}
-apply from: 'repositories.gradle'
+allprojects {
-apply plugin: 'kotlin'
-apply plugin: 'net.corda.plugins.cordapp'
-apply plugin: 'net.corda.plugins.cordformation'
-apply plugin: 'net.corda.plugins.quasar-utils'
-
-cordapp {
- targetPlatformVersion corda_platform_version.toInteger()
- minimumPlatformVersion corda_platform_version.toInteger()
- contract {
- name "Blacklist"
- vendor "Corda Open Source"
- licence "Apache License, Version 2.0"
- versionId 1
- }
- workflow {
- name "Blacklist"
- vendor "Corda Open Source"
- licence "Apache License, Version 2.0"
- versionId 1
- }
-}
-
-dependencies {
- compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
- testCompile "junit:junit:$junit_version"
-
- // This is for the deployNodes task only.
- cordaRuntime "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
-
- // Corda dependencies.
- cordaCompile "$corda_release_group:corda-core:$corda_release_version"
- cordaCompile "$corda_release_group:corda-jackson:$corda_release_version"
- cordaCompile "$corda_release_group:corda-rpc:$corda_release_version"
- cordaCompile "$corda_release_group:corda-node-api:$corda_release_version"
- cordaRuntime "$corda_release_group:corda:$corda_release_version"
-
- testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
-
- // CorDapp dependencies.
-}
-
-jar {
- // CorDapps do not configure the Node's logging!
- exclude '**/log4j2*.xml'
-}
-
-tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
- kotlinOptions {
- languageVersion = "1.2"
- apiVersion = "1.2"
- jvmTarget = "1.8"
- javaParameters = true // Useful for reflection.
+ repositories {
+ mavenLocal()
+ jcenter()
+ mavenCentral()
+ maven { url 'https://jitpack.io' }
+ maven { url 'https://ci-artifactory.corda.r3cev.com/artifactory/corda' }
+ maven { url 'https://repo.gradle.org/gradle/libs-releases' }
}
-}
-task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
- node {
- name "O=Notary,L=London,C=GB"
- notary = [validating : true]
- p2pPort 10002
- rpcSettings {
- address("localhost:10003")
- adminAddress("localhost:10043")
- }
- projectCordapp {
- deploy = false
- }
- }
- node {
- name "O=Monogram Bank,L=London,C=GB"
- p2pPort 10006
- rpcSettings {
- address("localhost:10007")
- adminAddress("localhost:10047")
+ configurations {
+ compile {
+ // We want to use SLF4J's version of these bindings: jcl-over-slf4j
+ // Remove any transitive dependency on Apache's version.
+ exclude group: 'commons-logging', module: 'commons-logging'
}
- cordapps = []
- rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
}
- node {
- name "O=Hiseville Deposit Bank,L=Sao Paulo,C=BR"
- p2pPort 10009
- rpcSettings {
- address("localhost:10010")
- adminAddress("localhost:10050")
- }
- cordapps = []
- rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
+
+ tasks.withType(JavaCompile) {
+ options.compilerArgs << "-parameters" // Required for shell commands.
}
- node {
- name "O=George State Bank,L=New York,C=US"
- p2pPort 10012
- rpcSettings {
- address("localhost:10013")
- adminAddress("localhost:10053")
+
+ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
+ kotlinOptions {
+ languageVersion = "1.2"
+ apiVersion = "1.2"
+ jvmTarget = "1.8"
+ javaParameters = true // Useful for reflection.
}
- cordapps = []
- rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
}
-}
-
-task uploadBlacklist(type: JavaExec, dependsOn: compileTestKotlin) {
- classpath = sourceSets.test.runtimeClasspath
- main = 'net.corda.examples.attachments.tests.client.ClientKt'
- args 'localhost:10007', 'localhost:10010', 'localhost:10013'
-}
+}
\ No newline at end of file
diff --git a/blacklist/clients-java/build.gradle b/blacklist/clients-java/build.gradle
new file mode 100644
index 000000000..6faf48538
--- /dev/null
+++ b/blacklist/clients-java/build.gradle
@@ -0,0 +1,43 @@
+apply plugin: 'org.springframework.boot'
+apply plugin: 'java'
+
+sourceSets {
+ main {
+ resources {
+ srcDir rootProject.file("config/dev")
+ }
+ }
+}
+
+dependencies {
+ // Corda dependencies.
+ compile "$corda_release_group:corda-rpc:$corda_release_version"
+
+ // CorDapp dependencies.
+ compile project(":contracts-java")
+ compile project(":workflows-java")
+
+ compile("org.springframework.boot:spring-boot-starter-websocket:$spring_boot_version") {
+ exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
+ }
+
+ compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
+ compile "org.apache.logging.log4j:log4j-web:${log4j_version}"
+ compile "org.slf4j:jul-to-slf4j:$slf4j_version"
+}
+
+springBoot {
+ mainClassName = "net.corda.examples.attachments.webserver.Server"
+}
+
+task runWebServer(type: JavaExec, dependsOn: assemble) {
+ classpath = sourceSets.main.runtimeClasspath
+ main = 'net.corda.examples.attachments.webserver.Starter'
+ args '--server.port=10050', '--config.rpc.host=localhost', '--config.rpc.port=10006', '--config.rpc.username=user1', '--config.rpc.password=test'
+}
+
+task uploadBlacklist(type: JavaExec, dependsOn: assemble) {
+ classpath = sourceSets.main.runtimeClasspath
+ main = 'net.corda.examples.attachments.client.Client'
+ args 'localhost:10006', 'localhost:10009', 'localhost:10012'
+}
diff --git a/blacklist/clients-java/src/main/java/net/corda/examples/attachments/client/Client.java b/blacklist/clients-java/src/main/java/net/corda/examples/attachments/client/Client.java
new file mode 100644
index 000000000..ed363cf40
--- /dev/null
+++ b/blacklist/clients-java/src/main/java/net/corda/examples/attachments/client/Client.java
@@ -0,0 +1,97 @@
+package net.corda.examples.attachments.client;
+
+import kotlin.text.Charsets;
+import net.corda.client.rpc.CordaRPCClient;
+import net.corda.client.rpc.CordaRPCConnection;
+import net.corda.core.crypto.SecureHash;
+import net.corda.core.messaging.CordaRPCOps;
+import net.corda.core.utilities.NetworkHostAndPort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.nio.file.FileAlreadyExistsException;
+import java.util.List;
+import java.util.jar.JarInputStream;
+import java.util.stream.Collectors;
+
+import static net.corda.examples.attachments.Constants.*;
+
+public class Client {
+
+ private static class Companion {
+ static Logger logger = LoggerFactory.getLogger(Client.class);
+ }
+
+ /**
+ * Uploads the jar of blacklisted counterparties with whom agreements cannot be struck to the node.
+ */
+ public static void main(String[] args) throws IOException {
+ if (args.length == 0) {
+ String message = "Usage: uploadBlacklist ...";
+ throw new IllegalArgumentException(message.toString());
+ }
+
+ for (String arg : args) {
+ NetworkHostAndPort nodeAddress = NetworkHostAndPort.parse(arg);
+ CordaRPCConnection rpcConnection = new CordaRPCClient(nodeAddress).start("user1", "test");
+ CordaRPCOps proxy = rpcConnection.getProxy();
+
+ SecureHash attachmentHash = BLACKLIST_JAR_HASH;
+
+ // take relative path using substring of constant BLACKLIST_JAR_PATH, check if node contains blacklist already
+ if (!proxy.attachmentExists(attachmentHash)) {
+ System.out.println("Working Directory = " +
+ System.getProperty("user.dir"));
+ attachmentHash = uploadAttachment(proxy, BLACKLIST_JAR_PATH);
+ Companion.logger.info("Blacklist uploaded to node at " + nodeAddress);
+ } else {
+ Companion.logger.info("Node already contains Blacklist, skipping upload at " + nodeAddress);
+ }
+
+ JarInputStream attachmentJar = downloadAttachment(proxy, attachmentHash);
+ Companion.logger.info("Blacklist downloaded from node at " + nodeAddress);
+
+ checkAttachment(attachmentJar, ATTACTMENT_FILE_NAME, ATTACHMENT_EXPECTED_CONTENTS);
+ Companion.logger.info("Attachment contents checked on node at " + nodeAddress);
+ }
+
+ }
+
+ /**
+ * Uploads the attachment at [attachmentPath] to the node.
+ */
+ private static SecureHash uploadAttachment(CordaRPCOps proxy, String attachmentPath) throws FileNotFoundException, FileAlreadyExistsException {
+ FileInputStream attachmentUploadInputStream = new FileInputStream(new File(attachmentPath));
+ return proxy.uploadAttachment(attachmentUploadInputStream);
+ }
+
+ /**
+ * Downloads the attachment with hash [attachmentHash] from the node.
+ */
+ private static JarInputStream downloadAttachment(CordaRPCOps proxy, SecureHash attachmentHash) throws IOException {
+ InputStream attachmentDownloadInputStream = proxy.openAttachment(attachmentHash);
+ return new JarInputStream(attachmentDownloadInputStream);
+ }
+
+ /**
+ * Checks the [expectedFileName] and [expectedContents] of the downloaded [attachmentJar].
+ */
+ private static void checkAttachment(JarInputStream attachmentJar, String expectedFileName, List expectedContents) throws IOException {
+ String name = attachmentJar.getNextEntry().getName();
+ while (!name.equals(expectedFileName)) {
+ name = attachmentJar.getNextEntry().getName();
+ }
+
+ BufferedInputStream bisAttachmentJar = new BufferedInputStream(attachmentJar, (8*1024));
+ InputStreamReader isrAttachmentJar = new InputStreamReader(bisAttachmentJar, Charsets.UTF_8);
+ BufferedReader brAttachmentJar = new BufferedReader(isrAttachmentJar);
+
+ List contents = brAttachmentJar.lines().collect(Collectors.toList());
+
+ if (!contents.equals(expectedContents)) {
+ throw new IllegalArgumentException("Downloaded JAR did not have the expected contents.");
+ }
+
+ }
+}
diff --git a/blacklist/clients-kotlin/build.gradle b/blacklist/clients-kotlin/build.gradle
new file mode 100644
index 000000000..15343ff02
--- /dev/null
+++ b/blacklist/clients-kotlin/build.gradle
@@ -0,0 +1,43 @@
+apply plugin: 'org.springframework.boot'
+apply plugin: 'kotlin'
+
+sourceSets {
+ main {
+ resources {
+ srcDir rootProject.file("config/dev")
+ }
+ }
+}
+
+dependencies {
+ // Corda dependencies.
+ compile "$corda_release_group:corda-rpc:$corda_release_version"
+
+ // CorDapp dependencies.
+ compile project(":contracts-kotlin")
+ compile project(":workflows-kotlin")
+
+ compile("org.springframework.boot:spring-boot-starter-websocket:$spring_boot_version") {
+ exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
+ }
+
+ compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
+ compile "org.apache.logging.log4j:log4j-web:${log4j_version}"
+ compile "org.slf4j:jul-to-slf4j:$slf4j_version"
+}
+
+springBoot {
+ mainClassName = "net.corda.examples.attachments.webserver.Server"
+}
+
+task runWebServer(type: JavaExec, dependsOn: assemble) {
+ classpath = sourceSets.main.runtimeClasspath
+ main = 'net.corda.examples.attachments.webserver.Starter'
+ args '--server.port=10050', '--config.rpc.host=localhost', '--config.rpc.port=10006', '--config.rpc.username=user1', '--config.rpc.password=test'
+}
+
+task uploadBlacklist(type: JavaExec, dependsOn: assemble) {
+ classpath = sourceSets.main.runtimeClasspath
+ main = 'net.corda.examples.attachments.client.ClientKt'
+ args 'localhost:10006', 'localhost:10009', 'localhost:10012'
+}
diff --git a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/client/Client.kt b/blacklist/clients-kotlin/src/main/kotlin/net/corda/examples/attachments/client/Client.kt
similarity index 98%
rename from blacklist/src/test/kotlin/net/corda/examples/attachments/tests/client/Client.kt
rename to blacklist/clients-kotlin/src/main/kotlin/net/corda/examples/attachments/client/Client.kt
index 35babbb03..125656453 100644
--- a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/client/Client.kt
+++ b/blacklist/clients-kotlin/src/main/kotlin/net/corda/examples/attachments/client/Client.kt
@@ -1,4 +1,4 @@
-package net.corda.examples.attachments.tests.client
+package net.corda.examples.attachments.client
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.crypto.SecureHash
diff --git a/blacklist/contracts-java/build.gradle b/blacklist/contracts-java/build.gradle
new file mode 100644
index 000000000..77d851a50
--- /dev/null
+++ b/blacklist/contracts-java/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'java'
+apply plugin: 'net.corda.plugins.cordapp'
+
+jar {
+ // CorDapps do not configure a Node's logging.
+ exclude '**/log4j2*.xml'
+}
+
+cordapp {
+ targetPlatformVersion corda_platform_version.toInteger()
+ minimumPlatformVersion corda_platform_version.toInteger()
+ contract {
+ name "Blacklist"
+ vendor "Corda Open Source"
+ licence "Apache License, Version 2.0"
+ versionId 1
+ }
+}
+
+dependencies {
+ testImplementation "junit:junit:$junit_version"
+ testImplementation "$corda_release_group:corda-node-driver:$corda_release_version"
+ testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
+
+ // Corda dependencies.
+ cordaCompile "$corda_release_group:corda-core:$corda_release_version"
+ cordaCompile "$corda_release_group:corda-jackson:$corda_release_version"
+ cordaCompile "$corda_release_group:corda-rpc:$corda_release_version"
+ cordaRuntime "$corda_release_group:corda:$corda_release_version"
+
+}
\ No newline at end of file
diff --git a/blacklist/contracts-java/src/main/java/net/corda/examples/attachments/Constants.java b/blacklist/contracts-java/src/main/java/net/corda/examples/attachments/Constants.java
new file mode 100644
index 000000000..ee32077f6
--- /dev/null
+++ b/blacklist/contracts-java/src/main/java/net/corda/examples/attachments/Constants.java
@@ -0,0 +1,28 @@
+package net.corda.examples.attachments;
+
+import net.corda.core.crypto.SecureHash;
+
+import java.util.Arrays;
+import java.util.List;
+
+public interface Constants {
+ String BLACKLIST_JAR_PATH = "../contracts-java/src/main/resources/blacklist.jar";
+ SecureHash BLACKLIST_JAR_HASH = SecureHash.parse("4CEC607599723D7E0393EB5F05F24562732CD1B217DEAEDEABD4C25AFE5B333A");
+ String ATTACTMENT_FILE_NAME = "blacklist.txt";
+ List ATTACHMENT_EXPECTED_CONTENTS = Arrays.asList(
+ "Crossland Savings",
+ "TCF National Bank Wisconsin",
+ "George State Bank",
+ "The James Polk Stone Community Bank",
+ "Tifton Banking Company"
+ );
+ List BLACKLISTED_PARTIES = Arrays.asList(
+ "Crossland Savings",
+ "TCF National Bank Wisconsin",
+ "George State Bank",
+ "The James Polk Stone Community Bank",
+ "Tifton Banking Company"
+ );
+ // This jar exists, but does not meet the constraints imposed by AttachmentContract.
+ String INCORRECT_JAR_PATH = "src/test/resources/invalid.jar";
+}
\ No newline at end of file
diff --git a/blacklist/contracts-java/src/main/java/net/corda/examples/attachments/contracts/AgreementContract.java b/blacklist/contracts-java/src/main/java/net/corda/examples/attachments/contracts/AgreementContract.java
new file mode 100644
index 000000000..096de191b
--- /dev/null
+++ b/blacklist/contracts-java/src/main/java/net/corda/examples/attachments/contracts/AgreementContract.java
@@ -0,0 +1,123 @@
+package net.corda.examples.attachments.contracts;
+
+import kotlin.text.Charsets;
+import net.corda.core.contracts.*;
+import net.corda.core.crypto.SecureHash;
+import net.corda.core.identity.AbstractParty;
+import net.corda.core.identity.Party;
+import net.corda.core.transactions.LedgerTransaction;
+import net.corda.examples.attachments.states.AgreementState;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.JarInputStream;
+import java.util.stream.Collectors;
+
+public class AgreementContract implements Contract {
+ public static final String AGREEMENT_CONTRACT_ID = "net.corda.examples.attachments.contracts.AgreementContract";
+ private static SecureHash BLACKLIST_JAR_HASH = SecureHash.parse("4CEC607599723D7E0393EB5F05F24562732CD1B217DEAEDEABD4C25AFE5B333A");
+
+ @Override
+ public void verify(LedgerTransaction tx) {
+ // Constraints on the inputs, outputs and commands.
+ ContractsDSL.requireThat(req -> {
+ req.using("The transaction should have no inputs",
+ tx.getInputStates().isEmpty());
+ req.using("The transaction should have an AgreementState output",
+ tx.outputsOfType(AgreementState.class).size() == 1);
+ req.using("The transaction should have no other outputs",
+ tx.getOutputs().size() == 1);
+ req.using("The transaction should have an Agree command",
+ tx.commandsOfType(Commands.Agree.class).size() == 1);
+ req.using("The transaction should have no other commands",
+ tx.getCommands().size() == 1);
+ return null;
+ });
+
+ // Constraints on the included attachments.
+ List nonContractAttachments = tx.getAttachments()
+ .stream()
+ .filter(p -> !(p instanceof ContractAttachment))
+ .map(p -> (Attachment) p)
+ .collect(Collectors.toList());
+
+ Attachment attached = nonContractAttachments.get(0);
+
+ ContractsDSL.requireThat(req -> {
+ req.using("The transaction should have a single non-contract attachment",
+ nonContractAttachments.size() == 1);
+
+ // TODO: Switch to constraint on the jar's signer.
+ // In the future, Corda will support singing of jars. We will then be able to restrict
+ // the attachments used to just those signed by party X.
+ req.using("The jar's hash should be correct", attached.getId().equals(BLACKLIST_JAR_HASH));
+ return null;
+ });
+
+ // Extract the blacklisted company names from the JAR.
+ List blacklistedCompanies = new ArrayList<>();
+ JarInputStream attachmentJar = attached.openAsJAR();
+ try {
+ while (!attachmentJar.getNextEntry().getName().equals("blacklist.txt")) {
+ // Calling 'getNextEntry()' causes us to scroll through the JAR.
+ }
+ InputStreamReader isrBlacklist = new InputStreamReader(attachmentJar, Charsets.UTF_8);
+ BufferedReader brBlacklist = new BufferedReader(isrBlacklist, (8 * 1024)); // Note - changed BIR to BR
+
+ String company = brBlacklist.readLine();
+
+ while (company != null) {
+ blacklistedCompanies.add(company);
+ company = brBlacklist.readLine();
+ }
+ } catch (IOException e) {
+ System.out.println("error reading blacklist.txt");
+ }
+
+ // Constraints on the blacklisted parties
+ AgreementState agreement = tx.outputsOfType(AgreementState.class).get(0);
+
+ List participants = new ArrayList<>();
+ List participantsOrgs = new ArrayList<>();
+ for (AbstractParty p : agreement.getParticipants()) {
+ Party participant = (Party) p;
+ participantsOrgs.add(participant.getName().getOrganisation());
+ participants.add(participant);
+ }
+
+ // overlap is whether any participants in the transaction belong to a blacklisted org.
+ Set overlap = new HashSet<>(blacklistedCompanies);
+ overlap.retainAll(new HashSet<>(participantsOrgs)); // intersection
+
+ ContractsDSL.requireThat(req -> {
+ req.using("The agreement involved blacklisted parties" + overlap.toString(),
+ overlap.isEmpty());
+ return null;
+ });
+
+ // Constraints on the signers.
+ Command command = tx.getCommand(0);
+ List particpantKeys = new ArrayList<>();
+ for (Party p : participants) {
+ particpantKeys.add(p.getOwningKey());
+ }
+
+ ContractsDSL.requireThat(req -> {
+ req.using("All the parties to the agreement are signers",
+ command.getSigners().containsAll(particpantKeys));
+ return null;
+ });
+
+
+ }
+
+ public interface Commands extends CommandData {
+ class Agree extends TypeOnlyCommandData implements Commands {}
+ }
+}
diff --git a/blacklist/contracts-java/src/main/java/net/corda/examples/attachments/states/AgreementState.java b/blacklist/contracts-java/src/main/java/net/corda/examples/attachments/states/AgreementState.java
new file mode 100644
index 000000000..830cb6bb5
--- /dev/null
+++ b/blacklist/contracts-java/src/main/java/net/corda/examples/attachments/states/AgreementState.java
@@ -0,0 +1,42 @@
+package net.corda.examples.attachments.states;
+
+import net.corda.core.contracts.BelongsToContract;
+import net.corda.core.contracts.ContractState;
+import net.corda.core.identity.AbstractParty;
+import net.corda.core.identity.Party;
+import net.corda.core.serialization.ConstructorForDeserialization;
+import net.corda.core.serialization.CordaSerializable;
+import net.corda.examples.attachments.contracts.AgreementContract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.List;
+
+@CordaSerializable
+@BelongsToContract(AgreementContract.class)
+public class AgreementState implements ContractState {
+ private final Party partyA;
+ private final Party partyB;
+ private final String txt;
+
+ public Party getPartyA() {
+ return partyA;
+ }
+ public Party getPartyB() { return partyB; }
+ public String getTxt() {
+ return txt;
+ }
+
+ @ConstructorForDeserialization
+ public AgreementState(Party partyA, Party partyB, String txt) {
+ this.partyA = partyA;
+ this.partyB = partyB;
+ this.txt = txt;
+ }
+
+ @NotNull
+ @Override
+ public List getParticipants() {
+ return Arrays.asList(partyA, partyB);
+ }
+}
diff --git a/blacklist/src/main/resources/blacklist.jar b/blacklist/contracts-java/src/main/resources/blacklist.jar
similarity index 100%
rename from blacklist/src/main/resources/blacklist.jar
rename to blacklist/contracts-java/src/main/resources/blacklist.jar
diff --git a/blacklist/src/main/resources/log4j2.xml b/blacklist/contracts-java/src/main/resources/log4j2.xml
similarity index 100%
rename from blacklist/src/main/resources/log4j2.xml
rename to blacklist/contracts-java/src/main/resources/log4j2.xml
diff --git a/blacklist/contracts-java/src/test/java/net/corda/examples/attachments/contracts/ContractTests.java b/blacklist/contracts-java/src/test/java/net/corda/examples/attachments/contracts/ContractTests.java
new file mode 100644
index 000000000..7dee81ee0
--- /dev/null
+++ b/blacklist/contracts-java/src/test/java/net/corda/examples/attachments/contracts/ContractTests.java
@@ -0,0 +1,99 @@
+package net.corda.examples.attachments.contracts;
+
+import net.corda.core.crypto.SecureHash;
+import net.corda.core.identity.CordaX500Name;
+import net.corda.core.identity.Party;
+import net.corda.examples.attachments.states.AgreementState;
+import net.corda.testing.core.TestIdentity;
+import net.corda.testing.node.MockServices;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+import static java.util.Arrays.asList;
+import static net.corda.core.crypto.CryptoUtils.generateKeyPair;
+import static net.corda.examples.attachments.Constants.BLACKLISTED_PARTIES;
+import static net.corda.examples.attachments.Constants.BLACKLIST_JAR_PATH;
+import static net.corda.examples.attachments.contracts.AgreementContract.AGREEMENT_CONTRACT_ID;
+import static net.corda.testing.core.TestUtils.getTestPartyAndCertificate;
+import static net.corda.testing.node.MockServicesKt.makeTestIdentityService;
+import static net.corda.testing.node.NodeTestUtils.ledger;
+
+
+public class ContractTests {
+ static private final MockServices ledgerServices = new MockServices(asList("net.corda.examples.attachments.contracts"), new TestIdentity(new CordaX500Name("TestIdentity", "", "GB")), makeTestIdentityService());
+ static private final CordaX500Name megaCorpName = new CordaX500Name("MegaCorp", "London", "GB");
+ static private final CordaX500Name miniCorpName = new CordaX500Name("MiniCorp", "London", "GB");
+ static private final TestIdentity megaCorp = new TestIdentity(megaCorpName);
+ static private final TestIdentity miniCorp = new TestIdentity(miniCorpName);
+
+ static private final String agreementTxt = megaCorpName + " agrees with " + miniCorpName + " that...";
+ static private final File validAttachment = new File(BLACKLIST_JAR_PATH);
+ static private final KeyPair blacklistedPartyKeyPair = generateKeyPair();
+ static private final PublicKey blacklistedPartyPubKey = blacklistedPartyKeyPair.getPublic();
+ static private final CordaX500Name blacklistedPartyName = new CordaX500Name(BLACKLISTED_PARTIES.get(0), "London", "GB");
+ static private final Party blacklistedParty = getTestPartyAndCertificate(blacklistedPartyName, blacklistedPartyPubKey).getParty();
+
+ @Test
+ public void agreementTransactionContainsOneNonContractAttachment() {
+ ledger(ledgerServices, (ledger -> {
+ // We upload a test attachment to the ledger.
+ FileInputStream attachmentInputStream = null;
+ try {
+ attachmentInputStream = new FileInputStream(validAttachment);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ SecureHash attachmentHash = ledger.attachment(attachmentInputStream);
+
+ ledger.transaction(tx -> {
+ tx.output(AGREEMENT_CONTRACT_ID, new AgreementState(megaCorp.getParty(), miniCorp.getParty(), agreementTxt));
+ tx.command(asList(megaCorp.getPublicKey(), miniCorp.getPublicKey()), new AgreementContract.Commands.Agree());
+ tx.fails();
+ tx.attachment(attachmentHash);
+ tx.verifies();
+ return null;
+ });
+ return null;
+ }));
+ }
+
+ @Test
+ public void nonContractAttachmentMustNotBlacklistAnyOfTheParticipants() {
+ ledger(ledgerServices, (ledger -> {
+ // We upload a test attachment to the ledger.
+ FileInputStream attachmentInputStream = null;
+ try {
+ attachmentInputStream = new FileInputStream(validAttachment);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ SecureHash attachmentHash = ledger.attachment(attachmentInputStream);
+
+ ledger.transaction(tx -> {
+ tx.output(AGREEMENT_CONTRACT_ID, new AgreementState(megaCorp.getParty(), blacklistedParty, agreementTxt));
+ tx.command(asList(megaCorp.getPublicKey(), blacklistedPartyPubKey), new AgreementContract.Commands.Agree());
+ tx.attachment(attachmentHash);
+ tx.fails();
+
+ return null;
+ });
+
+ ledger.transaction(tx -> {
+ tx.output(AGREEMENT_CONTRACT_ID, new AgreementState(megaCorp.getParty(), miniCorp.getParty(), agreementTxt));
+ tx.command(asList(megaCorp.getPublicKey(), miniCorp.getPublicKey()), new AgreementContract.Commands.Agree());
+ tx.attachment(attachmentHash);
+ tx.verifies();
+
+ return null;
+ });
+
+
+ return null;
+ }));
+ }
+}
diff --git a/blacklist/contracts-java/src/test/java/net/corda/examples/attachments/states/StateTests.java b/blacklist/contracts-java/src/test/java/net/corda/examples/attachments/states/StateTests.java
new file mode 100644
index 000000000..9327531c4
--- /dev/null
+++ b/blacklist/contracts-java/src/test/java/net/corda/examples/attachments/states/StateTests.java
@@ -0,0 +1,41 @@
+package net.corda.examples.attachments.states;
+
+import net.corda.core.contracts.ContractState;
+import net.corda.core.identity.CordaX500Name;
+import net.corda.core.identity.Party;
+import net.corda.testing.core.TestIdentity;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class StateTests {
+ private final Party alice = new TestIdentity(new CordaX500Name("Alice", "", "GB")).getParty();
+ private final Party bob = new TestIdentity(new CordaX500Name("Bob", "", "GB")).getParty();
+
+ @Test
+ public void agreementStateHasParamsOfCorrectTypeInConstructor() {
+ new AgreementState(alice, bob, "this is an agreement txt");
+ }
+
+ @Test
+ public void tokenStateHasGettersForPartyAPartyBandTxt() {
+ AgreementState agreementState = new AgreementState(alice, bob, "this is an agreement txt");
+ assertEquals(alice, agreementState.getPartyA());
+ assertEquals(bob, agreementState.getPartyB());
+ assertEquals("this is an agreement txt", agreementState.getTxt());
+ }
+
+ @Test
+ public void agreementStateImplementsContractState() {
+ assertTrue(new AgreementState(alice, bob, "this is an agreement txt") instanceof ContractState);
+ }
+
+ @Test
+ public void agreementStateHasTwoParticipantsPartyAPartyB() {
+ AgreementState agreementState = new AgreementState(alice, bob, "this is an agreement txt");
+ assertEquals(2, agreementState.getParticipants().size());
+ assertTrue(agreementState.getParticipants().contains(alice));
+ assertTrue(agreementState.getParticipants().contains(bob));
+ }
+}
\ No newline at end of file
diff --git a/blacklist/contracts-kotlin/build.gradle b/blacklist/contracts-kotlin/build.gradle
new file mode 100644
index 000000000..eba56b818
--- /dev/null
+++ b/blacklist/contracts-kotlin/build.gradle
@@ -0,0 +1,35 @@
+apply plugin: 'kotlin'
+apply plugin: 'net.corda.plugins.cordapp'
+
+jar {
+ // CorDapps do not configure a Node's logging.
+ exclude '**/log4j2*.xml'
+}
+
+cordapp {
+ targetPlatformVersion corda_platform_version.toInteger()
+ minimumPlatformVersion corda_platform_version.toInteger()
+ contract {
+ name "Blacklist"
+ vendor "Corda Open Source"
+ licence "Apache License, Version 2.0"
+ versionId 1
+ }
+}
+
+tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
+ kotlinOptions {
+ languageVersion = "1.2"
+ apiVersion = "1.2"
+ jvmTarget = "1.8"
+ javaParameters = true // Useful for reflection.
+ }
+}
+
+dependencies {
+ compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
+
+ // Corda dependencies.
+ cordaCompile "$corda_release_group:corda-core:$corda_release_version"
+ testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
+}
\ No newline at end of file
diff --git a/blacklist/contracts-kotlin/src/main/kotlin/net/corda/examples/attachments/Constants.kt b/blacklist/contracts-kotlin/src/main/kotlin/net/corda/examples/attachments/Constants.kt
new file mode 100644
index 000000000..21a6f72d0
--- /dev/null
+++ b/blacklist/contracts-kotlin/src/main/kotlin/net/corda/examples/attachments/Constants.kt
@@ -0,0 +1,19 @@
+package net.corda.examples.attachments
+
+const val BLACKLIST_JAR_PATH = "../contracts-kotlin/src/main/resources/blacklist.jar"
+const val ATTACHMENT_FILE_NAME = "blacklist.txt"
+val ATTACHMENT_EXPECTED_CONTENTS = listOf(
+ "Crossland Savings",
+ "TCF National Bank Wisconsin",
+ "George State Bank",
+ "The James Polk Stone Community Bank",
+ "Tifton Banking Company")
+val BLACKLISTED_PARTIES = listOf(
+ "Crossland Savings",
+ "TCF National Bank Wisconsin",
+ "George State Bank",
+ "The James Polk Stone Community Bank",
+ "Tifton Banking Company"
+)
+// This jar exists, but does not meet the constraints imposed by AttachmentContract.
+const val INCORRECT_JAR_PATH = "src/test/resources/invalid.jar"
\ No newline at end of file
diff --git a/blacklist/src/main/kotlin/net/corda/examples/attachments/contract/AgreementContract.kt b/blacklist/contracts-kotlin/src/main/kotlin/net/corda/examples/attachments/contracts/AgreementContract.kt
similarity index 95%
rename from blacklist/src/main/kotlin/net/corda/examples/attachments/contract/AgreementContract.kt
rename to blacklist/contracts-kotlin/src/main/kotlin/net/corda/examples/attachments/contracts/AgreementContract.kt
index 7d6a06be3..d80f1f108 100644
--- a/blacklist/src/main/kotlin/net/corda/examples/attachments/contract/AgreementContract.kt
+++ b/blacklist/contracts-kotlin/src/main/kotlin/net/corda/examples/attachments/contracts/AgreementContract.kt
@@ -1,4 +1,4 @@
-package net.corda.examples.attachments.contract
+package net.corda.examples.attachments.contracts
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractAttachment
@@ -6,11 +6,11 @@ import net.corda.core.contracts.TypeOnlyCommandData
import net.corda.core.contracts.requireThat
import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.LedgerTransaction
-import net.corda.examples.attachments.state.AgreementState
+import net.corda.examples.attachments.states.AgreementState
open class AgreementContract : Contract {
companion object {
- const val AGREEMENT_CONTRACT_ID = "net.corda.examples.attachments.contract.AgreementContract"
+ const val AGREEMENT_CONTRACT_ID = "net.corda.examples.attachments.contracts.AgreementContract"
val BLACKLIST_JAR_HASH = SecureHash.parse("4CEC607599723D7E0393EB5F05F24562732CD1B217DEAEDEABD4C25AFE5B333A")
}
diff --git a/blacklist/src/main/kotlin/net/corda/examples/attachments/state/AgreementState.kt b/blacklist/contracts-kotlin/src/main/kotlin/net/corda/examples/attachments/states/AgreementState.kt
similarity index 75%
rename from blacklist/src/main/kotlin/net/corda/examples/attachments/state/AgreementState.kt
rename to blacklist/contracts-kotlin/src/main/kotlin/net/corda/examples/attachments/states/AgreementState.kt
index c6b4ef857..6c87e1c39 100644
--- a/blacklist/src/main/kotlin/net/corda/examples/attachments/state/AgreementState.kt
+++ b/blacklist/contracts-kotlin/src/main/kotlin/net/corda/examples/attachments/states/AgreementState.kt
@@ -1,9 +1,9 @@
-package net.corda.examples.attachments.state
+package net.corda.examples.attachments.states
import net.corda.core.contracts.BelongsToContract
import net.corda.core.contracts.ContractState
import net.corda.core.identity.Party
-import net.corda.examples.attachments.contract.AgreementContract
+import net.corda.examples.attachments.contracts.AgreementContract
@BelongsToContract(AgreementContract::class)
data class AgreementState(val partyA: Party, val partyB: Party, val txt: String) : ContractState {
diff --git a/blacklist/contracts-kotlin/src/main/resources/blacklist.jar b/blacklist/contracts-kotlin/src/main/resources/blacklist.jar
new file mode 100644
index 000000000..f847c28d1
Binary files /dev/null and b/blacklist/contracts-kotlin/src/main/resources/blacklist.jar differ
diff --git a/blacklist/contracts-kotlin/src/main/resources/log4j2.xml b/blacklist/contracts-kotlin/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..aaabc5aa3
--- /dev/null
+++ b/blacklist/contracts-kotlin/src/main/resources/log4j2.xml
@@ -0,0 +1,31 @@
+
+
+
+ build/logs
+ blacklist-${hostName}
+
+
+
+
+
+
+ [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/contract/ContractTests.kt b/blacklist/contracts-kotlin/src/test/kotlin/net/corda/examples/attachments/contracts/ContractTests.kt
similarity index 85%
rename from blacklist/src/test/kotlin/net/corda/examples/attachments/tests/contract/ContractTests.kt
rename to blacklist/contracts-kotlin/src/test/kotlin/net/corda/examples/attachments/contracts/ContractTests.kt
index be3bfef37..9b5ebc66b 100644
--- a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/contract/ContractTests.kt
+++ b/blacklist/contracts-kotlin/src/test/kotlin/net/corda/examples/attachments/contracts/ContractTests.kt
@@ -1,12 +1,12 @@
-package net.corda.examples.attachments.tests.contract
+package net.corda.examples.attachments.contracts
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.CordaX500Name
+import net.corda.examples.attachments.BLACKLISTED_PARTIES
import net.corda.examples.attachments.BLACKLIST_JAR_PATH
-import net.corda.examples.attachments.contract.AgreementContract
-import net.corda.examples.attachments.contract.AgreementContract.Companion.AGREEMENT_CONTRACT_ID
-import net.corda.examples.attachments.state.AgreementState
-import net.corda.examples.attachments.tests.BLACKLISTED_PARTIES
+import net.corda.examples.attachments.contracts.AgreementContract
+import net.corda.examples.attachments.contracts.AgreementContract.Companion.AGREEMENT_CONTRACT_ID
+import net.corda.examples.attachments.states.AgreementState
import net.corda.testing.core.TestIdentity
import net.corda.testing.core.getTestPartyAndCertificate
import net.corda.testing.node.MockServices
@@ -16,7 +16,7 @@ import org.junit.Test
import java.io.File
class ContractTests {
- private val ledgerServices = MockServices(listOf("net.corda.examples.attachments.contract"), identityService = makeTestIdentityService(), initialIdentity = TestIdentity(CordaX500Name("TestIdentity", "", "GB")))
+ private val ledgerServices = MockServices(listOf("net.corda.examples.attachments.contracts"), identityService = makeTestIdentityService(), initialIdentity = TestIdentity(CordaX500Name("TestIdentity", "", "GB")))
private val megaCorpName = CordaX500Name("MegaCorp", "London", "GB")
private val miniCorpName = CordaX500Name("MiniCorp", "London", "GB")
private val megaCorp = TestIdentity(megaCorpName)
diff --git a/blacklist/contracts-kotlin/src/test/kotlin/net/corda/examples/attachments/states/StateTests.kt b/blacklist/contracts-kotlin/src/test/kotlin/net/corda/examples/attachments/states/StateTests.kt
new file mode 100644
index 000000000..1f5de1985
--- /dev/null
+++ b/blacklist/contracts-kotlin/src/test/kotlin/net/corda/examples/attachments/states/StateTests.kt
@@ -0,0 +1,34 @@
+package net.corda.examples.attachments.states
+
+import net.corda.core.identity.CordaX500Name
+import net.corda.testing.core.TestIdentity
+import org.junit.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class StateTests {
+
+ private val alice = TestIdentity(CordaX500Name("Alice", "", "GB")).party
+ private val bob = TestIdentity(CordaX500Name("Bob", "", "GB")).party
+
+ @Test
+ fun `agreementStateHasParamsOfCorrectTypeInConstructor`() {
+ AgreementState(alice, bob, "this is an agreement txt")
+ }
+
+ @Test
+ fun `agreementStateHasGettersForPartyAPartyBTxt`() {
+ val agreementState = AgreementState(alice, bob, "this is an agreement txt")
+ assertEquals(alice, agreementState.partyA)
+ assertEquals(bob, agreementState.partyB)
+ assertEquals("this is an agreement txt", agreementState.txt)
+ }
+
+ @Test
+ fun `agreementStateHasTwoParticipantsPartyAPartyB`() {
+ val agreementState = AgreementState(alice, bob, "this is an agreement txt")
+ assertEquals(2, agreementState.participants.size)
+ assertTrue(agreementState.participants.contains(alice))
+ assertTrue(agreementState.participants.contains(bob))
+ }
+}
\ No newline at end of file
diff --git a/blacklist/repositories.gradle b/blacklist/repositories.gradle
deleted file mode 100644
index 2874c2ab4..000000000
--- a/blacklist/repositories.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-repositories {
- mavenLocal()
- mavenCentral()
- jcenter()
- maven { url 'https://jitpack.io' }
- maven { url 'https://ci-artifactory.corda.r3cev.com/artifactory/corda' }
- maven { url 'https://repo.gradle.org/gradle/libs-releases' }
-}
diff --git a/blacklist/settings.gradle b/blacklist/settings.gradle
index e69de29bb..2bb92f868 100644
--- a/blacklist/settings.gradle
+++ b/blacklist/settings.gradle
@@ -0,0 +1,7 @@
+include 'workflows-kotlin'
+include 'contracts-kotlin'
+include 'workflows-java'
+include 'contracts-java'
+include 'clients-java'
+include 'clients-kotlin'
+
diff --git a/blacklist/src/main/kotlin/net/corda/examples/attachments/Constants.kt b/blacklist/src/main/kotlin/net/corda/examples/attachments/Constants.kt
deleted file mode 100644
index 8f0a6ffcb..000000000
--- a/blacklist/src/main/kotlin/net/corda/examples/attachments/Constants.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package net.corda.examples.attachments
-
-const val BLACKLIST_JAR_PATH = "src/main/resources/blacklist.jar"
-const val ATTACHMENT_FILE_NAME = "blacklist.txt"
-val ATTACHMENT_EXPECTED_CONTENTS = listOf(
- "Crossland Savings",
- "TCF National Bank Wisconsin",
- "George State Bank",
- "The James Polk Stone Community Bank",
- "Tifton Banking Company")
\ No newline at end of file
diff --git a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/Constants.kt b/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/Constants.kt
deleted file mode 100644
index 67dae0404..000000000
--- a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/Constants.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package net.corda.examples.attachments.tests
-
-// These blacklisted parties are named in the file blacklisted.txt file in blacklist.jar.
-val BLACKLISTED_PARTIES = listOf(
- "Crossland Savings",
- "TCF National Bank Wisconsin",
- "George State Bank",
- "The James Polk Stone Community Bank",
- "Tifton Banking Company"
-)
-// This jar exists, but does not meet the constraints imposed by AttachmentContract.
-const val INCORRECT_JAR_PATH = "src/test/resources/invalid.jar"
\ No newline at end of file
diff --git a/blacklist/workflows-java/build.gradle b/blacklist/workflows-java/build.gradle
new file mode 100644
index 000000000..9729d4651
--- /dev/null
+++ b/blacklist/workflows-java/build.gradle
@@ -0,0 +1,119 @@
+apply plugin: 'java'
+apply plugin: 'net.corda.plugins.cordapp'
+apply plugin: 'net.corda.plugins.quasar-utils'
+apply plugin: 'net.corda.plugins.cordformation'
+
+jar {
+ // CorDapps do not configure a Node's logging.
+ exclude '**/log4j2*.xml'
+}
+
+cordapp {
+ targetPlatformVersion corda_platform_version.toInteger()
+ minimumPlatformVersion corda_platform_version.toInteger()
+ workflow {
+ name "Blacklist"
+ vendor "Corda Open Source"
+ licence "Apache License, Version 2.0"
+ versionId 1
+ }
+}
+
+dependencies {
+ testCompile "junit:junit:$junit_version"
+
+ // Corda dependencies.
+ cordaCompile "$corda_release_group:corda-core:$corda_release_version"
+ cordaCompile "$corda_release_group:corda-jackson:$corda_release_version"
+ cordaCompile "$corda_release_group:corda-rpc:$corda_release_version"
+ cordaCompile "$corda_release_group:corda-node-api:$corda_release_version"
+
+ testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
+ // Needed by deployNodes task.
+ cordaRuntime "$corda_release_group:corda:$corda_release_version"
+
+ // CorDapp dependencies.
+ cordapp project(":contracts-java")
+}
+
+sourceSets {
+ main {
+ resources {
+ srcDir rootProject.file("config/dev")
+ }
+ }
+ test {
+ resources {
+ srcDir rootProject.file("config/test")
+ }
+ }
+ integrationTest {
+ java {
+ compileClasspath += main.output + test.output
+ runtimeClasspath += main.output + test.output
+ srcDir file('src/integrationTest/java')
+ }
+ }
+}
+
+configurations {
+ integrationTestCompile.extendsFrom testCompile
+ integrationTestRuntime.extendsFrom testRuntime
+}
+
+task integrationTest(type: Test, dependsOn: []) {
+ testClassesDirs = sourceSets.integrationTest.output.classesDirs
+ classpath = sourceSets.integrationTest.runtimeClasspath
+}
+
+task uploadBlacklist(type: JavaExec, dependsOn: assemble) {
+ classpath = sourceSets.test.runtimeClasspath
+ main = 'net.corda.examples.attachments.Client'
+ args 'localhost:10006', 'localhost:10009', 'localhost:10012'
+}
+
+tasks.withType(JavaCompile) {
+ options.compilerArgs << "-parameters" // Required for shell commands.
+}
+
+task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
+ nodeDefaults {
+ cordapp project(':contracts-java')
+ }
+ node {
+ name "O=Notary,L=London,C=GB"
+ notary = [validating : false]
+ p2pPort 10002
+ rpcSettings {
+ address("localhost:10003")
+ adminAddress("localhost:10043")
+ }
+ }
+ node {
+ name "O=Monogram Bank,L=London,C=GB"
+ p2pPort 10005
+ rpcSettings {
+ address("localhost:10006")
+ adminAddress("localhost:10046")
+ }
+ rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
+ }
+ node {
+ name "O=Hiseville Deposit Bank,L=Sao Paulo,C=BR"
+ p2pPort 10008
+ rpcSettings {
+ address("localhost:10009")
+ adminAddress("localhost:10049")
+ }
+ rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
+ }
+ node {
+ name "O=George State Bank,L=New York,C=US"
+ p2pPort 10011
+ rpcSettings {
+ address("localhost:10012")
+ adminAddress("localhost:10052")
+ }
+ rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
+ }
+}
\ No newline at end of file
diff --git a/blacklist/workflows-java/src/integrationTest/java/net/corda/examples/attachments/DriverBasedTest.java b/blacklist/workflows-java/src/integrationTest/java/net/corda/examples/attachments/DriverBasedTest.java
new file mode 100644
index 000000000..da5c1a348
--- /dev/null
+++ b/blacklist/workflows-java/src/integrationTest/java/net/corda/examples/attachments/DriverBasedTest.java
@@ -0,0 +1,49 @@
+package net.corda.examples.attachments;
+
+import com.google.common.collect.ImmutableList;
+import net.corda.core.concurrent.CordaFuture;
+import net.corda.core.identity.CordaX500Name;
+import net.corda.testing.core.TestIdentity;
+import net.corda.testing.driver.DriverParameters;
+import net.corda.testing.driver.NodeHandle;
+import net.corda.testing.driver.NodeParameters;
+import net.corda.testing.driver.PortAllocation;
+import org.junit.Test;
+
+import java.util.List;
+
+import static net.corda.testing.driver.Driver.driver;
+import static org.junit.Assert.assertEquals;
+
+public class DriverBasedTest {
+ private final TestIdentity bankA = new TestIdentity(new CordaX500Name("BankA", "", "GB"));
+ private final TestIdentity bankB = new TestIdentity(new CordaX500Name("BankB", "", "US"));
+
+ @Test
+ public void nodeTest() {
+ driver(new DriverParameters().withIsDebug(true).withStartNodesInProcess(true), dsl -> {
+ // Start a pair of nodes and wait for them both to be ready.
+ List> handleFutures = ImmutableList.of(
+ dsl.startNode(new NodeParameters().withProvidedName(bankA.getName())),
+ dsl.startNode(new NodeParameters().withProvidedName(bankB.getName()))
+ );
+
+ try {
+ NodeHandle partyAHandle = handleFutures.get(0).get();
+ NodeHandle partyBHandle = handleFutures.get(1).get();
+
+ // From each node, make an RPC call to retrieve another node's name from the network map, to verify that the
+ // nodes have started and can communicate.
+
+ // This is a very basic test: in practice tests would be starting flows, and verifying the states in the vault
+ // and other important metrics to ensure that your CorDapp is working as intended.
+ assertEquals(partyAHandle.getRpc().wellKnownPartyFromX500Name(bankB.getName()).getName(), bankB.getName());
+ assertEquals(partyBHandle.getRpc().wellKnownPartyFromX500Name(bankA.getName()).getName(), bankA.getName());
+ } catch (Exception e) {
+ throw new RuntimeException("Caught exception during test: ", e);
+ }
+
+ return null;
+ });
+ }
+}
\ No newline at end of file
diff --git a/blacklist/workflows-java/src/main/java/net/corda/examples/attachments/AgreeFlow.java b/blacklist/workflows-java/src/main/java/net/corda/examples/attachments/AgreeFlow.java
new file mode 100644
index 000000000..5eb7aa1f7
--- /dev/null
+++ b/blacklist/workflows-java/src/main/java/net/corda/examples/attachments/AgreeFlow.java
@@ -0,0 +1,40 @@
+package net.corda.examples.attachments;
+
+import co.paralleluniverse.fibers.Suspendable;
+import net.corda.core.crypto.SecureHash;
+import net.corda.core.flows.*;
+import net.corda.core.transactions.SignedTransaction;
+import net.corda.examples.attachments.states.AgreementState;
+import org.jetbrains.annotations.NotNull;
+
+import java.security.SignatureException;
+
+@InitiatedBy(ProposeFlow.class)
+public class AgreeFlow extends FlowLogic {
+ FlowSession counterPartySession = null;
+
+ public AgreeFlow(FlowSession counterPartySession) {
+ this.counterPartySession = counterPartySession;
+ }
+
+ @Suspendable
+ @Override
+ public SignedTransaction call() throws FlowException {
+ FlowLogic signTransactionFlow = new SignTransactionFlow(counterPartySession) {
+ @Override
+ protected void checkTransaction(@NotNull SignedTransaction stx) throws FlowException {
+ // We ensure that the transaction contains an AgreementContract
+ try {
+ if (stx.toLedgerTransaction(getServiceHub(), false).outputsOfType(AgreementState.class).isEmpty()) {
+ throw new FlowException("Agreement transaction did not contain an output AgreementState.");
+ }
+ } catch (SignatureException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+
+ final SecureHash txId = subFlow(signTransactionFlow).getId();
+ return subFlow(new ReceiveFinalityFlow(counterPartySession, txId));
+ }
+}
diff --git a/blacklist/workflows-java/src/main/java/net/corda/examples/attachments/ProposeFlow.java b/blacklist/workflows-java/src/main/java/net/corda/examples/attachments/ProposeFlow.java
new file mode 100644
index 000000000..999bbd56c
--- /dev/null
+++ b/blacklist/workflows-java/src/main/java/net/corda/examples/attachments/ProposeFlow.java
@@ -0,0 +1,60 @@
+package net.corda.examples.attachments;
+
+import co.paralleluniverse.fibers.Suspendable;
+import com.google.common.collect.ImmutableSet;
+import net.corda.core.crypto.SecureHash;
+import net.corda.core.flows.*;
+import net.corda.core.identity.Party;
+import net.corda.core.transactions.SignedTransaction;
+import net.corda.core.transactions.TransactionBuilder;
+import net.corda.examples.attachments.contracts.AgreementContract;
+import net.corda.examples.attachments.states.AgreementState;
+
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.List;
+
+import static net.corda.examples.attachments.contracts.AgreementContract.AGREEMENT_CONTRACT_ID;
+
+@InitiatingFlow
+@StartableByRPC
+public class ProposeFlow extends FlowLogic {
+ private static String agreementTxt;
+ private static SecureHash untrustedPartiesAttachment;
+ private static Party counterparty;
+
+ public ProposeFlow(String agreementTxt, SecureHash untrustedPartiesAttachment, Party counterparty) {
+ this.agreementTxt = agreementTxt;
+ this.untrustedPartiesAttachment = untrustedPartiesAttachment;
+ this.counterparty = counterparty;
+ }
+
+ Party getFirstNotary() throws FlowException {
+ List notaries = getServiceHub().getNetworkMapCache().getNotaryIdentities();
+ if (notaries.isEmpty()) throw new FlowException("No available notary");
+ return notaries.get(0);
+ }
+
+ @Override
+ @Suspendable
+ public SignedTransaction call() throws FlowException {
+ final Party notary = getFirstNotary();
+
+ final AgreementState agreementState = new AgreementState(getOurIdentity(), counterparty, agreementTxt);
+ final AgreementContract.Commands.Agree agreeCmd = new AgreementContract.Commands.Agree();
+ final List agreeCmdRequiredSigners = Arrays.asList(getOurIdentity().getOwningKey(), counterparty.getOwningKey());
+
+ final TransactionBuilder txBuilder = new TransactionBuilder(notary)
+ .addOutputState(agreementState, AGREEMENT_CONTRACT_ID)
+ .addCommand(agreeCmd, agreeCmdRequiredSigners)
+ .addAttachment(untrustedPartiesAttachment);
+
+ final SignedTransaction partSignedTx = getServiceHub().signInitialTransaction(txBuilder);
+
+ final FlowSession counterpartySession = initiateFlow(counterparty);
+ final SignedTransaction signedTx = subFlow(
+ new CollectSignaturesFlow(partSignedTx, ImmutableSet.of(counterpartySession)));
+
+ return subFlow(new FinalityFlow(signedTx, counterpartySession));
+ }
+}
diff --git a/blacklist/workflows-java/src/test/java/net/corda/examples/attachments/FlowTests.java b/blacklist/workflows-java/src/test/java/net/corda/examples/attachments/FlowTests.java
new file mode 100644
index 000000000..7e9b9aa98
--- /dev/null
+++ b/blacklist/workflows-java/src/test/java/net/corda/examples/attachments/FlowTests.java
@@ -0,0 +1,143 @@
+package net.corda.examples.attachments;
+
+import com.google.common.collect.ImmutableList;
+import net.corda.core.concurrent.CordaFuture;
+import net.corda.core.contracts.Attachment;
+import net.corda.core.contracts.StateAndRef;
+import net.corda.core.contracts.TransactionVerificationException;
+import net.corda.core.crypto.SecureHash;
+import net.corda.core.identity.Party;
+import net.corda.core.transactions.SignedTransaction;
+import net.corda.examples.attachments.states.AgreementState;
+import net.corda.testing.node.MockNetwork;
+import net.corda.testing.node.MockNetworkParameters;
+import net.corda.testing.node.StartedMockNode;
+import net.corda.testing.node.TestCordapp;
+import org.hamcrest.core.IsInstanceOf;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import static net.corda.examples.attachments.Constants.BLACKLIST_JAR_PATH;
+import static net.corda.examples.attachments.Constants.INCORRECT_JAR_PATH;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class FlowTests {
+ private MockNetwork network;
+ private StartedMockNode a;
+ private StartedMockNode b;
+ private Party aIdentity;
+ private Party bIdentity;
+ private String agreementTxt;
+ private SecureHash blacklistAttachment;
+ private SecureHash incorrectAttachment;
+
+ @Before
+ public void setup() throws FileNotFoundException {
+ network = new MockNetwork(new MockNetworkParameters().withCordappsForAllNodes(ImmutableList.of(
+ TestCordapp.findCordapp("net.corda.examples.attachments.contracts"))));
+ a = network.createNode();
+ b = network.createNode();
+ aIdentity = a.getInfo().getLegalIdentities().get(0);
+ bIdentity = b.getInfo().getLegalIdentities().get(0);
+
+ agreementTxt = aIdentity.getName() + " agrees with " + bIdentity.getName() + " that...";
+
+ // We upload the valid attachment to the first node, who will propagate it to the other node as part of the
+ // flow.
+ FileInputStream attachmentInputStream = new FileInputStream(new File(BLACKLIST_JAR_PATH));
+
+ a.transaction(() -> {
+ try {
+ blacklistAttachment = a.getServices().getAttachments().importAttachment(attachmentInputStream, "user", "blacklist");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ });
+
+ // We upload the invalid attachment to the first node, who will propagate it to the other node as part of the
+ // flow.
+ FileInputStream incorrectAttachmentInputStream = new FileInputStream(new File(INCORRECT_JAR_PATH));
+
+ a.transaction(() -> {
+ try {
+ incorrectAttachment = a.getServices().getAttachments().importAttachment(incorrectAttachmentInputStream, "user", "blacklist");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ });
+
+ b.registerInitiatedFlow(AgreeFlow.class);
+ network.runNetwork();
+ }
+
+ @After
+ public void tearDown() {
+ network.stopNodes();
+ }
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void flowRejectsAttachmentsThatDoNotMeetTheConstraintsOfAttachmentContract() throws ExecutionException, InterruptedException {
+ thrown.expectCause(IsInstanceOf.instanceOf(TransactionVerificationException.class));
+
+ // The attachment being passed to the propose flow is INVALID, will be rejected.
+ ProposeFlow flow = new ProposeFlow(agreementTxt, incorrectAttachment, bIdentity);
+ CordaFuture future = a.startFlow(flow);
+ network.runNetwork();
+ future.get();
+ }
+
+ @Test
+ public void flowRecordsTheCorrectTransactionInBothPartiesTransactionStorages() throws ExecutionException, InterruptedException {
+ reachAgreement();
+
+ // We check the recorded agreement in both vaults.
+ for (StartedMockNode node : ImmutableList.of(a, b)) {
+ node.transaction(() -> {
+ List> agreements = node.getServices().getVaultService().queryBy(AgreementState.class).getStates();
+ assertEquals(1, agreements.size());
+
+ AgreementState recordedState = agreements.get(0).getState().getData();
+ assertEquals(aIdentity, recordedState.getPartyA());
+ assertEquals(bIdentity, recordedState.getPartyB());
+ assertEquals(agreementTxt, recordedState.getTxt());
+
+ return null;
+ });
+ }
+ }
+
+ @Test
+ public void flowPropagatesTheAttachmentToBsAttachmentStorage() throws ExecutionException, InterruptedException {
+ reachAgreement();
+
+ b.transaction(() -> {
+ Attachment blacklist = b.getServices().getAttachments().openAttachment(blacklistAttachment);
+ assertNotNull(blacklist);
+
+ return null;
+ });
+ }
+
+ private SignedTransaction reachAgreement() throws ExecutionException, InterruptedException {
+ ProposeFlow flow = new ProposeFlow(agreementTxt, blacklistAttachment, bIdentity);
+ CordaFuture future = a.startFlow(flow);
+ network.runNetwork();
+ return (SignedTransaction) future.get();
+ }
+}
diff --git a/blacklist/workflows-java/src/test/java/net/corda/examples/attachments/NodeDriver.java b/blacklist/workflows-java/src/test/java/net/corda/examples/attachments/NodeDriver.java
new file mode 100644
index 000000000..36487579f
--- /dev/null
+++ b/blacklist/workflows-java/src/test/java/net/corda/examples/attachments/NodeDriver.java
@@ -0,0 +1,45 @@
+package net.corda.examples.attachments;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import net.corda.core.identity.CordaX500Name;
+import net.corda.testing.driver.DriverParameters;
+import net.corda.testing.driver.NodeParameters;
+import net.corda.testing.node.User;
+
+import java.util.List;
+
+import static net.corda.testing.driver.Driver.driver;
+
+/**
+ * Allows you to run your nodes through an IDE (as opposed to using deployNodes). Do not use in a production
+ * environment.
+ *
+ */
+public class NodeDriver {
+ public static void main(String[] args) {
+ final List rpcUsers =
+ ImmutableList.of(new User("user1", "test", ImmutableSet.of("ALL")));
+
+ driver(new DriverParameters().withStartNodesInProcess(true).withWaitForAllNodesToFinish(true), dsl -> {
+ try {
+ dsl.startNode(new NodeParameters()
+ .withProvidedName(new CordaX500Name("Monogram Bank", "London", "GB"))
+ .withRpcUsers(rpcUsers)).get();
+ dsl.startNode(new NodeParameters()
+ .withProvidedName(new CordaX500Name("Hiseville Deposit Bank", "Sao Paulo", "BR"))
+ .withRpcUsers(rpcUsers)).get();
+ dsl.startNode(new NodeParameters()
+ .withProvidedName(new CordaX500Name("George State Bank", "New York", "US"))
+ .withRpcUsers(rpcUsers)).get();
+ } catch (Throwable e) {
+ System.err.println("Encountered exception in node startup: " + e.getMessage());
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+ );
+ }
+}
+
diff --git a/blacklist/src/test/resources/invalid.jar b/blacklist/workflows-java/src/test/resources/invalid.jar
similarity index 100%
rename from blacklist/src/test/resources/invalid.jar
rename to blacklist/workflows-java/src/test/resources/invalid.jar
diff --git a/blacklist/workflows-kotlin/build.gradle b/blacklist/workflows-kotlin/build.gradle
new file mode 100644
index 000000000..fae6d5539
--- /dev/null
+++ b/blacklist/workflows-kotlin/build.gradle
@@ -0,0 +1,116 @@
+apply plugin: 'kotlin'
+apply plugin: 'net.corda.plugins.cordapp'
+apply plugin: 'net.corda.plugins.quasar-utils'
+apply plugin: 'net.corda.plugins.cordformation'
+
+jar {
+ // CorDapps do not configure a Node's logging.
+ exclude '**/log4j2*.xml'
+}
+
+cordapp {
+ targetPlatformVersion corda_platform_version.toInteger()
+ minimumPlatformVersion corda_platform_version.toInteger()
+ workflow {
+ name "Blacklist"
+ vendor "Corda Open Source"
+ licence "Apache License, Version 2.0"
+ versionId 1
+ }
+}
+
+dependencies {
+ testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
+ testCompile "junit:junit:$junit_version"
+
+ // Corda dependencies.
+ cordaCompile "$corda_release_group:corda-core:$corda_release_version"
+ cordaCompile "$corda_release_group:corda-jackson:$corda_release_version"
+ cordaCompile "$corda_release_group:corda-rpc:$corda_release_version"
+ cordaCompile "$corda_release_group:corda-node-api:$corda_release_version"
+
+ testCompile "$corda_release_group:corda-node-driver:$corda_release_version"
+ // Needed by deployNodes task.
+ cordaRuntime "$corda_release_group:corda:$corda_release_version"
+
+ // CorDapp dependencies.
+ cordapp project(":contracts-kotlin")
+}
+
+sourceSets {
+ main {
+ resources {
+ srcDir rootProject.file("config/dev")
+ }
+ }
+ test {
+ resources {
+ srcDir rootProject.file("config/test")
+ }
+ }
+ integrationTest {
+ kotlin {
+ compileClasspath += main.output + test.output
+ runtimeClasspath += main.output + test.output
+ srcDir file('src/integrationTest/kotlin')
+ }
+ }
+}
+
+configurations {
+ integrationTestCompile.extendsFrom testCompile
+ integrationTestRuntime.extendsFrom testRuntime
+}
+
+task integrationTest(type: Test, dependsOn: []) {
+ testClassesDirs = sourceSets.integrationTest.output.classesDirs
+ classpath = sourceSets.integrationTest.runtimeClasspath
+}
+
+task uploadBlacklist(type: JavaExec, dependsOn: compileTestKotlin) {
+ classpath = sourceSets.test.runtimeClasspath
+ main = 'net.corda.examples.attachments.ClientKt'
+ args 'localhost:10007', 'localhost:10010', 'localhost:10013'
+}
+
+task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
+ nodeDefaults {
+ cordapp project(':contracts-kotlin')
+ }
+ node {
+ name "O=Notary,L=London,C=GB"
+ notary = [validating : false]
+ p2pPort 10002
+ rpcSettings {
+ address("localhost:10003")
+ adminAddress("localhost:10043")
+ }
+ }
+ node {
+ name "O=Monogram Bank,L=London,C=GB"
+ p2pPort 10005
+ rpcSettings {
+ address("localhost:10006")
+ adminAddress("localhost:10046")
+ }
+ rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
+ }
+ node {
+ name "O=Hiseville Deposit Bank,L=Sao Paulo,C=BR"
+ p2pPort 10008
+ rpcSettings {
+ address("localhost:10009")
+ adminAddress("localhost:10049")
+ }
+ rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
+ }
+ node {
+ name "O=George State Bank,L=New York,C=US"
+ p2pPort 10011
+ rpcSettings {
+ address("localhost:10012")
+ adminAddress("localhost:10052")
+ }
+ rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
+ }
+}
\ No newline at end of file
diff --git a/blacklist/workflows-kotlin/src/integrationTest/kotlin/net/corda/examples/attachments/DriverBasedTest.kt b/blacklist/workflows-kotlin/src/integrationTest/kotlin/net/corda/examples/attachments/DriverBasedTest.kt
new file mode 100644
index 000000000..fa4622940
--- /dev/null
+++ b/blacklist/workflows-kotlin/src/integrationTest/kotlin/net/corda/examples/attachments/DriverBasedTest.kt
@@ -0,0 +1,47 @@
+package net.corda.examples.attachments
+
+import net.corda.core.identity.CordaX500Name
+import net.corda.core.utilities.getOrThrow
+import net.corda.testing.core.TestIdentity
+import net.corda.testing.driver.DriverDSL
+import net.corda.testing.driver.DriverParameters
+import net.corda.testing.driver.NodeHandle
+import net.corda.testing.driver.driver
+import org.junit.Test
+import java.util.concurrent.Future
+import kotlin.test.assertEquals
+
+class DriverBasedTest {
+ private val bankA = TestIdentity(CordaX500Name("BankA", "", "GB"))
+ private val bankB = TestIdentity(CordaX500Name("BankB", "", "US"))
+
+ @Test
+ fun `node test`() = withDriver {
+ // Start a pair of nodes and wait for them both to be ready.
+ val (partyAHandle, partyBHandle) = startNodes(bankA, bankB)
+
+ // From each node, make an RPC call to retrieve another node's name from the network map, to verify that the
+ // nodes have started and can communicate.
+
+ // This is a very basic test: in practice tests would be starting flows, and verifying the states in the vault
+ // and other important metrics to ensure that your CorDapp is working as intended.
+ assertEquals(bankB.name, partyAHandle.resolveName(bankB.name))
+ assertEquals(bankA.name, partyBHandle.resolveName(bankA.name))
+ }
+
+ // Runs a test inside the Driver DSL, which provides useful functions for starting nodes, etc.
+ private fun withDriver(test: DriverDSL.() -> Unit) = driver(
+ DriverParameters(isDebug = true, startNodesInProcess = true)
+ ) { test() }
+
+ // Makes an RPC call to retrieve another node's name from the network map.
+ private fun NodeHandle.resolveName(name: CordaX500Name) = rpc.wellKnownPartyFromX500Name(name)!!.name
+
+ // Resolves a list of futures to a list of the promised values.
+ private fun List>.waitForAll(): List = map { it.getOrThrow() }
+
+ // Starts multiple nodes simultaneously, then waits for them all to be ready.
+ private fun DriverDSL.startNodes(vararg identities: TestIdentity) = identities
+ .map { startNode(providedName = it.name) }
+ .waitForAll()
+}
\ No newline at end of file
diff --git a/blacklist/src/main/kotlin/net/corda/examples/attachments/flow/ReachAgreementFlow.kt b/blacklist/workflows-kotlin/src/main/kotlin/net/corda/examples/attachments/ReachAgreementFlow.kt
similarity index 89%
rename from blacklist/src/main/kotlin/net/corda/examples/attachments/flow/ReachAgreementFlow.kt
rename to blacklist/workflows-kotlin/src/main/kotlin/net/corda/examples/attachments/ReachAgreementFlow.kt
index 6708cff34..7e0c36b14 100644
--- a/blacklist/src/main/kotlin/net/corda/examples/attachments/flow/ReachAgreementFlow.kt
+++ b/blacklist/workflows-kotlin/src/main/kotlin/net/corda/examples/attachments/ReachAgreementFlow.kt
@@ -1,4 +1,4 @@
-package net.corda.examples.attachments.flow
+package net.corda.examples.attachments
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.requireThat
@@ -7,9 +7,9 @@ import net.corda.core.flows.*
import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
-import net.corda.examples.attachments.contract.AgreementContract
-import net.corda.examples.attachments.contract.AgreementContract.Companion.AGREEMENT_CONTRACT_ID
-import net.corda.examples.attachments.state.AgreementState
+import net.corda.examples.attachments.contracts.AgreementContract
+import net.corda.examples.attachments.contracts.AgreementContract.Companion.AGREEMENT_CONTRACT_ID
+import net.corda.examples.attachments.states.AgreementState
@InitiatingFlow
@StartableByRPC
diff --git a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/flow/FlowTests.kt b/blacklist/workflows-kotlin/src/test/kotlin/net/corda/examples/attachments/FlowTests.kt
similarity index 93%
rename from blacklist/src/test/kotlin/net/corda/examples/attachments/tests/flow/FlowTests.kt
rename to blacklist/workflows-kotlin/src/test/kotlin/net/corda/examples/attachments/FlowTests.kt
index 5026d908d..cee194ed0 100644
--- a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/flow/FlowTests.kt
+++ b/blacklist/workflows-kotlin/src/test/kotlin/net/corda/examples/attachments/FlowTests.kt
@@ -1,4 +1,4 @@
-package net.corda.examples.attachments.tests.flow
+package net.corda.examples.attachments
import net.corda.core.contracts.TransactionVerificationException
import net.corda.core.crypto.SecureHash
@@ -6,11 +6,7 @@ import net.corda.core.identity.Party
import net.corda.core.node.services.queryBy
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.getOrThrow
-import net.corda.examples.attachments.BLACKLIST_JAR_PATH
-import net.corda.examples.attachments.flow.AgreeFlow
-import net.corda.examples.attachments.flow.ProposeFlow
-import net.corda.examples.attachments.state.AgreementState
-import net.corda.examples.attachments.tests.INCORRECT_JAR_PATH
+import net.corda.examples.attachments.states.AgreementState
import net.corda.testing.node.MockNetwork
import net.corda.testing.node.MockNetworkParameters
import net.corda.testing.node.StartedMockNode
@@ -36,7 +32,7 @@ class FlowTests {
@Before
fun setup() {
network = MockNetwork(MockNetworkParameters(cordappsForAllNodes = listOf(
- TestCordapp.findCordapp("net.corda.examples.attachments.contract"))))
+ TestCordapp.findCordapp("net.corda.examples.attachments.contracts"))))
a = network.createNode()
b = network.createNode()
aIdentity = a.info.legalIdentities.first()
diff --git a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/NodeDriver.kt b/blacklist/workflows-kotlin/src/test/kotlin/net/corda/examples/attachments/NodeDriver.kt
similarity index 91%
rename from blacklist/src/test/kotlin/net/corda/examples/attachments/tests/NodeDriver.kt
rename to blacklist/workflows-kotlin/src/test/kotlin/net/corda/examples/attachments/NodeDriver.kt
index a1c4c43f7..03233bf36 100644
--- a/blacklist/src/test/kotlin/net/corda/examples/attachments/tests/NodeDriver.kt
+++ b/blacklist/workflows-kotlin/src/test/kotlin/net/corda/examples/attachments/NodeDriver.kt
@@ -1,4 +1,4 @@
-package net.corda.examples.attachments.tests
+package net.corda.examples.attachments
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.getOrThrow
@@ -29,8 +29,5 @@ fun main(args: Array) {
startNode(providedName = CordaX500Name("Hiseville Deposit Bank", "Sao Paulo", "BR"), rpcUsers = listOf(user)),
startNode(providedName = CordaX500Name("George State Bank", "New York", "US"), rpcUsers = listOf(user))).map { it.getOrThrow() }
- startWebserver(nodeA)
- startWebserver(nodeB)
- startWebserver(nodeC)
}
}
\ No newline at end of file
diff --git a/blacklist/workflows-kotlin/src/test/resources/invalid.jar b/blacklist/workflows-kotlin/src/test/resources/invalid.jar
new file mode 100644
index 000000000..f49523a05
Binary files /dev/null and b/blacklist/workflows-kotlin/src/test/resources/invalid.jar differ