Skip to content

Commit 9bc9adc

Browse files
authored
Merge pull request #46 from corda/due-diligence-app
add Due diligence app
2 parents 5fd2232 + 6eb21cd commit 9bc9adc

File tree

39 files changed

+1874
-81
lines changed

39 files changed

+1874
-81
lines changed

Advanced/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and [OwnableState](https://docs.corda.net/docs/corda-os/api-states.html#ownables
99
<img src="./auction-cordapp/snaps/setup.png" alt="Corda" width="600">
1010
</p>
1111

12+
### [Due Diligence Cordapp](./duediligence-cordapp):
13+
An capital market themed app that depicts an ideal shareable due diligence process between banks. It uses advance feature attachment functionality to whitelist the trusted auditors.
1214

1315
### [Negotiation Cordapp](./negotiation-cordapp):
1416
An application that depicts the businsess negotiation and communication process over a distributed ledger system.
@@ -21,6 +23,10 @@ A simple i-owe-you application illustrates all of the steps of creating an oblig
2123
This is an imlementation of Secret Santa using Corda as a tool to store multiple game states.It has a material-ui frontend that lets users create and self-service their own secret santa games. The frontend is implemented in ReactJS and the backend is implemented with a Spring Boot server and some corda flows. It is also equipped with an external emailing package(sendgrid), which you can utilze and turn the app into a live app and send the secret santa assignments to your friends'
2224
emails.
2325

26+
<p align="center">
27+
<img src="./secretsanta-cordapp/clients/src/main/webapp/src/Components/img/secret_corda.png" alt="Corda" width="200">
28+
</p>
29+
2430
### [Snake and Ladder Game Cordapp](./snakesandladders-cordapp):
2531
This sample implements a simple Snakes And Ladder Game on Corda. This cordapp demonstrate the use of multiple features, including Corda Account Library and Oracle service.
2632
<p align="center">
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2016, R3 Limited.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Due Diligence Cordapp
2+
3+
This cordapp is an example of how blockchain can work in the capital market industry. Due diligence is commonly the first step of any action in the capital market. In many of the industry, it is also required by the regulators to prevent fraudulent and risky transactions. Each firm will have its own due-diligence process and standards, but at the same time, the cost of executing due diligence is also bared by them individually.
4+
5+
## App Design
6+
7+
8+
The above picture is a high level mock overview of a shareable due diligence DLT app. BankA will initiate the original Corporate Records auditing with an autitor. Then it will share the auditiing report with BankB to save BankB's cost on getting the same report. Wise versa, BankB can work with a different auditor and produce a different report and share with BankA. In the implmentation of this sample cordapp, we will only cover one type of the file auditing.
9+
10+
Notes: another key feature of this app is whitlisting trusted auditors. It is done by utilizing attachment function in Corda. More samples on how to use attachment can be found in the [Features samples folder](../../Features)
11+
12+
13+
## Pre-running the app
14+
15+
Deploying nodes: `./gradlew clean deployNodes`
16+
17+
Starting the nodes: `./build/nodes/runnodes`
18+
19+
Uploading whitelisted Auditors: `./gradlew uploadWhitelists`
20+
21+
22+
23+
## Running the Cordapp
24+
Step #1: At PartyA, file the original Corporate Records auditing process with Auditor(Trusted Auditor)
25+
```
26+
flow start RequestToValidateCorporateRecordsInitiator validater: Trusted Auditor, numberOfFiles: 10
27+
```
28+
29+
Step #2: Go to the Trusted Auditor Node, validate the auditing request(This step symbolize the auditing process by this third party auditor). Put in the linearId which was returned in Step #1.
30+
```
31+
flow start ValidateCorporateRecordsInitiator linearId: <XXXX-XXX-XXXX-XXXXX-XXXXXX>
32+
```
33+
34+
Step #3: Go to PartyA, Do a query to confirm that the request has been validated. You should see the variable `qualification=true`.
35+
36+
```
37+
run vaultQuery contractStateType: net.corda.samples.duediligence.states.CorporateRecordsAuditRequest
38+
```
39+
Then, we will instruct PartyA to share a copy of the auditing result with PartyB: (Again, You would need put in the linearId returned from Step #1). The parameter `trustedAuditorAttachment` is a jar file which records the trusted auditors. If PartyA used an untrusted auditor to accquire the corporate records auditing report. He will be prohibited to share with anyone because it is valueless effort(in this business use case, Of course you can modify the business use cases).
40+
```
41+
flow start ShareAuditingResultInitiator AuditingResultID: <XXXX-XXX-XXXX-XXXXX-XXXXXX>, sendTo: BankB, trustedAuditorAttachment: "8DF3275D80B26B9A45AB022F2FDA4A2ED996449B425F8F2245FA5BCF7D1AC587"
42+
```
43+
This flow will return the LinearId of the copy of auditing report, you would need this in Step #6.
44+
45+
Step #4: Go to PartyB, do a query to confirm the delievery of copy of the Auditing Report.
46+
```
47+
run vaultQuery contractStateType: net.corda.samples.duediligence.states.CopyOfCoporateRecordsAuditRequest
48+
```
49+
As of now, the sharing of the trusted auditing report is done. What left now for both PartyA and PartyB in this use case is to upload the Corporate Records auditing report into a due-diligence list, which they can share with a regulator.(You can again alter this step to suit any other use cases).
50+
51+
52+
Step #5: Go to PartyA, Attach the Corporate Records auditing report into a due-diligence checklist and report to the Regulator. Again, the approvalId is the linearId returned in Step #1.
53+
```
54+
flow start CreateCheckListAndAddApprovalInitiator reportTo: Regulator, approvalId: <XXXX-XXX-XXXX-XXXXX-XXXXXX>
55+
56+
```
57+
Step #6: Go to PartyB, Attach the copy of the Corporate Records auditing report into a due-diligence checklist and report to the Regulator. You would need the linearId that is return from Step #5
58+
```
59+
flow start CreateCheckListAndAddApprovalInitiator reportTo: Regulator, approvalId: <XXXX-XXX-XXXX-XXXXX-XXXXXX-Returned-From-Step #5>
60+
```
61+
Step #7: Go to Regulator, do a query on reported due-diligence checklists. You will be able to see both PartyA and PartyB had filed a due-diligence checklist.
62+
```
63+
run vaultQuery contractStateType: net.corda.samples.duediligence.states.DueDChecklist
64+
65+
```
66+
This use of Distributed technology Corda helped PartyB saved the cost of go through corporate records due-diligence process while still reaching a trusted auditing report.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Corda and the Corda logo are trademarks of R3CEV LLC and its affiliates. All rights reserved.
2+
3+
For R3CEV LLC's trademark and logo usage information, please consult our Trademark Usage Policy at
4+
https://www.r3.com/trademark-policy/.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
buildscript {//properties that you need to build the project
2+
Properties constants = new Properties()
3+
file("$projectDir/../constants.properties").withInputStream { constants.load(it) }
4+
5+
ext {
6+
corda_release_group = constants.getProperty("cordaReleaseGroup")
7+
corda_core_release_group = constants.getProperty("cordaCoreReleaseGroup")
8+
corda_release_version = constants.getProperty("cordaVersion")
9+
corda_core_release_version = constants.getProperty("cordaCoreVersion")
10+
corda_gradle_plugins_version = constants.getProperty("gradlePluginsVersion")
11+
kotlin_version = constants.getProperty("kotlinVersion")
12+
junit_version = constants.getProperty("junitVersion")
13+
quasar_version = constants.getProperty("quasarVersion")
14+
log4j_version = constants.getProperty("log4jVersion")
15+
slf4j_version = constants.getProperty("slf4jVersion")
16+
corda_platform_version = constants.getProperty("platformVersion").toInteger()
17+
//springboot
18+
spring_boot_version = '2.0.2.RELEASE'
19+
spring_boot_gradle_plugin_version = '2.0.2.RELEASE'
20+
}
21+
22+
repositories {
23+
mavenLocal()
24+
mavenCentral()
25+
jcenter()
26+
maven { url 'https://software.r3.com/artifactory/corda-releases' }
27+
}
28+
29+
dependencies {
30+
classpath "net.corda.plugins:cordapp:$corda_gradle_plugins_version"
31+
classpath "net.corda.plugins:cordformation:$corda_gradle_plugins_version"
32+
classpath "net.corda.plugins:quasar-utils:$corda_gradle_plugins_version"
33+
classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_gradle_plugin_version"
34+
}
35+
}
36+
37+
allprojects {//Properties that you need to compile your project (The application)
38+
apply from: "${rootProject.projectDir}/repositories.gradle"
39+
apply plugin: 'java'
40+
41+
repositories {
42+
mavenLocal()
43+
jcenter()
44+
mavenCentral()
45+
maven { url 'https://software.r3.com/artifactory/corda' }
46+
maven { url 'https://jitpack.io' }
47+
}
48+
49+
tasks.withType(JavaCompile) {
50+
options.compilerArgs << "-parameters" // Required by Corda's serialisation framework.
51+
}
52+
53+
jar {
54+
// This makes the JAR's SHA-256 hash repeatable.
55+
preserveFileTimestamps = false
56+
reproducibleFileOrder = true
57+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
58+
}
59+
}
60+
61+
apply plugin: 'net.corda.plugins.cordapp'
62+
apply plugin: 'net.corda.plugins.cordformation'
63+
apply plugin: 'net.corda.plugins.quasar-utils'
64+
65+
sourceSets {
66+
main {
67+
resources {
68+
srcDir rootProject.file("config/dev")
69+
}
70+
}
71+
}
72+
//Module dependencis
73+
dependencies {
74+
// Corda dependencies.
75+
cordaCompile "$corda_core_release_group:corda-core:$corda_core_release_version"
76+
cordaCompile "$corda_release_group:corda-node-api:$corda_release_version"
77+
cordaRuntime "$corda_release_group:corda:$corda_release_version"
78+
79+
// CorDapp dependencies.
80+
cordapp project(":workflows")
81+
cordapp project(":contracts")
82+
83+
cordaCompile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
84+
cordaCompile "org.apache.logging.log4j:log4j-web:${log4j_version}"
85+
cordaCompile "org.slf4j:jul-to-slf4j:$slf4j_version"
86+
}
87+
88+
89+
//Task to deploy the nodes in order to bootstrap a network
90+
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
91+
92+
/* This property will load the CorDapps to each of the node by default, including the Notary. You can find them
93+
* in the cordapps folder of the node at build/nodes/Notary/cordapps. However, the notary doesn't really understand
94+
* the notion of cordapps. In production, Notary does not need cordapps as well. This is just a short cut to load
95+
* the Corda network bootstrapper.
96+
*/
97+
nodeDefaults {
98+
projectCordapp {
99+
deploy = false
100+
}
101+
cordapp project(':contracts')
102+
cordapp project(':workflows')
103+
runSchemaMigration = true //This configuration is for any CorDapps with custom schema, We will leave this as true to avoid
104+
//problems for developers who are not familiar with Corda. If you are not using custom schemas, you can change
105+
//it to false for quicker project compiling time.
106+
}
107+
node {
108+
name "O=Notary,L=London,C=GB"
109+
notary = [validating : false]
110+
p2pPort 10002
111+
rpcSettings {
112+
address("localhost:10003")
113+
adminAddress("localhost:10043")
114+
}
115+
}
116+
node {
117+
name "O=BankA,L=London,C=GB"
118+
p2pPort 10005
119+
rpcSettings {
120+
address("localhost:10006")
121+
adminAddress("localhost:10046")
122+
}
123+
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
124+
}
125+
node {
126+
name "O=BankB,L=New York,C=US"
127+
p2pPort 10008
128+
rpcSettings {
129+
address("localhost:10009")
130+
adminAddress("localhost:10049")
131+
}
132+
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
133+
}
134+
node {
135+
name "O=Trusted Auditor,L=New York,C=US"
136+
p2pPort 10011
137+
rpcSettings {
138+
address("localhost:10012")
139+
adminAddress("localhost:10052")
140+
}
141+
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
142+
}
143+
node {
144+
name "O=Regulator,L=New York,C=US"
145+
p2pPort 10014
146+
rpcSettings {
147+
address("localhost:10015")
148+
adminAddress("localhost:10055")
149+
}
150+
rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]]
151+
}
152+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
apply plugin: 'org.springframework.boot'
2+
3+
sourceSets {
4+
main {
5+
resources {
6+
srcDir rootProject.file("config/dev")
7+
}
8+
}
9+
}
10+
11+
dependencies {
12+
// Corda dependencies.
13+
compile "$corda_release_group:corda-rpc:$corda_release_version"
14+
15+
// CorDapp dependencies.
16+
compile project(":contracts")
17+
compile project(":workflows")
18+
compile("org.springframework.boot:spring-boot-starter-websocket:$spring_boot_version") {
19+
exclude group: "org.springframework.boot", module: "spring-boot-starter-logging"
20+
}
21+
compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
22+
compile "org.apache.logging.log4j:log4j-web:${log4j_version}"
23+
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
24+
}
25+
26+
27+
task uploadWhitelists(type: JavaExec, dependsOn: assemble) {
28+
classpath = sourceSets.main.runtimeClasspath
29+
main = 'net.corda.samples.duediligence.client.Client'
30+
args 'localhost:10006', 'localhost:10009', 'localhost:10012'
31+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package net.corda.samples.duediligence.client;
2+
3+
import com.google.common.base.Charsets;
4+
import net.corda.client.rpc.CordaRPCClient;
5+
import net.corda.client.rpc.CordaRPCConnection;
6+
import net.corda.core.crypto.SecureHash;
7+
import net.corda.core.messaging.CordaRPCOps;
8+
import net.corda.core.utilities.NetworkHostAndPort;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
import net.corda.samples.duediligence.Constants;
12+
13+
import java.io.*;
14+
import java.nio.file.FileAlreadyExistsException;
15+
import java.util.List;
16+
import java.util.jar.JarInputStream;
17+
import java.util.stream.Collectors;
18+
19+
public class Client {
20+
21+
private static class Companion {
22+
static Logger logger = LoggerFactory.getLogger(Client.class);
23+
}
24+
25+
/**
26+
* Uploads the jar of blacklisted counterparties with whom agreements cannot be struck to the node.
27+
*/
28+
public static void main(String[] args) throws IOException {
29+
if (args.length == 0) {
30+
String message = "Usage: uploadBlacklist <node address 1> <node address 2> ...";
31+
throw new IllegalArgumentException(message.toString());
32+
}
33+
34+
for (String arg : args) {
35+
NetworkHostAndPort nodeAddress = NetworkHostAndPort.parse(arg);
36+
CordaRPCConnection rpcConnection = new CordaRPCClient(nodeAddress).start("user1", "test");
37+
CordaRPCOps proxy = rpcConnection.getProxy();
38+
39+
SecureHash attachmentHash = Constants.CORPORATE_JAR_HASH;
40+
41+
// take relative path using substring of constant BLACKLIST_JAR_PATH, check if node contains blacklist already
42+
if (!proxy.attachmentExists(attachmentHash)) {
43+
System.out.println("Working Directory = " +
44+
System.getProperty("user.dir"));
45+
attachmentHash = uploadAttachment(proxy, Constants.CORPORATE_JAR_PATH);
46+
Companion.logger.info("Corporate Auditor List uploaded to node at " + nodeAddress);
47+
} else {
48+
Companion.logger.info("Node already contains Corporate Auditor List, skipping upload at " + nodeAddress);
49+
}
50+
51+
JarInputStream attachmentJar = downloadAttachment(proxy, attachmentHash);
52+
Companion.logger.info("Corporate Auditor List downloaded from node at " + nodeAddress);
53+
54+
checkAttachment(attachmentJar, Constants.CORPORATE_ATTACTMENT_FILE_NAME, Constants.CORPORATE_ATTACHMENT_EXPECTED_CONTENTS);
55+
Companion.logger.info("Attachment contents checked on node at " + nodeAddress);
56+
}
57+
58+
}
59+
60+
/**
61+
* Uploads the attachment at [attachmentPath] to the node.
62+
*/
63+
private static SecureHash uploadAttachment(CordaRPCOps proxy, String attachmentPath) throws FileNotFoundException, FileAlreadyExistsException {
64+
FileInputStream attachmentUploadInputStream = new FileInputStream(new File(attachmentPath));
65+
return proxy.uploadAttachment(attachmentUploadInputStream);
66+
}
67+
68+
/**
69+
* Downloads the attachment with hash [attachmentHash] from the node.
70+
*/
71+
private static JarInputStream downloadAttachment(CordaRPCOps proxy, SecureHash attachmentHash) throws IOException {
72+
InputStream attachmentDownloadInputStream = proxy.openAttachment(attachmentHash);
73+
return new JarInputStream(attachmentDownloadInputStream);
74+
}
75+
76+
/**
77+
* Checks the [expectedFileName] and [expectedContents] of the downloaded [attachmentJar].
78+
*/
79+
private static void checkAttachment(JarInputStream attachmentJar, String expectedFileName, List<String> expectedContents) throws IOException {
80+
String name = attachmentJar.getNextEntry().getName();
81+
while (!name.equals(expectedFileName)) {
82+
name = attachmentJar.getNextEntry().getName();
83+
}
84+
85+
BufferedInputStream bisAttachmentJar = new BufferedInputStream(attachmentJar, (8 * 1024));
86+
InputStreamReader isrAttachmentJar = new InputStreamReader(bisAttachmentJar, Charsets.UTF_8);
87+
BufferedReader brAttachmentJar = new BufferedReader(isrAttachmentJar);
88+
89+
List<String> contents = brAttachmentJar.lines().collect(Collectors.toList());
90+
91+
if (!contents.equals(expectedContents)) {
92+
throw new IllegalArgumentException("Downloaded JAR did not have the expected contents.");
93+
}
94+
95+
}
96+
}

0 commit comments

Comments
 (0)