From ca1f4afb366c7901128e9a0b71fc808429b407c2 Mon Sep 17 00:00:00 2001 From: peterli-r3 <51169685+peterli-r3@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:38:14 +0800 Subject: [PATCH] Add SendTransaction Cordapp (#31) * added sendAndRecieve cordapp * added Java sendAndRecieve * fixed up readmes * clean up sendAndRecieveTransaction * updated readme * update readme --------- Co-authored-by: Faris Alblooki <66366646+parisyup@users.noreply.github.com> --- .../sendAndRecieveTransaction/.ci/Jenkinsfile | 10 + .../.ci/nightly/JenkinsfileSnykScan | 6 + .../sendAndRecieveTransaction/.gitignore | 86 +++++ .../runConfigurations/DebugCorDapp.run.xml | 15 + java-samples/sendAndRecieveTransaction/.snyk | 14 + .../FlowManagementUI/Dockerfile | 5 + .../FlowManagementUI/README.md | 84 +++++ .../FlowManagementUI/app.py | 12 + .../FlowManagementUI/requirements.txt | 1 + .../FlowManagementUI/static/Scripts/script.js | 322 ++++++++++++++++++ .../FlowManagementUI/static/css/main.css | 202 +++++++++++ .../FlowManagementUI/templates/index.html | 87 +++++ .../sendAndRecieveTransaction/README.md | 120 +++++++ .../sendAndRecieveTransaction/aliceVault.png | Bin 0 -> 17869 bytes .../sendAndRecieveTransaction/build.gradle | 73 ++++ .../charlieAfter.png | Bin 0 -> 50718 bytes .../charlieBefore.png | Bin 0 -> 9976 bytes .../config/combined-worker-compose.yaml | 87 +++++ .../config/gradle-plugin-default-key.pem | 13 + .../config/log4j2.xml | 51 +++ .../config/r3-ca-key.pem | 32 ++ .../config/static-network-config.json | 23 ++ .../contracts/build.gradle | 86 +++++ .../obligation/contracts/IOUContract.java | 50 +++ .../samples/obligation/states/IOUState.java | 81 +++++ .../gradle.properties | 73 ++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61574 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../sendAndRecieveTransaction/gradlew | 244 +++++++++++++ .../sendAndRecieveTransaction/gradlew.bat | 92 +++++ .../sendAndRecieveTransaction/settings.gradle | 25 ++ .../workflows/build.gradle | 96 ++++++ .../obligation/workflows/FinalizeIOUFlow.java | 125 +++++++ .../obligation/workflows/IOUIssueFlow.java | 139 ++++++++ .../workflows/IOUIssueFlowArgs.java | 23 ++ .../obligation/workflows/IOUSettleFlow.java | 127 +++++++ .../workflows/IOUSettleFlowArgs.java | 25 ++ .../obligation/workflows/IOUTransferFlow.java | 127 +++++++ .../workflows/IOUTransferFlowArgs.java | 26 ++ .../obligation/workflows/ListIOUFlow.java | 59 ++++ .../workflows/ListIOUFlowResults.java | 44 +++ .../workflows/ReceiveTransactionFlow.java | 26 ++ .../workflows/sendAndReceiveTransaction.java | 99 ++++++ .../sendAndRecieveTransactionArgs.java | 33 ++ .../samples/obligation/MyFirstFlowTest.java | 41 +++ .../sendAndRecieveTransaction/.ci/Jenkinsfile | 11 + .../.ci/nightly/JenkinsfileSnykScan | 6 + .../sendAndRecieveTransaction/.gitignore | 86 +++++ .../runConfigurations/DebugCorDapp.run.xml | 15 + .../sendAndRecieveTransaction/.snyk | 14 + .../FlowManagementUI/Dockerfile | 5 + .../FlowManagementUI/README.md | 84 +++++ .../FlowManagementUI/app.py | 12 + .../FlowManagementUI/requirements.txt | 1 + .../FlowManagementUI/static/Scripts/script.js | 322 ++++++++++++++++++ .../FlowManagementUI/static/css/main.css | 202 +++++++++++ .../FlowManagementUI/templates/index.html | 87 +++++ .../sendAndRecieveTransaction/README.md | 120 +++++++ .../sendAndRecieveTransaction/aliceVault.png | Bin 0 -> 17869 bytes .../sendAndRecieveTransaction/build.gradle | 88 +++++ .../charlieAfter.png | Bin 0 -> 50718 bytes .../charlieBefore.png | Bin 0 -> 9976 bytes .../config/combined-worker-compose.yaml | 87 +++++ .../config/gradle-plugin-default-key.pem | 13 + .../config/log4j2.xml | 51 +++ .../config/r3-ca-key.pem | 32 ++ .../config/static-network-config.json | 23 ++ .../contracts/build.gradle | 88 +++++ .../obligation/contracts/IOUContract.kt | 55 +++ .../samples/obligation/states/IOUState.kt | 36 ++ .../gradle.properties | 73 ++++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 61574 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../sendAndRecieveTransaction/gradlew | 244 +++++++++++++ .../sendAndRecieveTransaction/gradlew.bat | 92 +++++ .../sendAndRecieveTransaction/settings.gradle | 25 ++ .../workflows/build.gradle | 98 ++++++ .../obligation/workflows/FinalizeIOUFlow.kt | 98 ++++++ .../obligation/workflows/IOUIssueFlow.kt | 119 +++++++ .../obligation/workflows/IOUSettleFlow.kt | 119 +++++++ .../obligation/workflows/IOUTransferFlow.kt | 116 +++++++ .../obligation/workflows/ListIOUFlow.kt | 58 ++++ .../sendAndRecieveTransactionFlow.kt | 110 ++++++ .../samples/obligation/MyFirstFlowTest.kt | 45 +++ 84 files changed, 5531 insertions(+) create mode 100644 java-samples/sendAndRecieveTransaction/.ci/Jenkinsfile create mode 100644 java-samples/sendAndRecieveTransaction/.ci/nightly/JenkinsfileSnykScan create mode 100644 java-samples/sendAndRecieveTransaction/.gitignore create mode 100644 java-samples/sendAndRecieveTransaction/.run/runConfigurations/DebugCorDapp.run.xml create mode 100644 java-samples/sendAndRecieveTransaction/.snyk create mode 100644 java-samples/sendAndRecieveTransaction/FlowManagementUI/Dockerfile create mode 100644 java-samples/sendAndRecieveTransaction/FlowManagementUI/README.md create mode 100644 java-samples/sendAndRecieveTransaction/FlowManagementUI/app.py create mode 100644 java-samples/sendAndRecieveTransaction/FlowManagementUI/requirements.txt create mode 100644 java-samples/sendAndRecieveTransaction/FlowManagementUI/static/Scripts/script.js create mode 100644 java-samples/sendAndRecieveTransaction/FlowManagementUI/static/css/main.css create mode 100644 java-samples/sendAndRecieveTransaction/FlowManagementUI/templates/index.html create mode 100644 java-samples/sendAndRecieveTransaction/README.md create mode 100644 java-samples/sendAndRecieveTransaction/aliceVault.png create mode 100644 java-samples/sendAndRecieveTransaction/build.gradle create mode 100644 java-samples/sendAndRecieveTransaction/charlieAfter.png create mode 100644 java-samples/sendAndRecieveTransaction/charlieBefore.png create mode 100644 java-samples/sendAndRecieveTransaction/config/combined-worker-compose.yaml create mode 100644 java-samples/sendAndRecieveTransaction/config/gradle-plugin-default-key.pem create mode 100644 java-samples/sendAndRecieveTransaction/config/log4j2.xml create mode 100644 java-samples/sendAndRecieveTransaction/config/r3-ca-key.pem create mode 100644 java-samples/sendAndRecieveTransaction/config/static-network-config.json create mode 100644 java-samples/sendAndRecieveTransaction/contracts/build.gradle create mode 100644 java-samples/sendAndRecieveTransaction/contracts/src/main/java/com/r3/developers/samples/obligation/contracts/IOUContract.java create mode 100644 java-samples/sendAndRecieveTransaction/contracts/src/main/java/com/r3/developers/samples/obligation/states/IOUState.java create mode 100644 java-samples/sendAndRecieveTransaction/gradle.properties create mode 100644 java-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.jar create mode 100644 java-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.properties create mode 100644 java-samples/sendAndRecieveTransaction/gradlew create mode 100644 java-samples/sendAndRecieveTransaction/gradlew.bat create mode 100644 java-samples/sendAndRecieveTransaction/settings.gradle create mode 100644 java-samples/sendAndRecieveTransaction/workflows/build.gradle create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/FinalizeIOUFlow.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUIssueFlow.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUIssueFlowArgs.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUSettleFlow.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUSettleFlowArgs.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUTransferFlow.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUTransferFlowArgs.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ListIOUFlow.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ListIOUFlowResults.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ReceiveTransactionFlow.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/sendAndReceiveTransaction.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/sendAndRecieveTransactionArgs.java create mode 100644 java-samples/sendAndRecieveTransaction/workflows/src/test/java/com/r3/developers/samples/obligation/MyFirstFlowTest.java create mode 100644 kotlin-samples/sendAndRecieveTransaction/.ci/Jenkinsfile create mode 100644 kotlin-samples/sendAndRecieveTransaction/.ci/nightly/JenkinsfileSnykScan create mode 100644 kotlin-samples/sendAndRecieveTransaction/.gitignore create mode 100644 kotlin-samples/sendAndRecieveTransaction/.run/runConfigurations/DebugCorDapp.run.xml create mode 100644 kotlin-samples/sendAndRecieveTransaction/.snyk create mode 100644 kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/Dockerfile create mode 100644 kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/README.md create mode 100644 kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/app.py create mode 100644 kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/requirements.txt create mode 100644 kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/static/Scripts/script.js create mode 100644 kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/static/css/main.css create mode 100644 kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/templates/index.html create mode 100644 kotlin-samples/sendAndRecieveTransaction/README.md create mode 100644 kotlin-samples/sendAndRecieveTransaction/aliceVault.png create mode 100644 kotlin-samples/sendAndRecieveTransaction/build.gradle create mode 100644 kotlin-samples/sendAndRecieveTransaction/charlieAfter.png create mode 100644 kotlin-samples/sendAndRecieveTransaction/charlieBefore.png create mode 100644 kotlin-samples/sendAndRecieveTransaction/config/combined-worker-compose.yaml create mode 100644 kotlin-samples/sendAndRecieveTransaction/config/gradle-plugin-default-key.pem create mode 100644 kotlin-samples/sendAndRecieveTransaction/config/log4j2.xml create mode 100644 kotlin-samples/sendAndRecieveTransaction/config/r3-ca-key.pem create mode 100644 kotlin-samples/sendAndRecieveTransaction/config/static-network-config.json create mode 100644 kotlin-samples/sendAndRecieveTransaction/contracts/build.gradle create mode 100644 kotlin-samples/sendAndRecieveTransaction/contracts/src/main/kotlin/com/r3/developers/samples/obligation/contracts/IOUContract.kt create mode 100644 kotlin-samples/sendAndRecieveTransaction/contracts/src/main/kotlin/com/r3/developers/samples/obligation/states/IOUState.kt create mode 100644 kotlin-samples/sendAndRecieveTransaction/gradle.properties create mode 100644 kotlin-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.jar create mode 100644 kotlin-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.properties create mode 100644 kotlin-samples/sendAndRecieveTransaction/gradlew create mode 100644 kotlin-samples/sendAndRecieveTransaction/gradlew.bat create mode 100644 kotlin-samples/sendAndRecieveTransaction/settings.gradle create mode 100644 kotlin-samples/sendAndRecieveTransaction/workflows/build.gradle create mode 100644 kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/FinalizeIOUFlow.kt create mode 100644 kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUIssueFlow.kt create mode 100644 kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUSettleFlow.kt create mode 100644 kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUTransferFlow.kt create mode 100644 kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/ListIOUFlow.kt create mode 100644 kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/sendAndRecieveTransactionFlow.kt create mode 100644 kotlin-samples/sendAndRecieveTransaction/workflows/src/test/kotlin/com/r3/developers/samples/obligation/MyFirstFlowTest.kt diff --git a/java-samples/sendAndRecieveTransaction/.ci/Jenkinsfile b/java-samples/sendAndRecieveTransaction/.ci/Jenkinsfile new file mode 100644 index 0000000..f926354 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/.ci/Jenkinsfile @@ -0,0 +1,10 @@ +@Library('corda-shared-build-pipeline-steps@5.0') _ + +cordaPipeline( + nexusAppId: 'com.corda.CSDE-Java.5.0', + publishRepoPrefix: '', + slimBuild: true, + runUnitTests: false, + dedicatedJobForSnykDelta: false, + slackChannel: '#corda-corda5-dev-ex-build-notifications' + ) diff --git a/java-samples/sendAndRecieveTransaction/.ci/nightly/JenkinsfileSnykScan b/java-samples/sendAndRecieveTransaction/.ci/nightly/JenkinsfileSnykScan new file mode 100644 index 0000000..fc2b1ee --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/.ci/nightly/JenkinsfileSnykScan @@ -0,0 +1,6 @@ +@Library('corda-shared-build-pipeline-steps@5.0') _ + +cordaSnykScanPipeline ( + snykTokenId: 'r3-snyk-corda5', + snykAdditionalCommands: "--all-sub-projects -d" +) diff --git a/java-samples/sendAndRecieveTransaction/.gitignore b/java-samples/sendAndRecieveTransaction/.gitignore new file mode 100644 index 0000000..d2879c4 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/.gitignore @@ -0,0 +1,86 @@ + +# Eclipse, ctags, Mac metadata, log files +.classpath +.project +.settings +tags +.DS_Store +*.log +*.orig + +# Created by .ignore support plugin (hsz.mobi) + +.gradle +local.properties +.gradletasknamecache + +# General build files +**/build/* + +lib/quasar.jar + +**/logs/* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/*.xml +.idea/.name +.idea/copyright +.idea/inspectionProfiles +.idea/libraries +.idea/shelf +.idea/dataSources +.idea/markdown-navigator +.idea/runConfigurations +.idea/dictionaries + + +# Include the -parameters compiler option by default in IntelliJ required for serialization. +!.idea/codeStyleSettings.xml + + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +**/out/ +/classes/ + + + +# vim +*.swp +*.swn +*.swo + + + +# Directory generated during Resolve and TestOSGi gradle tasks +bnd/ + +# Ignore Gradle build output directory +build +/.idea/codeStyles/codeStyleConfig.xml +/.idea/codeStyles/Project.xml + + + +# Ignore Visual studio directory +bin/ + + + +*.cpi +*.cpb +*.cpk +workspace/** + +# ingore temporary data files +*.dat \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/.run/runConfigurations/DebugCorDapp.run.xml b/java-samples/sendAndRecieveTransaction/.run/runConfigurations/DebugCorDapp.run.xml new file mode 100644 index 0000000..1d8da82 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/.run/runConfigurations/DebugCorDapp.run.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/.snyk b/java-samples/sendAndRecieveTransaction/.snyk new file mode 100644 index 0000000..c04521f --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/.snyk @@ -0,0 +1,14 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.25.0 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JAVA-ORGJETBRAINSKOTLIN-2393744: + - '*': + reason: >- + This vulnerability relates to information exposure via creation of + temporary files (via Kotlin functions) with insecure permissions. + Corda does not use any of the vulnerable functions so it is not + susceptible to this vulnerability + expires: 2023-10-19T17:15:26.836Z + created: 2023-02-02T17:15:26.839Z +patch: {} diff --git a/java-samples/sendAndRecieveTransaction/FlowManagementUI/Dockerfile b/java-samples/sendAndRecieveTransaction/FlowManagementUI/Dockerfile new file mode 100644 index 0000000..016bc21 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/FlowManagementUI/Dockerfile @@ -0,0 +1,5 @@ +FROM python +WORKDIR /app +COPY . /app +RUN pip install -r requirements.txt +CMD ["python3", "app.py"] \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/FlowManagementUI/README.md b/java-samples/sendAndRecieveTransaction/FlowManagementUI/README.md new file mode 100644 index 0000000..fffff29 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/FlowManagementUI/README.md @@ -0,0 +1,84 @@ +# Corda 5 CorDapp Flow Management Tool + + +This user guide provides step-by-step instructions on using the Corda 5 flow management tool. This article will help you learn how to connect the running corDapp, make flow calls, configure flow queries, and retrieve results. + +## Prerequisites +* Install and run Python and Flask framework. link. + +* Prepare your local Corda 5 environment. (By default, the Flow Management Tool is looking to connect to https://localhost:8888/api/v5_2/swagger#/ with Login: Admin and Password: Admin.) + +* Clong the Flow Management Tool repository. FlowManagementUI: main + +## Set Up + +1. Assuming your local Corda 5 environment is populated and the swagger endpoint is at: https://localhost:8888/api/v5_2/swagger#/ + +2. Navigate to where you downloaded the Corda 5 Flow Management Tool + +3. To run the framework + * Navigate to the file name using cd command. + * use the python app.py command to run it. + ![image](https://github.com/parisyup/FlowManagementUI/assets/51169685/f0c3bf59-8180-48a0-91cc-80f2d260e530) + + * Later on, click on the IP Address which will open the Interface: + +![image(4)](https://github.com/parisyup/FlowManagementUI/assets/66366646/8d88e37c-edbb-4d6d-8bcd-d773e818a106) + + +The Flow Management Tool should be automatically connected with the CorDapp running locally from your CSDE. You can test the connection by click on the dropdown list at the Flow Initiator section. You should be able to see the vNodes of your started CorDapp from CSDE. + +![image](https://github.com/parisyup/FlowManagementUI/assets/51169685/5a2356f2-cd14-489c-abd0-4afe0bf0d251) + +## Set Up With Docker + +1- Open up Command Prompt + +2- Navigate to the application folder using the CD commands + +3- Ensure that Docker application is open and build the image using the following command: +`docker build -t your-image-name .` + +Make sure to include the dot at the end of the command + +the `your-image-name` at the end of the command can be whatever you like but make sure to use the same name in the next step + +4- Run the docker image using the following command: +`docker run --rm -it --expose 8888 -p 5000:5000 your-image-name` + +5- You can access the website by using https://localhost:5000 or https://127.0.0.1:5000 + +## Using the Flow Management Tool + +### Selecting the Flow Initiator + +As the first step of using the Flow Management Tool, you would need to select the Flow Initiator. The Flow Initiator indicates which vNode will be triggering the flow. If you wish to have Alice to run a transaction to Bob, select the X500Name of Alice. The selected vNode’s shortHash (Corda 5 Network participant identifier) will also be shown below the dropdown list to signify your selection. + +### Function 1: To Make a Flow Call + +1. Click on "Flow Call" tab in the application. +2. Paste the your JSON format request body into the request input box. +3. Click Post button to trigger the call. + +![image(5)](https://github.com/parisyup/FlowManagementUI/assets/66366646/c65195a6-0a70-4354-804e-37884f657746) + + +### Function 2: To Configure Flow Query + +1. Click on the “Flow Query” tab. +2. Choose whether to query a single flow or all flows at the selected Flow Initiator. +3. If you choose to query all of the flows, select “Query All Flows“ then click “Get“. + +![image](https://github.com/parisyup/FlowManagementUI/assets/51169685/0482cfa4-7ee1-42f2-8786-2d8ad80b2936) +4. If you choose to query a single flow, select “Query Single Flow“, please add the ClientID in specified filed. +5. Click on “Get” to retrieve the result. + +![image(6)](https://github.com/parisyup/FlowManagementUI/assets/66366646/13e979b0-f76e-4f2c-9d55-81be8880890b) + +If you have any suggestions or questions, feel free to give us your feedback through Github for a better experience in the future! + +## Conclusion +In summary, our project introduces a specialized flow management layer on top of Swagger for Corda developers. We understand the challenges developers face in testing Corda applications due to the complexity of commands, our solution focuses on simplifying the process. + +Our all-in-one flow management system provides developers with a unified platform, streamlining development and enhancing efficiency. A key feature allows developers to run flows directly from an externally developed website and monitor their status in real-time, offering a user-friendly and practical solution for Corda developers. Overall, our project aims to make Corda development more accessible and tailored to the specific needs of flow management. + diff --git a/java-samples/sendAndRecieveTransaction/FlowManagementUI/app.py b/java-samples/sendAndRecieveTransaction/FlowManagementUI/app.py new file mode 100644 index 0000000..5d3e3f6 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/FlowManagementUI/app.py @@ -0,0 +1,12 @@ +from flask import Flask +from flask import render_template +app = Flask(__name__) + + +@app.route('/') +def home(): # put application's code here + return render_template("index.html") + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') + diff --git a/java-samples/sendAndRecieveTransaction/FlowManagementUI/requirements.txt b/java-samples/sendAndRecieveTransaction/FlowManagementUI/requirements.txt new file mode 100644 index 0000000..8ab6294 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/FlowManagementUI/requirements.txt @@ -0,0 +1 @@ +flask \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/FlowManagementUI/static/Scripts/script.js b/java-samples/sendAndRecieveTransaction/FlowManagementUI/static/Scripts/script.js new file mode 100644 index 0000000..8fd4ec0 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/FlowManagementUI/static/Scripts/script.js @@ -0,0 +1,322 @@ +// This script contains functions for making API requests, handling data, and updating the UI. + +// Variable to store the selected X500Name from the dropdown +let selectedX500Name; + +// Variable to indicate whether data is currently being loaded from the pull request +let loading = false; + +// Function to make a GET request to an external API and return the data +function getData() { + // Replace the URL with the actual API endpoint + return fetch('https://jsonplaceholder.typicode.com/todos/1') + .then(response => response.json()) + .then(data => { + console.log('API Result:', data); + + // Return specific data fields + return { + id: data.id, + title: data.title + }; + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to get the data and display it on the page +function getDataAndDisplay() { + getData() + .then(result => { + // Update the result div with the data + document.getElementById('result').innerHTML = ` +

ID: ${result.id}

+

Title: ${result.title}

+ `; + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to make a GET request to retrieve CPI data +function getCPI() { + const url = 'https://localhost:8888/api/v1/cpi'; + // Perform the GET request with authorization headers + return fetch(url, { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=' + } + }) + .then(response => response.json()) + .then(data => { + console.log('API Result:', data); + // Further processing of the data can be done here + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to get all virtual nodes and populate a dropdown with the data +function getAllVirtualNodes() { + const url = 'https://localhost:8888/api/v1/virtualnode'; + + return fetch(url, { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=' + } + }) + .then(response => response.json()) + .then(data => { + // Extract virtualNodes from the API response + const virtualNodes = data.virtualNodes; + console.log('API Result:', virtualNodes); + + // Process the data and populate the dropdown + if (Array.isArray(virtualNodes)) { + const dropdown = document.getElementById('itemDropdown'); + dropdown.innerHTML = ''; + dropdown.innerHTML += ''; + + virtualNodes.forEach(item => { + // Display each item on the console + console.log('Item X500Name:', item.holdingIdentity.x500Name); + console.log('Item ShortHash:', item.holdingIdentity.shortHash); + + // Create an option element and add it to the dropdown + const option = document.createElement('option'); + option.value = item.holdingIdentity.shortHash; + option.text = item.holdingIdentity.x500Name; + dropdown.appendChild(option); + }); + + // Add event listener to the dropdown to detect changes + dropdown.addEventListener('change', function () { + selectedX500Name = this.value; + console.log('Selected ShortHash:', selectedX500Name); + + // Call a function or update a variable based on the selected item + handleDropdownChange(selectedX500Name); + }); + + } else { + console.warn('API Result is not an array.'); + } + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Initialize the virtual nodes dropdown on page load +getAllVirtualNodes(); + +// function to handle dropdown change +function handleDropdownChange(selectedX500Name) { + console.log('Handling dropdown change for Item ID:', selectedX500Name); + getSelectedVNode(); + // Additional actions based on the selected item can be performed here +} + +// Function to get the selected virtual node and display it +function getSelectedVNode() { + const displayElement = document.getElementById('selectedX500Display'); + displayElement.textContent = `Selected X500: ${selectedX500Name}`; +} + +// Function to get all flow results based on the selected virtual node +function getAllFlowResult() { + const url = `https://localhost:8888/api/v1/flow/${selectedX500Name}`; + + return fetch(url, { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=' + } + }) + .then(response => response.json()) + .then(data => { + const flowStatusResponses = data.flowStatusResponses; + console.log('API Result:', flowStatusResponses); + + // Convert the JSON object to a string for display + const jsonString = JSON.stringify(flowStatusResponses, null, 2); + + // Display the JSON string in the result div + document.getElementById('idResutl').innerHTML = `
${jsonString}
`; + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to make a POST request with a request body +async function postCallFlow() { + let postBtn = $('#postBtn'); + const url = `https://localhost:8888/api/v1/flow/${selectedX500Name}`; + + // Change the button text to indicate loading + postBtn.html('Loading...'); + + try { + // Perform the POST request with the provided request body + const response = await fetch(url, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=', + 'Content-Type': 'application/json' + }, + body: `${document.getElementById('requestBody').value}` + }); + + // Parse the response as JSON + const data = await response.json(); + + if (!data.ok) { + console.log(data.status); + + // Check if the response status is not OK (2xx range) + if (data.status == "409" || data.status == "400") { + let flowStatusResponse = "title: " + data.title + "\nStatus: " + data.status; + + // Display additional details for 400 status + if (data.status == "400") { + flowStatusResponse += "\nDetails: \n Cause: " + data.details.cause + "\n Reason: " + data.details.reason; + } + + document.getElementById('queryResult').innerHTML = `
${flowStatusResponse}
`; + return; + } + } + + let msg = "null"; + let typ = "null"; + + // Construct a string with the flow status responses + let flowStatusResponses = "client request ID: " + data.clientRequestId + + "\nFlow Result " + data.flowResult + + "\nFlow Error Message: " + msg + + "\nflow error type: " + typ + + "\nFlow ID: " + data.flowId + + "\nFlow status: " + data.flowStatus + + "\nHolding identity short hash: " + data.holdingIdentityShortHash + + "\nTime stamp: " + data.timestamp; + + console.log('API Result:', flowStatusResponses); + + // Display the flow status responses in the result div + document.getElementById('queryResult').innerHTML = `
${flowStatusResponses}
`; + } catch (error) { + console.log('Error:', error); + } finally { + // Restore the button text after the operation is complete + postBtn.html('Post'); + } +} + +// Function to display an item on the page +function displayItemOnPage(item) { + const resultDiv = document.getElementById('queryResult'); + + // Create a new element to display the item + const itemElement = document.createElement('div'); + itemElement.innerHTML = ` +

QueryResult: ${item}

+ `; + + // Append the new element to the result div + resultDiv.appendChild(itemElement); +} + +// Function to open a specific tab by hiding/showing content +function openTab(tabName) { + // Hide all tab content + var tabContents = document.getElementsByClassName("tab-content"); + for (var i = 0; i < tabContents.length; i++) { + tabContents[i].style.display = "none"; + document.getElementById(tabContents[i].id + "-tab").style.backgroundColor = "rgb(52,152,219)"; + } + + // Show the selected tab content + var selectedTab = document.getElementById(tabName); + if (selectedTab) { + selectedTab.style.display = "block"; + document.getElementById(tabName + "-tab").style.backgroundColor = "rgb(173,216,230)"; + } +} + +// Function to display a flow for a specific virtual node +function oneFlow() { + const url = `https://localhost:8888/api/v1/flow/${selectedX500Name}/${document.getElementById('clientID').value}`; + + // Validate clientID input + if (document.getElementById('clientID').value == "") { + alert("Please input a clientId"); + return; + } + + // Perform a GET request to display a flow + return fetch(url, { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=' + } + }) + .then(response => response.json()) + .then(data => { + let msg = "null"; + let typ = "null"; + + // Check if the flow status is "FAILED" and extract error details + if (data.flowStatus == "FAILED") { + msg = data.flowError.message; + typ = data.flowError.type; + } + + // Construct a string with the flow status responses + const flowStatusResponses = "client request ID: " + data.clientRequestId + + "\nFlow Result " + data.flowResult + + "\nFlow Error Message: " + msg + + "\nflow error type: " + typ + + "\nFlow ID: " + data.flowId + + "\nFlow status: " + data.flowStatus + + "\nHolding identity short hash: " + data.holdingIdentityShortHash + + "\nTime stamp: " + data.timestamp; + + console.log('API Result:', flowStatusResponses); + + // Display the flow status responses in the result div + document.getElementById('idResutl').innerHTML = `
${flowStatusResponses}
`; + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to determine which flow-related action to execute based on user input +function executeButtonFlow() { + // Check the value of the dropdown to determine which action to perform + if (document.getElementById("dropdown").value == "option1") { + getAllFlowResult(); + } else { + oneFlow(); + } +} + +function queryDropDownChange(){ + if (document.getElementById("dropdown").value == "option1") { + document.getElementById("clientID").style.display = 'none'; + }else{ + document.getElementById("clientID").style.display = 'block'; + + } +} \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/FlowManagementUI/static/css/main.css b/java-samples/sendAndRecieveTransaction/FlowManagementUI/static/css/main.css new file mode 100644 index 0000000..5e4d849 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/FlowManagementUI/static/css/main.css @@ -0,0 +1,202 @@ +body { + font-family: 'Roboto', sans-serif; + background-color: #f4f4f4; + color: #333; + margin: 50px; /* Add margin to the entire body */ + padding: 0; +} + +h1 { + text-align: center; + color: #0e0c0c; +} + +/* Style for the label */ +label { + display: block; + margin-bottom: 10px; + font-weight: bold; + color: #333; +} +/* Style for the dropdown button */ +select { + width: 20%; + padding: 8px; + margin-right: 10px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + font-size: 14px; + color: #555; +} + +#itemDropdown { + width: 40%; + padding: 10px; + box-sizing: border-box; +} + +#clientID{ + padding: 8px; + margin-bottom: 15px; + margin-right: 10px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + font-size: 14px; + color: #555; +} + +#OneFlowButon{ + padding: 8px; + margin-bottom: 15px; + margin-right: 10px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + font-size: 14px; +} + +#result { + margin-bottom: 10px; +} + +/* Button styling */ +button { + background-color: #3498db; + color: #fff; + padding: 10px 25px; + font-size: 16px; + border: 2px; + border-radius: 15px; + cursor: pointer; + transition: background-color 0.3s ease; +} + +button:hover { + background-color: #2980b9; +} + +/* Tab styling */ +.tab { + list-style-type: none; /* Remove default list styles */ + display: inline-block; /* Display tabs inline */ + padding: 2px 00px; /* Add padding to the tabs */ + margin: 0 1px; /* Add margin between tabs */ + cursor: pointer; /* Change cursor to pointer on hover */ +} + + +.tab li { + flex: 1; + text-align: center; + padding: 10px; + background-color: #3498db; + color: #fff; + border-radius: 8px 15px 0 0; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.tab li:hover { + background-color: #2980b9; +} + +/* Tab content styling */ +.tab-content { + display: none; + padding: 20px; + border: 1px solid #3498db; + border-radius: 0 0 5px 5px; + background-color: #fff; +} + +.styled-input { + width: 100%; + padding: 10px; + margin-bottom: 10px; + box-sizing: border-box; +} + +.output { + border: 1px solid #3498db; + padding: 10px; + border-radius: 5px; + background-color: #fff; + box-sizing: border-box; +} + +#idResutl { + border: 1px solid #3498db; + padding: 10px; + border-radius: 5px; + background-color: #fff; + margin-top: 10px; + height: 290px; + max-height: 290px; /* Set a maximum height for the scroll box */ + overflow-y: auto; /* Enable vertical scrolling if content exceeds the box height */ +} + +/* Responsive design */ +@media screen and (max-width: 600px) { + .tab li { + border-radius: 5px; + margin-bottom: 5px; + } + .tab-content { + border-radius: 5px; + } +} +#call { + display: -ms-inline-flexbox; + flex-wrap: wrap; + +} + +/* Style for side-by-side input boxes */ +.flowcall-container { + display: flex; +} + +.input-box, .text-box { + width: 150px; /* Set the desired width */ + margin-right: 10px; /* Optional: Add margin for spacing between input boxes */ + border-radius: 5px; + border: 1px solid #3498db; +} + +.queryoption-container{ + display: flex; +} + + + +#requestBody { + flex: 1; + box-sizing: border-box; + width: 100px; /* Set the desired width */ + height: 300px; /* Set the desired height */ + padding: 10px; /* Optional: Add padding for better aesthetics */ + margin-right: 10px; /* Add some margin between the input and button */ +} + +#postBtn { + flex: 0 0 auto; /* Don't allow the button to grow or shrink */ + margin-top: 10px; /* Add some margin between the button and result box */ + margin-right: 10px; /* Add some margin between the button and result box */ + width: 500px; /* Set the desired width */ + height: 50px; /* Set the desired height */ +} + +#queryResult { + flex: 1; + box-sizing: border-box; + width: 100px; /* Set the desired width */ + height: 300px; /* Set the desired height */ + padding: 10px; /* Optional: Add padding for better aesthetics */ +} +.content-box{ + padding: 10px; /* Add padding to the content boxes */ +} + + diff --git a/java-samples/sendAndRecieveTransaction/FlowManagementUI/templates/index.html b/java-samples/sendAndRecieveTransaction/FlowManagementUI/templates/index.html new file mode 100644 index 0000000..44df7ac --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/FlowManagementUI/templates/index.html @@ -0,0 +1,87 @@ + + + + + + + + + + Flask Frontend Example + + + + + + + + + + + + +

Flow Management APIs

+
+ + + + + + +

Please select a flow initiator

+ + + + + +
+ +
+ + + + +
+
+ + +
Result will be displayed here
+
+ +
+
+ + +
+ + + +
+ + + + + + + + +
+ + + + + +
Result will be displayed here
+
+ + diff --git a/java-samples/sendAndRecieveTransaction/README.md b/java-samples/sendAndRecieveTransaction/README.md new file mode 100644 index 0000000..46f983b --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/README.md @@ -0,0 +1,120 @@ +# sendAndReceiveTransaction + +When working with the Corda platform, every transaction is stored in the participants' +vaults. The vault is a where all the transactions involving the owner are securely saved. +Each vault is unique and accessible only by its owner, +serving as a ledger to track all the owner's transactions. +However, there are scenarios where you may want a third party to receive a copy of the +transaction. This is where the SendAndRecieveTransaction function becomes essential. +For instance, if Alice conducts a transaction with Bob and wants Charlie to receive a copy, +Alice can simply run a flow using the transaction ID to send a copy to Charlie's vault. +Another application of this function can be an automated reporting tool, +which can be utilized at the end of each transaction finalizing flow to automatically +report to a specific vnode. This functionality can act like a bookkeeper, +meticulously tracking each transaction and ensuring accurate record-keeping. +` + +### Setting up + +1. We will begin our test deployment with clicking the `startCorda`. This task will load up the combined Corda workers in docker. + A successful deployment will allow you to open the REST APIs at: https://localhost:8888/api/v5_2/swagger#/. You can test out some + functions to check connectivity. (GET /cpi function call should return an empty list as for now.) +2. We will now deploy the cordapp with a click of `5-vNodeSetup` task. Upon successful deployment of the CPI, the GET /cpi function call should now return the meta data of the cpi you just upload + + + +### Running the app + +In Corda 5, flows will be triggered via `POST /flow/{holdingidentityshorthash}` and flow result will need to be view at `GET /flow/{holdingidentityshorthash}/{clientrequestid}` +* holdingidentityshorthash: the id of the network participants, ie Bob, Alice, Charlie. You can view all the short hashes of the network member with another gradle task called `ListVNodes` +* clientrequestid: the id you specify in the flow requestBody when you trigger a flow. + +#### Step 1: Create IOUState between two parties +Pick a VNode identity to initiate the IOU creation, and get its short hash. (Let's pick Alice. Don't pick Bob because Bob is the person who alice will borrow from). + +Go to `POST /flow/{holdingidentityshorthash}`, enter the identity short hash(Alice's hash) and request body: +``` +{ + "clientRequestId": "createiou-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.IOUIssueFlow", + "requestBody": { + "amount":"20", + "lender":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB" + } +} +``` + +After trigger the create-IOU flow, hop to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and enter the short hash(Alice's hash) and client request id to view the flow result +The stateRef of the transaction will be returned as a result of the flow query. Which is the "flowResult". + +#### Step 2: Sending a copy of the transaction to a third party. +If a member needs to share a copy of their transaction with another member, +they can do so using the process outlined below. For instance, +if Alice wishes to send a copy of the transaction to Dave, +we can execute the following request body with her short hash: + +``` +{ + "clientRequestId": "sendAndRecieve-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.sendAndRecieveTransactionFlow", + "requestBody": { + "stateRef": "STATEREF ID HERE", + "members": ["CN=Dave, OU=Test Dept, O=R3, L=London, C=GB"], + "forceBackchain": "false" + } +} +``` + +Ensure to replace the stateRef variable with the stateRef of the transaction. The stateRef will be found when you run the GET call, +QueryAll in the swaggerAPI. The stateRef is labeled as `flowResult` in response body, begins with SHA-256D:XXXXX.. +``` +[ + { + "holdingIdentityShortHash": "A93A019B324E", + "clientRequestId": "createiou-1", + "flowId": "26ea3f95-141b-4aaa-9b58-e3a685dc54d3", + "flowStatus": "COMPLETED", + "flowResult": "SHA-256D:B3D87C8B446C277B5658BBB2A18DC7491539D898B70F074418878091AE315B4A", + "flowError": null, + "timestamp": "2024-07-08T04:37:16.175Z" + } +] +``` +After running this flow Dave will have the transaction in his vault. + + +Results + +Currently, Alice has two transactions stored in her vault. +The transaction we aim to send to Charlie is identified by the ID SHA-256D:8DFDD672…. +You can view Alice's transactions in the image provided below: + +

+ Encumbrance Flow +

+ +On the other hand, Charlie’s vault currently holds no transactions, +as illustrated in the image below: + +

+ Encumbrance Flow +

+ +Once Alice executes the flow, Charlie’s vault is updated to include the transaction, +which is displayed as follows: + +

+ Encumbrance Flow +

+ +All images of the vault were sourced through DBeaver by establishing a connection +to the Cordapp using PostgreSQL. The credentials utilized for this connection are as follows: + +POSTGRES_DB = cordacluster
+POSTGRES_USER = postgres
+POSTGRES_PASSWORD = password + +To access the vault, navigate through the hierarchy in +PostgreSQL: Databases > cordacluster > Schemas > vnode_vault_(HASH_ID_OF_VNODE) > +Tables > utxo_transaction. To view the transactions, +simply double-click on utxo_transaction. \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/aliceVault.png b/java-samples/sendAndRecieveTransaction/aliceVault.png new file mode 100644 index 0000000000000000000000000000000000000000..d377488d4b50cbdc78d2c8199dabf5fbad10b0e7 GIT binary patch literal 17869 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 AK>z>% literal 0 HcmV?d00001 diff --git a/java-samples/sendAndRecieveTransaction/build.gradle b/java-samples/sendAndRecieveTransaction/build.gradle new file mode 100644 index 0000000..eb0a9bd --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/build.gradle @@ -0,0 +1,73 @@ +import static org.gradle.api.JavaVersion.VERSION_17 +import static org.gradle.jvm.toolchain.JavaLanguageVersion.of + +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'net.corda.cordapp.cordapp-configuration' + id 'org.jetbrains.kotlin.plugin.jpa' + id 'java' + id 'maven-publish' + id 'net.corda.gradle.plugin' +} + +allprojects { + group 'com.r3.developers.cordapptemplate' + version '1.0-SNAPSHOT' + + // Configure Corda runtime gradle plugin + cordaRuntimeGradlePlugin { + notaryVersion = cordaNotaryPluginsVersion + notaryCpiName = "NotaryServer" + corDappCpiName = "MyCorDapp" + cpiUploadTimeout = "30000" + vnodeRegistrationTimeout = "60000" + cordaProcessorTimeout = "300000" + workflowsModuleName = "workflows" + cordaClusterURL = "https://localhost:8888" + cordaRestUser = "admin" + cordaRestPasswd ="admin" + composeFilePath = "config/combined-worker-compose.yaml" + networkConfigFile = "config/static-network-config.json" + r3RootCertFile = "config/r3-ca-key.pem" + skipTestsDuringBuildCpis = "false" + cordaRuntimePluginWorkspaceDir = "workspace" + cordaBinDir = "${System.getProperty("user.home")}/.corda/corda5" + cordaCliBinDir = "${System.getProperty("user.home")}/.corda/cli" + } + + java { + toolchain { + languageVersion = of(VERSION_17.majorVersion.toInteger()) + } + withSourcesJar() + } + + // Declare the set of Java compiler options we need to build a CorDapp. + tasks.withType(JavaCompile) { + // -parameters - Needed for reflection and serialization to work correctly. + options.compilerArgs += [ + "-parameters" + ] + } + + repositories { + // All dependencies are held in Maven Central + mavenLocal() + mavenCentral() + } + + tasks.withType(Test).configureEach { + useJUnitPlatform() + } + +} + +publishing { + publications { + maven(MavenPublication) { + artifactId "corda-dev-template-java-sample" + groupId project.group + artifact jar + } + } +} diff --git a/java-samples/sendAndRecieveTransaction/charlieAfter.png b/java-samples/sendAndRecieveTransaction/charlieAfter.png new file mode 100644 index 0000000000000000000000000000000000000000..51d47a777d1b32d6f19f0685528d83a161cc0225 GIT binary patch literal 50718 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 KMNUMnLSTZBOgaPr literal 0 HcmV?d00001 diff --git a/java-samples/sendAndRecieveTransaction/charlieBefore.png b/java-samples/sendAndRecieveTransaction/charlieBefore.png new file mode 100644 index 0000000000000000000000000000000000000000..e9cb66106212225fd128fc207a1b7fab7c57a63a GIT binary patch literal 9976 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 huL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/config/r3-ca-key.pem b/java-samples/sendAndRecieveTransaction/config/r3-ca-key.pem new file mode 100644 index 0000000..a803613 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/config/r3-ca-key.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/config/static-network-config.json b/java-samples/sendAndRecieveTransaction/config/static-network-config.json new file mode 100644 index 0000000..b0f2519 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/config/static-network-config.json @@ -0,0 +1,23 @@ +[ + { + "x500Name" : "CN=Alice, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "MyCorDapp" + }, + { + "x500Name" : "CN=Bob, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "MyCorDapp" + }, + { + "x500Name" : "CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "MyCorDapp" + }, + { + "x500Name" : "CN=Dave, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "MyCorDapp" + }, + { + "x500Name" : "CN=NotaryRep1, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "NotaryServer", + "serviceX500Name": "CN=NotaryService, OU=Test Dept, O=R3, L=London, C=GB" + } +] diff --git a/java-samples/sendAndRecieveTransaction/contracts/build.gradle b/java-samples/sendAndRecieveTransaction/contracts/build.gradle new file mode 100644 index 0000000..513a070 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/contracts/build.gradle @@ -0,0 +1,86 @@ +plugins { + // Include the cordapp-cpb plugin. This automatically includes the cordapp-cpk plugin as well. + // These extend existing build environment so that CPB and CPK files can be built. + // This includes a CorDapp DSL that allows the developer to supply metadata for the CorDapp + // required by Corda. + id 'net.corda.plugins.cordapp-cpb2' + id 'org.jetbrains.kotlin.jvm' + id 'maven-publish' +} + +// Declare dependencies for the modules we will use. +// A cordaProvided declaration is required for anything that we use that the Corda API provides. +// This is required to allow us to build CorDapp modules as OSGi bundles that CPI and CPB files are built on. +dependencies { + + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + + // Declare a "platform" so that we use the correct set of dependency versions for the version of the + // Corda API specified. + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + + // If using transistive dependencies this will provide most of Corda-API: + // cordaProvided 'net.corda:corda-application' + + // Alternatively we can explicitly specify all our Corda-API dependencies: + cordaProvided 'net.corda:corda-base' + cordaProvided 'net.corda:corda-application' + cordaProvided 'net.corda:corda-crypto' + cordaProvided 'net.corda:corda-membership' + // cordaProvided 'net.corda:corda-persistence' + cordaProvided 'net.corda:corda-serialization' + cordaProvided 'net.corda:corda-ledger-utxo' + cordaProvided 'net.corda:corda-ledger-consensual' + + // CorDapps that use the UTXO ledger must include at least one notary client plugin + cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" + + // The CorDapp uses the slf4j logging framework. Corda-API provides this so we need a 'cordaProvided' declaration. + cordaProvided 'org.slf4j:slf4j-api' + + // 3rd party libraries + // Required + testImplementation "org.slf4j:slf4j-simple:$slf4jVersion" + testImplementation "org.junit.jupiter:junit-jupiter:$junitVersion" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + + // Optional but used by example tests. + testImplementation "org.mockito:mockito-core:$mockitoVersion" + testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion" + testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion" +// testImplementation "com.r3.corda.ledger.utxo:contract-testing:$contractTestingVersion" +} + +// The CordApp section. +// This is part of the DSL provided by the corda plugins to define metadata for our CorDapp. +// Each component of the CorDapp would get its own CorDapp section in the build.gradle file for the component’s +// subproject. +// This is required by the corda plugins to build the CorDapp. +cordapp { + // "targetPlatformVersion" and "minimumPlatformVersion" are intended to specify the preferred + // and earliest versions of the Corda platform that the CorDapp will run on respectively. + // Enforced versioning has not implemented yet so we need to pass in a dummy value for now. + // The platform version will correspond to and be roughly equivalent to the Corda API version. + targetPlatformVersion = platformVersion.toInteger() + minimumPlatformVersion = platformVersion.toInteger() + + // The cordapp section contains either a workflow or contract subsection depending on the type of component. + // Declares the type and metadata of the CPK (this CPB has one CPK). + contract { + name contractsModule + versionId 1 + licence cordappLicense + vendor cordappVendorName + } +} + +// Use the name of the contract module as the name of the generated CPK and CPB. +archivesBaseName = cordapp.contract.name.isPresent() ? cordapp.contract.name.get() : contractsModule + +publishing { + publications { + maven(MavenPublication) { + from components.cordapp + } + } +} \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/contracts/src/main/java/com/r3/developers/samples/obligation/contracts/IOUContract.java b/java-samples/sendAndRecieveTransaction/contracts/src/main/java/com/r3/developers/samples/obligation/contracts/IOUContract.java new file mode 100644 index 0000000..c162215 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/contracts/src/main/java/com/r3/developers/samples/obligation/contracts/IOUContract.java @@ -0,0 +1,50 @@ +package com.r3.developers.samples.obligation.contracts; + +import com.r3.developers.samples.obligation.states.IOUState; +import net.corda.v5.base.exceptions.CordaRuntimeException; +import net.corda.v5.ledger.utxo.Command; +import net.corda.v5.ledger.utxo.Contract; +import net.corda.v5.ledger.utxo.ContractState; +import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction; + +import java.security.PublicKey; +import java.util.Set; + + +public class IOUContract implements Contract { + + //IOU Commands + public static class Issue implements Command { } + public static class Settle implements Command { } + public static class Transfer implements Command { } + + @Override + public void verify(UtxoLedgerTransaction transaction) { + + // Ensures that there is only one command in the transaction + requireThat( transaction.getCommands().size() == 1, "Require a single command."); + Command command = transaction.getCommands().get(0); + IOUState output = transaction.getOutputStates(IOUState.class).get(0); + requireThat(output.getParticipants().size() == 2, "The output state should have two and only two participants."); + + // Switches case based on the command + if(command.getClass() == IOUContract.Issue.class) {// Rules applied only to transactions with the Issue Command. + requireThat(transaction.getOutputContractStates().size() == 1, "Only one output states should be created when issuing an IOU."); + }else if(command.getClass() == IOUContract.Transfer.class) {// Rules applied only to transactions with the Transfer Command. + requireThat( transaction.getInputContractStates().size() > 0, "There must be one input IOU."); + } + else if(command.getClass() == IOUContract.Settle.class) {// Rules applied only to transactions with the Settle Command. + requireThat( transaction.getInputContractStates().size() > 0, "There must be one input IOU."); + } + else { + throw new CordaRuntimeException("Unsupported command"); + } + } + + // Helper function to allow writing constraints in the Corda 4 '"text" using (boolean)' style + private void requireThat(boolean asserted, String errorMessage) { + if(!asserted) { + throw new CordaRuntimeException("Failed requirement: " + errorMessage); + } + } +} diff --git a/java-samples/sendAndRecieveTransaction/contracts/src/main/java/com/r3/developers/samples/obligation/states/IOUState.java b/java-samples/sendAndRecieveTransaction/contracts/src/main/java/com/r3/developers/samples/obligation/states/IOUState.java new file mode 100644 index 0000000..120558a --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/contracts/src/main/java/com/r3/developers/samples/obligation/states/IOUState.java @@ -0,0 +1,81 @@ +package com.r3.developers.samples.obligation.states; + +import com.r3.developers.samples.obligation.contracts.IOUContract; +import net.corda.v5.base.annotations.ConstructorForDeserialization; +import net.corda.v5.base.types.MemberX500Name; +import net.corda.v5.ledger.utxo.BelongsToContract; +import net.corda.v5.ledger.utxo.ContractState; + +import java.security.PublicKey; +import java.util.List; +import java.util.UUID; + +//Link with the Contract class +@BelongsToContract(IOUContract.class) +public class IOUState implements ContractState { + + //private variables + public final int amount; + public final MemberX500Name lender; + public final MemberX500Name borrower; + public final int paid; + private final UUID linearId; + public List participants; + + + @ConstructorForDeserialization + public IOUState(int amount, MemberX500Name lender, MemberX500Name borrower, int paid, UUID linearId, List participants) { + this.amount = amount; + this.lender = lender; + this.borrower = borrower; + this.paid = paid; + this.linearId = linearId; + this.participants = participants; + } + + public IOUState(int amount, MemberX500Name lender, MemberX500Name borrower, List participants) { + this.amount = amount; + this.lender = lender; + this.borrower = borrower; + this.paid = 0; + this.linearId = UUID.randomUUID(); + this.participants = participants; + } + + public int getAmount() { + return amount; + } + + public MemberX500Name getLender() { + return lender; + } + + public MemberX500Name getBorrower() { + return borrower; + } + + public int getPaid() { + return paid; + } + + public UUID getLinearId() { + return linearId; + } + + @Override + public List getParticipants() { + return participants; + } + + //Helper method for settle flow + public IOUState pay(int amountToPay) { + int newAmountPaid = this.paid + (amountToPay); + return new IOUState(amount, lender, borrower, newAmountPaid,this.linearId,this.participants); + } + + //Helper method for transfer flow + public IOUState withNewLender(MemberX500Name newLender, List newParticipants) { + return new IOUState(amount, newLender, borrower, paid,linearId,newParticipants); + } + +} diff --git a/java-samples/sendAndRecieveTransaction/gradle.properties b/java-samples/sendAndRecieveTransaction/gradle.properties new file mode 100644 index 0000000..2549e5f --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/gradle.properties @@ -0,0 +1,73 @@ +kotlin.code.style=official + +# Specify the version of the Corda-API to use. +# This needs to match the version supported by the Corda Cluster the CorDapp will run on. +cordaApiVersion=5.2.0.52 + +# Specify the version of the notary plugins to use. +# Currently packaged as part of corda-runtime-os, so should be set to a corda-runtime-os version. +cordaNotaryPluginsVersion=5.2.0.0 + +# Specify the version of the cordapp-cpb and cordapp-cpk plugins +cordaPluginsVersion=7.0.4 + +# Specify the version of the Corda runtime Gradle plugin to use +cordaGradlePluginVersion=5.2.0.0 + +# Specify the name of the workflows module +# This will be the name of the generated cpk and cpb files +workflowsModule=workflows + +# Specify the name of the contracts module +# This will be the name of the generated cpk and cpb files +contractsModule=contracts + +# Specify the location of where Corda 5 binaries can be downloaded +# Relative path from user.home +cordaBinariesDirectory = .corda/corda5 + +# Specify the location of where Corda 5 CLI binaries can be downloaded +# Relative path from user.home +cordaCliBinariesDirectory = .corda/cli + +# Metadata for the CorDapp. +cordappLicense="Apache License, Version 2.0" +cordappVendorName="R3" + +# For the time being this just needs to be set to a dummy value. +platformVersion = 999 + +# Version of Kotlin to use. +# We recommend using a version close to that used by Corda-API. +kotlinVersion = 1.7.21 + +# Do not use default dependencies. +kotlin.stdlib.default.dependency=false + +# Test Tooling Dependency Versions +junitVersion = 5.10.0 +mockitoKotlinVersion=4.0.0 +mockitoVersion=4.6.1 +hamcrestVersion=2.2 +assertjVersion = 3.24.1 +contractTestingVersion=1.0.0-beta-+ +jacksonVersion=2.15.2 +slf4jVersion=1.7.36 + +# Specify the maximum amount of time allowed for the CPI upload +# As your CorDapp grows you might need to increase this +# Value is in milliseconds +cpiUploadDefault=10000 + +# Specify the length of time, in milliseconds, that Corda waits for an individual event to process. +# Keep at -1 to use the default. Refer to the Corda Api Docs for the exact value. +processorTimeout=-1 + +# Specify the maximum amount of time allowed to check all vNodes are registered +# Value is in milliseconds +vnodeRegistrationTimeoutDefault=30000 + +# Specify if you want to run the contracts and workflows tests as part of the corda-runtime-plugin-cordapp > buildCpis task +# False by default, will execute the tests every time you stand the template up - gives extra protection +# Set to true to skip the tests, making the launching process quicker. You will be responsible for running workflow tests yourself +skipTestsDuringBuildCpis=false \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.jar b/java-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..943f0cbfa754578e88a3dae77fce6e3dea56edbf GIT binary patch literal 61574 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 ScNgau(U?ep-K_E5zy1%ZQTdPn literal 0 HcmV?d00001 diff --git a/java-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.properties b/java-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..5083229 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/java-samples/sendAndRecieveTransaction/gradlew b/java-samples/sendAndRecieveTransaction/gradlew new file mode 100644 index 0000000..65dcd68 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/java-samples/sendAndRecieveTransaction/gradlew.bat b/java-samples/sendAndRecieveTransaction/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java-samples/sendAndRecieveTransaction/settings.gradle b/java-samples/sendAndRecieveTransaction/settings.gradle new file mode 100644 index 0000000..bdec735 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + // Declare the repositories where plugins are stored. + repositories { + gradlePluginPortal() + mavenCentral() + mavenLocal() + } + + // The plugin dependencies with versions of the plugins congruent with the specified CorDapp plugin version, + // Corda API version, and Kotlin version. + plugins { + id 'net.corda.plugins.cordapp-cpk2' version cordaPluginsVersion + id 'net.corda.plugins.cordapp-cpb2' version cordaPluginsVersion + id 'net.corda.cordapp.cordapp-configuration' version cordaApiVersion + id 'org.jetbrains.kotlin.jvm' version kotlinVersion + id 'org.jetbrains.kotlin.plugin.jpa' version kotlinVersion + id 'org.jetbrains.kotlin.plugin.allopen' version kotlinVersion + id 'net.corda.gradle.plugin' version cordaGradlePluginVersion + } +} + +// Root project name, used in naming the project as a whole and used in naming objects built by the project. +rootProject.name = 'sendAndRecieveTransaction' +include ':workflows' +include ':contracts' \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/workflows/build.gradle b/java-samples/sendAndRecieveTransaction/workflows/build.gradle new file mode 100644 index 0000000..748dd3d --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/build.gradle @@ -0,0 +1,96 @@ +plugins { + // Include the cordapp-cpb plugin. This automatically includes the cordapp-cpk plugin as well. + // These extend existing build environment so that CPB and CPK files can be built. + // This includes a CorDapp DSL that allows the developer to supply metadata for the CorDapp + // required by Corda. + id 'net.corda.plugins.cordapp-cpb2' + id 'org.jetbrains.kotlin.jvm' + id 'maven-publish' +} + +// Declare dependencies for the modules we will use. +// A cordaProvided declaration is required for anything that we use that the Corda API provides. +// This is required to allow us to build CorDapp modules as OSGi bundles that CPI and CPB files are built on. +dependencies { + constraints { + testImplementation('org.slf4j:slf4j-api') { + version { + // Corda cannot use SLF4J 2.x yet. + strictly '1.7.36' + } + } + } + // From other subprojects: + cordapp project(':contracts') + + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + + // Declare a "platform" so that we use the correct set of dependency versions for the version of the + // Corda API specified. + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + + // If using transistive dependencies this will provide most of Corda-API: + // cordaProvided 'net.corda:corda-application' + + // Alternatively we can explicitly specify all our Corda-API dependencies: + cordaProvided 'net.corda:corda-base' + cordaProvided 'net.corda:corda-application' + cordaProvided 'net.corda:corda-crypto' + cordaProvided 'net.corda:corda-membership' + // cordaProvided 'net.corda:corda-persistence' + cordaProvided 'net.corda:corda-serialization' + cordaProvided 'net.corda:corda-ledger-utxo' + cordaProvided 'net.corda:corda-ledger-consensual' + + // CorDapps that use the UTXO ledger must include at least one notary client plugin + cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" + + // The CorDapp uses the slf4j logging framework. Corda-API provides this so we need a 'cordaProvided' declaration. + cordaProvided 'org.slf4j:slf4j-api' + + // 3rd party libraries + // Required + testImplementation "org.slf4j:slf4j-simple:$slf4jVersion" + testImplementation "org.junit.jupiter:junit-jupiter:$junitVersion" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + + // Optional but used by example tests. + testImplementation "org.mockito:mockito-core:$mockitoVersion" + testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion" + testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion" + testImplementation "org.assertj:assertj-core:$assertjVersion" +} + +// The CordApp section. +// This is part of the DSL provided by the corda plugins to define metadata for our CorDapp. +// Each component of the CorDapp would get its own CorDapp section in the build.gradle file for the component’s +// subproject. +// This is required by the corda plugins to build the CorDapp. +cordapp { + // "targetPlatformVersion" and "minimumPlatformVersion" are intended to specify the preferred + // and earliest versions of the Corda platform that the CorDapp will run on respectively. + // Enforced versioning has not implemented yet so we need to pass in a dummy value for now. + // The platform version will correspond to and be roughly equivalent to the Corda API version. + targetPlatformVersion = platformVersion.toInteger() + minimumPlatformVersion = platformVersion.toInteger() + + // The cordapp section contains either a workflow or contract subsection depending on the type of component. + // Declares the type and metadata of the CPK (this CPB has one CPK). + workflow { + name workflowsModule + versionId 1 + licence cordappLicense + vendor cordappVendorName + } +} + +// Use the name of the workflow module as the name of the generated CPK and CPB. +archivesBaseName = cordapp.workflow.name.isPresent() ? cordapp.workflow.name.get() : workflowsModule + +publishing { + publications { + maven(MavenPublication) { + from components.cordapp + } + } +} \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/FinalizeIOUFlow.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/FinalizeIOUFlow.java new file mode 100644 index 0000000..10c14be --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/FinalizeIOUFlow.java @@ -0,0 +1,125 @@ +package com.r3.developers.samples.obligation.workflows; + +import com.r3.developers.samples.obligation.states.IOUState; +import net.corda.v5.application.flows.*; +import net.corda.v5.application.messaging.FlowMessaging; +import net.corda.v5.application.messaging.FlowSession; +import net.corda.v5.base.annotations.Suspendable; +import net.corda.v5.base.exceptions.CordaRuntimeException; +import net.corda.v5.base.types.MemberX500Name; +import net.corda.v5.ledger.utxo.UtxoLedgerService; +import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; +import net.corda.v5.ledger.utxo.transaction.UtxoTransactionValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class FinalizeIOUFlow { + private final static Logger log = LoggerFactory.getLogger(FinalizeIOUFlow.class); + + // @InitiatingFlow declares the protocol which will be used to link the initiator to the responder. + @InitiatingFlow(protocol = "finalize-iou-protocol") + public static class FinalizeIOU implements SubFlow { + + private final UtxoSignedTransaction signedTransaction; + private final List otherMembers; + + public FinalizeIOU(UtxoSignedTransaction signedTransaction, List otherMembers) { + this.signedTransaction = signedTransaction; + this.otherMembers = otherMembers; + } + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + public UtxoLedgerService ledgerService; + + @CordaInject + public FlowMessaging flowMessaging; + + @Override + @Suspendable + public String call() { + + log.info("FinalizeIOU.call() called"); + + // Initiates a session with the other Member. + //FlowSession session = flowMessaging.initiateFlow(otherMember); + + List sessionsList = new ArrayList<>(); + + for (MemberX500Name member: otherMembers) { + sessionsList.add(flowMessaging.initiateFlow(member)); + } + + // Calls the Corda provided finalise() function which gather signatures from the counterparty, + // notarises the transaction and persists the transaction to each party's vault. + // On success returns the id of the transaction created. + String result; + try { + + UtxoSignedTransaction finalizedSignedTransaction = ledgerService.finalize( + signedTransaction, + sessionsList + ).getTransaction(); + + result = finalizedSignedTransaction.getId().toString(); + log.info("Success! Response: " + result); + + } + // Soft fails the flow and returns the error message without throwing a flow exception. + catch (Exception e) { + log.warn("Finality failed", e); + result = "Finality failed, " + e.getMessage(); + } + // Returns the transaction id converted as a string + return result; + } + } + + @InitiatedBy(protocol = "finalize-iou-protocol") + public static class FinalizeIOUResponderFlow implements ResponderFlow { + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + public UtxoLedgerService utxoLedgerService; + + @Suspendable + @Override + public void call(FlowSession session) { + + log.info("FinalizeIOUResponderFlow.call() called"); + + try { + // Defines the lambda validator used in receiveFinality below. + UtxoTransactionValidator txValidator = ledgerTransaction -> { + + // Note, this exception will only be shown in the logs if Corda Logging is set to debug. + if(!(ledgerTransaction.getOutputContractStates().get(0).getClass().equals(IOUState.class))) + throw new CordaRuntimeException("Failed verification - transaction did not have exactly one output IOUState."); + + log.info("Verified the transaction - " + ledgerTransaction.getId()); + }; + + // Calls receiveFinality() function which provides the responder to the finalise() function + // in the Initiating Flow. Accepts a lambda validator containing the business logic to decide whether + // responder should sign the Transaction. + UtxoSignedTransaction finalizedSignedTransaction = utxoLedgerService.receiveFinality(session, txValidator).getTransaction(); + log.info("Finished responder flow - " + finalizedSignedTransaction.getId()); + log.info("HEREEE PPP- " + finalizedSignedTransaction.getOutputStateAndRefs().stream().map(it -> it.getRef().toString()).toString()); + log.warn(finalizedSignedTransaction.getOutputStateAndRefs().stream() + .map(stateAndRef -> stateAndRef.getRef().toString()) + .toList() + .toString()); + + } + // Soft fails the flow and log the exception. + catch(Exception e) + { + log.warn("Exceptionally finished responder flow", e); + } + } + } +} diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUIssueFlow.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUIssueFlow.java new file mode 100644 index 0000000..fad623e --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUIssueFlow.java @@ -0,0 +1,139 @@ +package com.r3.developers.samples.obligation.workflows; + +import com.r3.developers.samples.obligation.contracts.IOUContract; +import com.r3.developers.samples.obligation.states.IOUState; +import net.corda.v5.application.flows.ClientRequestBody; +import net.corda.v5.application.flows.ClientStartableFlow; +import net.corda.v5.application.flows.CordaInject; +import net.corda.v5.application.flows.FlowEngine; +import net.corda.v5.application.marshalling.JsonMarshallingService; +import net.corda.v5.application.membership.MemberLookup; +import net.corda.v5.base.annotations.Suspendable; +import net.corda.v5.base.exceptions.CordaRuntimeException; +import net.corda.v5.base.types.MemberX500Name; +import net.corda.v5.ledger.common.NotaryLookup; +import net.corda.v5.ledger.utxo.UtxoLedgerService; +import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; +import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder; +import net.corda.v5.membership.MemberInfo; +import net.corda.v5.membership.NotaryInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.PublicKey; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class IOUIssueFlow implements ClientStartableFlow { + private final static Logger log = LoggerFactory.getLogger(IOUIssueFlow.class); + + // Injects the JsonMarshallingService to read and populate JSON parameters. + @CordaInject + public JsonMarshallingService jsonMarshallingService; + + // Injects the MemberLookup to look up the VNode identities. + @CordaInject + public MemberLookup memberLookup; + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + public UtxoLedgerService ledgerService; + + // Injects the NotaryLookup to look up the notary identity. + @CordaInject + public NotaryLookup notaryLookup; + + // FlowEngine service is required to run SubFlows. + @CordaInject + public FlowEngine flowEngine; + + @Override + @Suspendable + public String call(ClientRequestBody requestBody) { + log.info("IOUIssueFlow.call() called"); + + try { + // Obtain the deserialized input arguments to the flow from the requestBody. + IOUIssueFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, IOUIssueFlowArgs.class); + + // Get MemberInfos for the Vnode running the flow and the otherMember. + MemberInfo myInfo = memberLookup.myInfo(); + MemberInfo lenderInfo = requireNonNull( + memberLookup.lookup(MemberX500Name.parse(flowArgs.getLender())), + "MemberLookup can't find otherMember specified in flow arguments." + ); + log.info("PASS 0"); + // Create the IOUState from the input arguments and member information. + IOUState iou = new IOUState( + Integer.parseInt(flowArgs.getAmount()), + lenderInfo.getName(), + myInfo.getName(), + Arrays.asList(myInfo.getLedgerKeys().get(0), lenderInfo.getLedgerKeys().get(0)) + ); + log.info("PASS 1"); + // Obtain the Notary name and public key. + NotaryInfo notary = requireNonNull( + notaryLookup.lookup(MemberX500Name.parse("CN=NotaryService, OU=Test Dept, O=R3, L=London, C=GB")), + "NotaryLookup can't find notary specified in flow arguments." + ); + + log.info("PASS 2"); + PublicKey notaryKey = notary.getPublicKey();; + for(MemberInfo memberInfo: memberLookup.lookup()){ + if(!memberInfo.getLedgerKeys().isEmpty()) { + if (Objects.equals( + memberInfo.getMemberProvidedContext().get("corda.notary.service.name"), + notary.getName().toString())) { + notaryKey = memberInfo.getLedgerKeys().get(0); + break; + } + } + } + log.info("PASS 3"); + // Note, in Java CorDapps only unchecked RuntimeExceptions can be thrown not + // declared checked exceptions as this changes the method signature and breaks override. + if(notaryKey == null) { + throw new CordaRuntimeException("No notary PublicKey found"); + + } + log.info("PASS 4"); + // Use UTXOTransactionBuilder to build up the draft transaction. + UtxoTransactionBuilder txBuilder = ledgerService.createTransactionBuilder() + .setNotary(notary.getName()) + .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) + .addOutputState(iou) + .addCommand(new IOUContract.Issue()) + .addSignatories(iou.getParticipants()); + log.info("PASS 5"); + // Convert the transaction builder to a UTXOSignedTransaction and sign with this Vnode's first Ledger key. + // Note, toSignedTransaction() is currently a placeholder method, hence being marked as deprecated. + @SuppressWarnings("DEPRECATION") + UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(); + + // Call FinalizeIOUSubFlow which will finalise the transaction. + // If successful the flow will return a String of the created transaction id, + // if not successful it will return an error message. + return flowEngine.subFlow(new FinalizeIOUFlow.FinalizeIOU(signedTransaction, Arrays.asList(lenderInfo.getName()))); + } + // Catch any exceptions, log them and rethrow the exception. + catch (Exception e) { + log.warn("Failed to process utxo flow for request body " + requestBody + " because: " + e.getMessage()); + throw new CordaRuntimeException(e.getMessage()); + } + } +} +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "createiou-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.IOUIssueFlow", + "requestBody": { + "amount":"20", + "lender":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB" + } +} + */ diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUIssueFlowArgs.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUIssueFlowArgs.java new file mode 100644 index 0000000..4b49bdb --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUIssueFlowArgs.java @@ -0,0 +1,23 @@ +package com.r3.developers.samples.obligation.workflows; + +// A class to hold the deserialized arguments required to start the flow. +public class IOUIssueFlowArgs { + private String amount; + private String lender; + + public IOUIssueFlowArgs() { + } + + public IOUIssueFlowArgs(String amount, String lender) { + this.amount = amount; + this.lender = lender; + } + + public String getAmount() { + return amount; + } + + public String getLender() { + return lender; + } +} diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUSettleFlow.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUSettleFlow.java new file mode 100644 index 0000000..0e18e85 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUSettleFlow.java @@ -0,0 +1,127 @@ +package com.r3.developers.samples.obligation.workflows; + + +import com.r3.developers.samples.obligation.contracts.IOUContract; +import com.r3.developers.samples.obligation.states.IOUState; +import net.corda.v5.application.flows.ClientRequestBody; +import net.corda.v5.application.flows.ClientStartableFlow; +import net.corda.v5.application.flows.CordaInject; +import net.corda.v5.application.flows.FlowEngine; + +import net.corda.v5.application.marshalling.JsonMarshallingService; +import net.corda.v5.application.membership.MemberLookup; +import net.corda.v5.base.annotations.Suspendable; +import net.corda.v5.base.exceptions.CordaRuntimeException; +import net.corda.v5.base.types.MemberX500Name; +import net.corda.v5.ledger.utxo.StateAndRef; +import net.corda.v5.ledger.utxo.UtxoLedgerService; +import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; +import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder; +import net.corda.v5.membership.MemberInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; + +public class IOUSettleFlow implements ClientStartableFlow { + + private final static Logger log = LoggerFactory.getLogger(IOUSettleFlow.class); + + // Injects the JsonMarshallingService to read and populate JSON parameters. + @CordaInject + public JsonMarshallingService jsonMarshallingService; + + // Injects the MemberLookup to look up the VNode identities. + @CordaInject + public MemberLookup memberLookup; + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + public UtxoLedgerService ledgerService; + + // FlowEngine service is required to run SubFlows. + @CordaInject + public FlowEngine flowEngine; + + @Override + @Suspendable + public String call(ClientRequestBody requestBody) { + log.info("IOUSettleFlow.call() called"); + + try { + // Obtain the deserialized input arguments to the flow from the requestBody. + IOUSettleFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, IOUSettleFlowArgs.class); + + // Get flow args from the input JSON + UUID iouID = flowArgs.getIouID(); + int amountSettle = Integer.parseInt(flowArgs.getAmountSettle()); + + //query the IOU input + List> iouStateAndRefs = ledgerService.findUnconsumedStatesByExactType(IOUState.class,100, Instant.now()).getResults(); + List> iouStateAndRefsWithId = iouStateAndRefs.stream() + .filter(sar -> sar.getState().getContractState().getLinearId().equals(iouID)).collect(toList()); + + if (iouStateAndRefsWithId.size() != 1) throw new CordaRuntimeException("Multiple or zero IOU states with id " + iouID + " found"); + StateAndRef iouStateAndRef = iouStateAndRefsWithId.get(0); + IOUState iouInput = iouStateAndRef.getState().getContractState(); + + //flow logic checks + MemberInfo myInfo = memberLookup.myInfo(); + if (!(myInfo.getName().equals(iouInput.getBorrower()))) throw new CordaRuntimeException("Only IOU borrower can settle the IOU."); + MemberInfo lenderInfo = requireNonNull( + memberLookup.lookup(iouInput.getLender()), + "MemberLookup can't find otherMember specified in flow arguments." + ); + + // Create the IOUState from the input arguments and member information. + IOUState iouOutput = iouInput.pay(amountSettle); + + //get notary from input + MemberX500Name notary = iouStateAndRef.getState().getNotaryName(); + + // Use UTXOTransactionBuilder to build up the draft transaction. + UtxoTransactionBuilder txBuilder = ledgerService.createTransactionBuilder() + .setNotary(notary) + .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) + .addInputState(iouStateAndRef.getRef()) + .addOutputState(iouOutput) + .addCommand(new IOUContract.Settle()) + .addSignatories(iouOutput.getParticipants()); + + // Convert the transaction builder to a UTXOSignedTransaction and sign with this Vnode's first Ledger key. + // Note, toSignedTransaction() is currently a placeholder method, hence being marked as deprecated. + UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(); + + // Call FinalizeIOUSubFlow which will finalise the transaction. + // If successful the flow will return a String of the created transaction id, + // if not successful it will return an error message. + return flowEngine.subFlow(new FinalizeIOUFlow.FinalizeIOU(signedTransaction, Arrays.asList(lenderInfo.getName()))); + } + // Catch any exceptions, log them and rethrow the exception. + catch (Exception e) { + log.warn("Failed to process utxo flow for request body " + requestBody + " because: " + e.getMessage()); + throw new CordaRuntimeException(e.getMessage()); + } + } +} +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "settleiou-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.IOUSettleFlow", + "requestBody": { + "amountSettle":"10", + "iouID":" ** fill in id **" + } +} +1ac69d82-804b-487b-9178-ea527d0e4b80 +*/ + + diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUSettleFlowArgs.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUSettleFlowArgs.java new file mode 100644 index 0000000..d2b605e --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUSettleFlowArgs.java @@ -0,0 +1,25 @@ +package com.r3.developers.samples.obligation.workflows; + +import java.util.UUID; + +// A class to hold the deserialized arguments required to start the flow. +public class IOUSettleFlowArgs { + private String amountSettle; + private UUID iouID; + + public IOUSettleFlowArgs() { + } + + public IOUSettleFlowArgs(String amountSettle, UUID iouID) { + this.amountSettle = amountSettle; + this.iouID = iouID; + } + + public String getAmountSettle() { + return amountSettle; + } + + public UUID getIouID() { + return iouID; + } +} \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUTransferFlow.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUTransferFlow.java new file mode 100644 index 0000000..939bba9 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUTransferFlow.java @@ -0,0 +1,127 @@ +package com.r3.developers.samples.obligation.workflows; + + +import com.r3.developers.samples.obligation.contracts.IOUContract; +import com.r3.developers.samples.obligation.states.IOUState; +import net.corda.v5.application.flows.ClientRequestBody; +import net.corda.v5.application.flows.ClientStartableFlow; +import net.corda.v5.application.flows.CordaInject; +import net.corda.v5.application.flows.FlowEngine; +import net.corda.v5.application.marshalling.JsonMarshallingService; +import net.corda.v5.application.membership.MemberLookup; +import net.corda.v5.base.annotations.Suspendable; +import net.corda.v5.base.exceptions.CordaRuntimeException; +import net.corda.v5.base.types.MemberX500Name; +import net.corda.v5.ledger.utxo.StateAndRef; +import net.corda.v5.ledger.utxo.UtxoLedgerService; +import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction; +import net.corda.v5.ledger.utxo.transaction.UtxoTransactionBuilder; +import net.corda.v5.membership.MemberInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; + +public class IOUTransferFlow implements ClientStartableFlow { + + private final static Logger log = LoggerFactory.getLogger(IOUTransferFlow.class); + + // Injects the JsonMarshallingService to read and populate JSON parameters. + @CordaInject + public JsonMarshallingService jsonMarshallingService; + + // Injects the MemberLookup to look up the VNode identities. + @CordaInject + public MemberLookup memberLookup; + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + public UtxoLedgerService ledgerService; + + // FlowEngine service is required to run SubFlows. + @CordaInject + public FlowEngine flowEngine; + + @Override + @Suspendable + public String call(ClientRequestBody requestBody) { + log.info("IOUTransferFlow.call() called"); + + try { + // Obtain the deserialized input arguments to the flow from the requestBody. + IOUTransferFlowArgs flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, IOUTransferFlowArgs.class); + + // Get flow args from the input JSON + UUID iouID = flowArgs.getIouID(); + + //query the IOU input + List> iouStateAndRefs = ledgerService.findUnconsumedStatesByExactType(IOUState.class,100, Instant.now()).getResults(); + List> iouStateAndRefsWithId = iouStateAndRefs.stream() + .filter(sar -> sar.getState().getContractState().getLinearId().equals(iouID)).collect(toList()); + if (iouStateAndRefsWithId.size() != 1) throw new CordaRuntimeException("Multiple or zero IOU states with id " + iouID + " found"); + StateAndRef iouStateAndRef = iouStateAndRefsWithId.get(0); + IOUState iouInput = iouStateAndRef.getState().getContractState(); + + //flow logic checks + MemberInfo myInfo = memberLookup.myInfo(); + if (!(myInfo.getName().equals(iouInput.getLender()))) throw new CordaRuntimeException("Only IOU lender can transfer the IOU."); + + // Get MemberInfos for the Vnode running the flow and the otherMember. + MemberInfo newLenderInfo = requireNonNull( + memberLookup.lookup(MemberX500Name.parse(flowArgs.getNewLender())), + "MemberLookup can't find otherMember specified in flow arguments." + ); + MemberInfo borrower = requireNonNull( + memberLookup.lookup(iouInput.getBorrower()), + "MemberLookup can't find otherMember specified in flow arguments." + ); + + // Create the IOUState from the input arguments and member information. + IOUState iouOutput = iouInput.withNewLender(newLenderInfo.getName(), Arrays.asList(borrower.getLedgerKeys().get(0), newLenderInfo.getLedgerKeys().get(0))); + + //get notary from input + MemberX500Name notary = iouStateAndRef.getState().getNotaryName(); + + // Use UTXOTransactionBuilder to build up the draft transaction. + UtxoTransactionBuilder txBuilder = ledgerService.createTransactionBuilder() + .setNotary(notary) + .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) + .addInputState(iouStateAndRef.getRef()) + .addOutputState(iouOutput) + .addCommand(new IOUContract.Transfer()) + .addSignatories(Arrays.asList(borrower.getLedgerKeys().get(0), newLenderInfo.getLedgerKeys().get(0),myInfo.getLedgerKeys().get(0))); + + // Convert the transaction builder to a UTXOSignedTransaction and sign with this Vnode's first Ledger key. + // Note, toSignedTransaction() is currently a placeholder method, hence being marked as deprecated. + UtxoSignedTransaction signedTransaction = txBuilder.toSignedTransaction(); + + // Call FinalizeIOUSubFlow which will finalise the transaction. + // If successful the flow will return a String of the created transaction id, + // if not successful it will return an error message. + return flowEngine.subFlow(new FinalizeIOUFlow.FinalizeIOU(signedTransaction, Arrays.asList(borrower.getName(),newLenderInfo.getName()))); + } + // Catch any exceptions, log them and rethrow the exception. + catch (Exception e) { + log.warn("Failed to process utxo flow for request body " + requestBody + " because: " + e.getMessage()); + throw new CordaRuntimeException(e.getMessage()); + } + } +} +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "transferiou-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.IOUTransferFlow", + "requestBody": { + "newLender":"CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB", + "iouID":"1ac69d82-804b-487b-9178-ea527d0e4b80" + } +} + */ \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUTransferFlowArgs.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUTransferFlowArgs.java new file mode 100644 index 0000000..d0e5b03 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/IOUTransferFlowArgs.java @@ -0,0 +1,26 @@ +package com.r3.developers.samples.obligation.workflows; + +import java.util.UUID; + +// A class to hold the deserialized arguments required to start the flow. +public class IOUTransferFlowArgs { + + private String newLender; + private UUID iouID; + + public IOUTransferFlowArgs() { + } + + public IOUTransferFlowArgs(String newLender, UUID iouID) { + this.newLender = newLender; + this.iouID = iouID; + } + + public String getNewLender() { + return newLender; + } + + public UUID getIouID() { + return iouID; + } +} diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ListIOUFlow.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ListIOUFlow.java new file mode 100644 index 0000000..f7498bd --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ListIOUFlow.java @@ -0,0 +1,59 @@ +package com.r3.developers.samples.obligation.workflows; + +import com.r3.developers.samples.obligation.states.IOUState; +import net.corda.v5.application.flows.ClientRequestBody; +import net.corda.v5.application.flows.ClientStartableFlow; +import net.corda.v5.application.flows.CordaInject; +import net.corda.v5.application.marshalling.JsonMarshallingService; +import net.corda.v5.base.annotations.Suspendable; +import net.corda.v5.ledger.utxo.StateAndRef; +import net.corda.v5.ledger.utxo.UtxoLedgerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Instant; +import java.util.List; +import java.util.stream.Collectors; + +public class ListIOUFlow implements ClientStartableFlow { + + private final static Logger log = LoggerFactory.getLogger(ListIOUFlow.class); + + // Injects the JsonMarshallingService to read and populate JSON parameters. + @CordaInject + public JsonMarshallingService jsonMarshallingService; + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + public UtxoLedgerService utxoLedgerService; + + @Suspendable + @Override + public String call(ClientRequestBody requestBody) { + + log.info("ListIOUFlow.call() called"); + + // Queries the VNode's vault for unconsumed states and converts the result to a serializable DTO. + List> states = utxoLedgerService.findUnconsumedStatesByExactType(IOUState.class,100, Instant.now()).getResults(); + List results = states.stream().map(stateAndRef -> + new ListIOUFlowResults( + stateAndRef.getState().getContractState().getLinearId(), + stateAndRef.getState().getContractState().getAmount(), + stateAndRef.getState().getContractState().getBorrower().toString(), + stateAndRef.getState().getContractState().getLender().toString(), + stateAndRef.getState().getContractState().getPaid() + ) + ).collect(Collectors.toList()); + + // Uses the JsonMarshallingService's format() function to serialize the DTO to Json. + return jsonMarshallingService.format(results); + } +} +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "list-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.ListIOUFlow", + "requestBody": {} +} +*/ \ No newline at end of file diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ListIOUFlowResults.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ListIOUFlowResults.java new file mode 100644 index 0000000..170f44b --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ListIOUFlowResults.java @@ -0,0 +1,44 @@ +package com.r3.developers.samples.obligation.workflows; + +import java.util.UUID; + +// A class to hold the deserialized arguments required to start the flow. +public class ListIOUFlowResults { + + private UUID id; + private int amount; + private String borrower; + private String lender; + private int paid; + + public ListIOUFlowResults() { + } + + public ListIOUFlowResults(UUID id, int amount, String borrower, String lender, int paid) { + this.id = id; + this.amount = amount; + this.borrower = borrower; + this.lender = lender; + this.paid = paid; + } + + public UUID getId() { + return id; + } + + public int getAmount() { + return amount; + } + + public String getBorrower() { + return borrower; + } + + public String getLender() { + return lender; + } + + public int getPaid() { + return paid; + } +} diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ReceiveTransactionFlow.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ReceiveTransactionFlow.java new file mode 100644 index 0000000..fbf7303 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/ReceiveTransactionFlow.java @@ -0,0 +1,26 @@ +package com.r3.developers.samples.obligation.workflows; + +import net.corda.v5.application.flows.CordaInject; +import net.corda.v5.application.flows.InitiatedBy; +import net.corda.v5.application.flows.ResponderFlow; +import net.corda.v5.application.messaging.FlowSession; +import net.corda.v5.base.annotations.Suspendable; +import net.corda.v5.ledger.utxo.UtxoLedgerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@InitiatedBy(protocol = "utxo-transaction-transmission-protocol") +public class ReceiveTransactionFlow implements ResponderFlow { + private static final Logger log = LoggerFactory.getLogger(ReceiveTransactionFlow.class); + + @CordaInject + private UtxoLedgerService utxoLedgerService; + + @Suspendable + @Override + public void call(FlowSession session) { + // Receive the transaction and log its details. + var transaction = utxoLedgerService.receiveTransaction(session); + log.info("Received transaction - " + transaction.getId()); + } +} diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/sendAndReceiveTransaction.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/sendAndReceiveTransaction.java new file mode 100644 index 0000000..fc76ecb --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/sendAndReceiveTransaction.java @@ -0,0 +1,99 @@ +package com.r3.developers.samples.obligation.workflows; + +import net.corda.v5.application.crypto.DigestService; +import net.corda.v5.application.flows.*; +import net.corda.v5.application.marshalling.JsonMarshallingService; +import net.corda.v5.application.membership.MemberLookup; +import net.corda.v5.application.messaging.FlowMessaging; +import net.corda.v5.application.messaging.FlowSession; +import net.corda.v5.base.annotations.Suspendable; +import net.corda.v5.base.types.MemberX500Name; +import net.corda.v5.crypto.SecureHash; +import net.corda.v5.ledger.utxo.StateRef; +import net.corda.v5.ledger.utxo.UtxoLedgerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.stream.Collectors; + +@InitiatingFlow(protocol = "utxo-transaction-transmission-protocol") +public class sendAndReceiveTransaction implements ClientStartableFlow { + + @CordaInject + private FlowMessaging flowMessaging; + + @CordaInject + private UtxoLedgerService utxoLedgerService; + + @CordaInject + private JsonMarshallingService jsonMarshallingService; + + @CordaInject + private MemberLookup memberLookup; + + @CordaInject + private DigestService digestService; + + private static final Logger log = LoggerFactory.getLogger(sendAndReceiveTransaction.class); + + @Suspendable + @Override + public String call(ClientRequestBody requestBody) { + sendAndRecieveTransactionArgs request = requestBody.getRequestBodyAs(jsonMarshallingService, sendAndRecieveTransactionArgs.class); + + // Parse the state reference to obtain the transaction ID. + SecureHash transactionId = StateRef.parse(request.getStateRef() + ":0", digestService).getTransactionId(); + + // Retrieve the signed transaction from the ledger. + var transaction = requireNotNull(utxoLedgerService.findSignedTransaction(transactionId), + "Transaction is not found or verified."); + + // Map the X500 names in the request to Member objects, ensuring each member exists. + var members = request.getMembers().stream() + .map(x500 -> requireNotNull(memberLookup.lookup(MemberX500Name.parse(x500)), + "Member " + x500 + " does not exist in the membership group")) + .collect(Collectors.toList()); + + // Initialize the sessions with the memebers that will be used to send the transaction. + var sessions = members.stream() + .map(member -> flowMessaging.initiateFlow(member.getName())) + .collect(Collectors.toList()); + + // Send the transaction with or without backchain depending on the request. + try { + if (request.isForceBackchain()) { + utxoLedgerService.sendTransactionWithBackchain(transaction, sessions); + } else { + utxoLedgerService.sendTransaction(transaction, sessions); + } + } catch (Exception e) { + // Log and rethrow any exceptions encountered during transaction sending. + log.warn("Sending transaction for " + transactionId + " failed.", e); + throw e; + } + + // Format and log the successful transaction response. + String response = jsonMarshallingService.format(transactionId.toString()); + log.info("SendTransaction is successful. Response: " + response); + return response; + } + + private T requireNotNull(T obj, String message) { + if (obj == null) throw new IllegalArgumentException(message); + return obj; + } +} + +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "sendAndRecieve-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.sendAndReceiveTransaction", + "requestBody": { + "stateRef": "STATE REF ID HERE", + "members": ["CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB"], + "forceBackchain": "false" + } +} +*/ diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/sendAndRecieveTransactionArgs.java b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/sendAndRecieveTransactionArgs.java new file mode 100644 index 0000000..d1cfd98 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/main/java/com/r3/developers/samples/obligation/workflows/sendAndRecieveTransactionArgs.java @@ -0,0 +1,33 @@ +package com.r3.developers.samples.obligation.workflows; + +import java.util.List; + +public class sendAndRecieveTransactionArgs { + + + private String stateRef; + private List members; + private boolean forceBackchain; + + public sendAndRecieveTransactionArgs(){ + + } + public sendAndRecieveTransactionArgs(String stateRef, List members, boolean forceBackchain) { + this.stateRef = stateRef; + this.members = members; + this.forceBackchain = forceBackchain; + } + + public String getStateRef() { + return stateRef; + } + + public List getMembers() { + return members; + } + + public boolean isForceBackchain() { + return forceBackchain; + } + +} diff --git a/java-samples/sendAndRecieveTransaction/workflows/src/test/java/com/r3/developers/samples/obligation/MyFirstFlowTest.java b/java-samples/sendAndRecieveTransaction/workflows/src/test/java/com/r3/developers/samples/obligation/MyFirstFlowTest.java new file mode 100644 index 0000000..f1efee1 --- /dev/null +++ b/java-samples/sendAndRecieveTransaction/workflows/src/test/java/com/r3/developers/samples/obligation/MyFirstFlowTest.java @@ -0,0 +1,41 @@ +package com.r3.developers.samples.obligation; +// +//import net.corda.simulator.RequestData; +//import net.corda.simulator.SimulatedVirtualNode; +//import net.corda.simulator.Simulator; +//import net.corda.v5.base.types.MemberX500Name; +//import org.junit.jupiter.api.Test; +// +//class MyFirstFlowTest { +// // Names picked to match the corda network in config/dev-net.json +// private MemberX500Name aliceX500 = MemberX500Name.parse("CN=Alice, OU=Test Dept, O=R3, L=London, C=GB"); +// private MemberX500Name bobX500 = MemberX500Name.parse("CN=Bob, OU=Test Dept, O=R3, L=London, C=GB"); +// +// @Test +// @SuppressWarnings("unchecked") +// public void test_that_MyFirstFLow_returns_correct_message() { +// // Instantiate an instance of the simulator. +// Simulator simulator = new Simulator(); +// +// // Create Alice's and Bob's virtual nodes, including the classes of the flows which will be registered on each node. +// // Don't assign Bob's virtual node to a value. You don't need it for this particular test. +// SimulatedVirtualNode aliceVN = simulator.createVirtualNode(aliceX500, MyFirstFlow.class); +// simulator.createVirtualNode(bobX500, MyFirstFlowResponder.class); +// +// // Create an instance of the MyFirstFlowStartArgs which contains the request arguments for starting the flow. +// MyFirstFlowStartArgs myFirstFlowStartArgs = new MyFirstFlowStartArgs(bobX500); +// +// // Create a requestData object. +// RequestData requestData = RequestData.Companion.create( +// "request no 1", // A unique reference for the instance of the flow request. +// MyFirstFlow.class, // The name of the flow class which is to be started. +// myFirstFlowStartArgs // The object which contains the start arguments of the flow. +// ); +// +// // Call the flow on Alice's virtual node and capture the response. +// String flowResponse = aliceVN.callFlow(requestData); +// +// // Check that the flow has returned the expected string. +// assert(flowResponse.equals("Hello Alice, best wishes from Bob")); +// } +//} diff --git a/kotlin-samples/sendAndRecieveTransaction/.ci/Jenkinsfile b/kotlin-samples/sendAndRecieveTransaction/.ci/Jenkinsfile new file mode 100644 index 0000000..2108886 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/.ci/Jenkinsfile @@ -0,0 +1,11 @@ +@Library('corda-shared-build-pipeline-steps@5.0') _ + +cordaPipeline( + nexusAppId: 'com.corda.CSDE-kotlin.5.0', + publishRepoPrefix: '', + slimBuild: true, + runUnitTests: false, + dedicatedJobForSnykDelta: false, + slackChannel: '#corda-corda5-dev-ex-build-notifications', + gitHubComments: false + ) diff --git a/kotlin-samples/sendAndRecieveTransaction/.ci/nightly/JenkinsfileSnykScan b/kotlin-samples/sendAndRecieveTransaction/.ci/nightly/JenkinsfileSnykScan new file mode 100644 index 0000000..fc2b1ee --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/.ci/nightly/JenkinsfileSnykScan @@ -0,0 +1,6 @@ +@Library('corda-shared-build-pipeline-steps@5.0') _ + +cordaSnykScanPipeline ( + snykTokenId: 'r3-snyk-corda5', + snykAdditionalCommands: "--all-sub-projects -d" +) diff --git a/kotlin-samples/sendAndRecieveTransaction/.gitignore b/kotlin-samples/sendAndRecieveTransaction/.gitignore new file mode 100644 index 0000000..d2879c4 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/.gitignore @@ -0,0 +1,86 @@ + +# Eclipse, ctags, Mac metadata, log files +.classpath +.project +.settings +tags +.DS_Store +*.log +*.orig + +# Created by .ignore support plugin (hsz.mobi) + +.gradle +local.properties +.gradletasknamecache + +# General build files +**/build/* + +lib/quasar.jar + +**/logs/* + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/*.xml +.idea/.name +.idea/copyright +.idea/inspectionProfiles +.idea/libraries +.idea/shelf +.idea/dataSources +.idea/markdown-navigator +.idea/runConfigurations +.idea/dictionaries + + +# Include the -parameters compiler option by default in IntelliJ required for serialization. +!.idea/codeStyleSettings.xml + + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +**/out/ +/classes/ + + + +# vim +*.swp +*.swn +*.swo + + + +# Directory generated during Resolve and TestOSGi gradle tasks +bnd/ + +# Ignore Gradle build output directory +build +/.idea/codeStyles/codeStyleConfig.xml +/.idea/codeStyles/Project.xml + + + +# Ignore Visual studio directory +bin/ + + + +*.cpi +*.cpb +*.cpk +workspace/** + +# ingore temporary data files +*.dat \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/.run/runConfigurations/DebugCorDapp.run.xml b/kotlin-samples/sendAndRecieveTransaction/.run/runConfigurations/DebugCorDapp.run.xml new file mode 100644 index 0000000..1d8da82 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/.run/runConfigurations/DebugCorDapp.run.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/.snyk b/kotlin-samples/sendAndRecieveTransaction/.snyk new file mode 100644 index 0000000..a4b3e0e --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/.snyk @@ -0,0 +1,14 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.25.0 +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JAVA-ORGJETBRAINSKOTLIN-2393744: + - '*': + reason: >- + This vulnerability relates to information exposure via creation of + temporary files (via Kotlin functions) with insecure permissions. + Corda does not use any of the vulnerable functions so it is not + susceptible to this vulnerability + expires: 2023-10-19T17:08:41.029Z + created: 2023-02-02T17:08:41.032Z +patch: {} \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/Dockerfile b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/Dockerfile new file mode 100644 index 0000000..016bc21 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/Dockerfile @@ -0,0 +1,5 @@ +FROM python +WORKDIR /app +COPY . /app +RUN pip install -r requirements.txt +CMD ["python3", "app.py"] \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/README.md b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/README.md new file mode 100644 index 0000000..fffff29 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/README.md @@ -0,0 +1,84 @@ +# Corda 5 CorDapp Flow Management Tool + + +This user guide provides step-by-step instructions on using the Corda 5 flow management tool. This article will help you learn how to connect the running corDapp, make flow calls, configure flow queries, and retrieve results. + +## Prerequisites +* Install and run Python and Flask framework. link. + +* Prepare your local Corda 5 environment. (By default, the Flow Management Tool is looking to connect to https://localhost:8888/api/v5_2/swagger#/ with Login: Admin and Password: Admin.) + +* Clong the Flow Management Tool repository. FlowManagementUI: main + +## Set Up + +1. Assuming your local Corda 5 environment is populated and the swagger endpoint is at: https://localhost:8888/api/v5_2/swagger#/ + +2. Navigate to where you downloaded the Corda 5 Flow Management Tool + +3. To run the framework + * Navigate to the file name using cd command. + * use the python app.py command to run it. + ![image](https://github.com/parisyup/FlowManagementUI/assets/51169685/f0c3bf59-8180-48a0-91cc-80f2d260e530) + + * Later on, click on the IP Address which will open the Interface: + +![image(4)](https://github.com/parisyup/FlowManagementUI/assets/66366646/8d88e37c-edbb-4d6d-8bcd-d773e818a106) + + +The Flow Management Tool should be automatically connected with the CorDapp running locally from your CSDE. You can test the connection by click on the dropdown list at the Flow Initiator section. You should be able to see the vNodes of your started CorDapp from CSDE. + +![image](https://github.com/parisyup/FlowManagementUI/assets/51169685/5a2356f2-cd14-489c-abd0-4afe0bf0d251) + +## Set Up With Docker + +1- Open up Command Prompt + +2- Navigate to the application folder using the CD commands + +3- Ensure that Docker application is open and build the image using the following command: +`docker build -t your-image-name .` + +Make sure to include the dot at the end of the command + +the `your-image-name` at the end of the command can be whatever you like but make sure to use the same name in the next step + +4- Run the docker image using the following command: +`docker run --rm -it --expose 8888 -p 5000:5000 your-image-name` + +5- You can access the website by using https://localhost:5000 or https://127.0.0.1:5000 + +## Using the Flow Management Tool + +### Selecting the Flow Initiator + +As the first step of using the Flow Management Tool, you would need to select the Flow Initiator. The Flow Initiator indicates which vNode will be triggering the flow. If you wish to have Alice to run a transaction to Bob, select the X500Name of Alice. The selected vNode’s shortHash (Corda 5 Network participant identifier) will also be shown below the dropdown list to signify your selection. + +### Function 1: To Make a Flow Call + +1. Click on "Flow Call" tab in the application. +2. Paste the your JSON format request body into the request input box. +3. Click Post button to trigger the call. + +![image(5)](https://github.com/parisyup/FlowManagementUI/assets/66366646/c65195a6-0a70-4354-804e-37884f657746) + + +### Function 2: To Configure Flow Query + +1. Click on the “Flow Query” tab. +2. Choose whether to query a single flow or all flows at the selected Flow Initiator. +3. If you choose to query all of the flows, select “Query All Flows“ then click “Get“. + +![image](https://github.com/parisyup/FlowManagementUI/assets/51169685/0482cfa4-7ee1-42f2-8786-2d8ad80b2936) +4. If you choose to query a single flow, select “Query Single Flow“, please add the ClientID in specified filed. +5. Click on “Get” to retrieve the result. + +![image(6)](https://github.com/parisyup/FlowManagementUI/assets/66366646/13e979b0-f76e-4f2c-9d55-81be8880890b) + +If you have any suggestions or questions, feel free to give us your feedback through Github for a better experience in the future! + +## Conclusion +In summary, our project introduces a specialized flow management layer on top of Swagger for Corda developers. We understand the challenges developers face in testing Corda applications due to the complexity of commands, our solution focuses on simplifying the process. + +Our all-in-one flow management system provides developers with a unified platform, streamlining development and enhancing efficiency. A key feature allows developers to run flows directly from an externally developed website and monitor their status in real-time, offering a user-friendly and practical solution for Corda developers. Overall, our project aims to make Corda development more accessible and tailored to the specific needs of flow management. + diff --git a/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/app.py b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/app.py new file mode 100644 index 0000000..5d3e3f6 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/app.py @@ -0,0 +1,12 @@ +from flask import Flask +from flask import render_template +app = Flask(__name__) + + +@app.route('/') +def home(): # put application's code here + return render_template("index.html") + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') + diff --git a/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/requirements.txt b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/requirements.txt new file mode 100644 index 0000000..8ab6294 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/requirements.txt @@ -0,0 +1 @@ +flask \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/static/Scripts/script.js b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/static/Scripts/script.js new file mode 100644 index 0000000..8fd4ec0 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/static/Scripts/script.js @@ -0,0 +1,322 @@ +// This script contains functions for making API requests, handling data, and updating the UI. + +// Variable to store the selected X500Name from the dropdown +let selectedX500Name; + +// Variable to indicate whether data is currently being loaded from the pull request +let loading = false; + +// Function to make a GET request to an external API and return the data +function getData() { + // Replace the URL with the actual API endpoint + return fetch('https://jsonplaceholder.typicode.com/todos/1') + .then(response => response.json()) + .then(data => { + console.log('API Result:', data); + + // Return specific data fields + return { + id: data.id, + title: data.title + }; + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to get the data and display it on the page +function getDataAndDisplay() { + getData() + .then(result => { + // Update the result div with the data + document.getElementById('result').innerHTML = ` +

ID: ${result.id}

+

Title: ${result.title}

+ `; + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to make a GET request to retrieve CPI data +function getCPI() { + const url = 'https://localhost:8888/api/v1/cpi'; + // Perform the GET request with authorization headers + return fetch(url, { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=' + } + }) + .then(response => response.json()) + .then(data => { + console.log('API Result:', data); + // Further processing of the data can be done here + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to get all virtual nodes and populate a dropdown with the data +function getAllVirtualNodes() { + const url = 'https://localhost:8888/api/v1/virtualnode'; + + return fetch(url, { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=' + } + }) + .then(response => response.json()) + .then(data => { + // Extract virtualNodes from the API response + const virtualNodes = data.virtualNodes; + console.log('API Result:', virtualNodes); + + // Process the data and populate the dropdown + if (Array.isArray(virtualNodes)) { + const dropdown = document.getElementById('itemDropdown'); + dropdown.innerHTML = ''; + dropdown.innerHTML += ''; + + virtualNodes.forEach(item => { + // Display each item on the console + console.log('Item X500Name:', item.holdingIdentity.x500Name); + console.log('Item ShortHash:', item.holdingIdentity.shortHash); + + // Create an option element and add it to the dropdown + const option = document.createElement('option'); + option.value = item.holdingIdentity.shortHash; + option.text = item.holdingIdentity.x500Name; + dropdown.appendChild(option); + }); + + // Add event listener to the dropdown to detect changes + dropdown.addEventListener('change', function () { + selectedX500Name = this.value; + console.log('Selected ShortHash:', selectedX500Name); + + // Call a function or update a variable based on the selected item + handleDropdownChange(selectedX500Name); + }); + + } else { + console.warn('API Result is not an array.'); + } + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Initialize the virtual nodes dropdown on page load +getAllVirtualNodes(); + +// function to handle dropdown change +function handleDropdownChange(selectedX500Name) { + console.log('Handling dropdown change for Item ID:', selectedX500Name); + getSelectedVNode(); + // Additional actions based on the selected item can be performed here +} + +// Function to get the selected virtual node and display it +function getSelectedVNode() { + const displayElement = document.getElementById('selectedX500Display'); + displayElement.textContent = `Selected X500: ${selectedX500Name}`; +} + +// Function to get all flow results based on the selected virtual node +function getAllFlowResult() { + const url = `https://localhost:8888/api/v1/flow/${selectedX500Name}`; + + return fetch(url, { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=' + } + }) + .then(response => response.json()) + .then(data => { + const flowStatusResponses = data.flowStatusResponses; + console.log('API Result:', flowStatusResponses); + + // Convert the JSON object to a string for display + const jsonString = JSON.stringify(flowStatusResponses, null, 2); + + // Display the JSON string in the result div + document.getElementById('idResutl').innerHTML = `
${jsonString}
`; + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to make a POST request with a request body +async function postCallFlow() { + let postBtn = $('#postBtn'); + const url = `https://localhost:8888/api/v1/flow/${selectedX500Name}`; + + // Change the button text to indicate loading + postBtn.html('Loading...'); + + try { + // Perform the POST request with the provided request body + const response = await fetch(url, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=', + 'Content-Type': 'application/json' + }, + body: `${document.getElementById('requestBody').value}` + }); + + // Parse the response as JSON + const data = await response.json(); + + if (!data.ok) { + console.log(data.status); + + // Check if the response status is not OK (2xx range) + if (data.status == "409" || data.status == "400") { + let flowStatusResponse = "title: " + data.title + "\nStatus: " + data.status; + + // Display additional details for 400 status + if (data.status == "400") { + flowStatusResponse += "\nDetails: \n Cause: " + data.details.cause + "\n Reason: " + data.details.reason; + } + + document.getElementById('queryResult').innerHTML = `
${flowStatusResponse}
`; + return; + } + } + + let msg = "null"; + let typ = "null"; + + // Construct a string with the flow status responses + let flowStatusResponses = "client request ID: " + data.clientRequestId + + "\nFlow Result " + data.flowResult + + "\nFlow Error Message: " + msg + + "\nflow error type: " + typ + + "\nFlow ID: " + data.flowId + + "\nFlow status: " + data.flowStatus + + "\nHolding identity short hash: " + data.holdingIdentityShortHash + + "\nTime stamp: " + data.timestamp; + + console.log('API Result:', flowStatusResponses); + + // Display the flow status responses in the result div + document.getElementById('queryResult').innerHTML = `
${flowStatusResponses}
`; + } catch (error) { + console.log('Error:', error); + } finally { + // Restore the button text after the operation is complete + postBtn.html('Post'); + } +} + +// Function to display an item on the page +function displayItemOnPage(item) { + const resultDiv = document.getElementById('queryResult'); + + // Create a new element to display the item + const itemElement = document.createElement('div'); + itemElement.innerHTML = ` +

QueryResult: ${item}

+ `; + + // Append the new element to the result div + resultDiv.appendChild(itemElement); +} + +// Function to open a specific tab by hiding/showing content +function openTab(tabName) { + // Hide all tab content + var tabContents = document.getElementsByClassName("tab-content"); + for (var i = 0; i < tabContents.length; i++) { + tabContents[i].style.display = "none"; + document.getElementById(tabContents[i].id + "-tab").style.backgroundColor = "rgb(52,152,219)"; + } + + // Show the selected tab content + var selectedTab = document.getElementById(tabName); + if (selectedTab) { + selectedTab.style.display = "block"; + document.getElementById(tabName + "-tab").style.backgroundColor = "rgb(173,216,230)"; + } +} + +// Function to display a flow for a specific virtual node +function oneFlow() { + const url = `https://localhost:8888/api/v1/flow/${selectedX500Name}/${document.getElementById('clientID').value}`; + + // Validate clientID input + if (document.getElementById('clientID').value == "") { + alert("Please input a clientId"); + return; + } + + // Perform a GET request to display a flow + return fetch(url, { + method: 'GET', + headers: { + 'Accept': 'application/json', + 'Authorization': 'Basic YWRtaW46YWRtaW4=' + } + }) + .then(response => response.json()) + .then(data => { + let msg = "null"; + let typ = "null"; + + // Check if the flow status is "FAILED" and extract error details + if (data.flowStatus == "FAILED") { + msg = data.flowError.message; + typ = data.flowError.type; + } + + // Construct a string with the flow status responses + const flowStatusResponses = "client request ID: " + data.clientRequestId + + "\nFlow Result " + data.flowResult + + "\nFlow Error Message: " + msg + + "\nflow error type: " + typ + + "\nFlow ID: " + data.flowId + + "\nFlow status: " + data.flowStatus + + "\nHolding identity short hash: " + data.holdingIdentityShortHash + + "\nTime stamp: " + data.timestamp; + + console.log('API Result:', flowStatusResponses); + + // Display the flow status responses in the result div + document.getElementById('idResutl').innerHTML = `
${flowStatusResponses}
`; + }) + .catch(error => { + console.error('Error:', error); + }); +} + +// Function to determine which flow-related action to execute based on user input +function executeButtonFlow() { + // Check the value of the dropdown to determine which action to perform + if (document.getElementById("dropdown").value == "option1") { + getAllFlowResult(); + } else { + oneFlow(); + } +} + +function queryDropDownChange(){ + if (document.getElementById("dropdown").value == "option1") { + document.getElementById("clientID").style.display = 'none'; + }else{ + document.getElementById("clientID").style.display = 'block'; + + } +} \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/static/css/main.css b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/static/css/main.css new file mode 100644 index 0000000..5e4d849 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/static/css/main.css @@ -0,0 +1,202 @@ +body { + font-family: 'Roboto', sans-serif; + background-color: #f4f4f4; + color: #333; + margin: 50px; /* Add margin to the entire body */ + padding: 0; +} + +h1 { + text-align: center; + color: #0e0c0c; +} + +/* Style for the label */ +label { + display: block; + margin-bottom: 10px; + font-weight: bold; + color: #333; +} +/* Style for the dropdown button */ +select { + width: 20%; + padding: 8px; + margin-right: 10px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + font-size: 14px; + color: #555; +} + +#itemDropdown { + width: 40%; + padding: 10px; + box-sizing: border-box; +} + +#clientID{ + padding: 8px; + margin-bottom: 15px; + margin-right: 10px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + font-size: 14px; + color: #555; +} + +#OneFlowButon{ + padding: 8px; + margin-bottom: 15px; + margin-right: 10px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + font-size: 14px; +} + +#result { + margin-bottom: 10px; +} + +/* Button styling */ +button { + background-color: #3498db; + color: #fff; + padding: 10px 25px; + font-size: 16px; + border: 2px; + border-radius: 15px; + cursor: pointer; + transition: background-color 0.3s ease; +} + +button:hover { + background-color: #2980b9; +} + +/* Tab styling */ +.tab { + list-style-type: none; /* Remove default list styles */ + display: inline-block; /* Display tabs inline */ + padding: 2px 00px; /* Add padding to the tabs */ + margin: 0 1px; /* Add margin between tabs */ + cursor: pointer; /* Change cursor to pointer on hover */ +} + + +.tab li { + flex: 1; + text-align: center; + padding: 10px; + background-color: #3498db; + color: #fff; + border-radius: 8px 15px 0 0; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.tab li:hover { + background-color: #2980b9; +} + +/* Tab content styling */ +.tab-content { + display: none; + padding: 20px; + border: 1px solid #3498db; + border-radius: 0 0 5px 5px; + background-color: #fff; +} + +.styled-input { + width: 100%; + padding: 10px; + margin-bottom: 10px; + box-sizing: border-box; +} + +.output { + border: 1px solid #3498db; + padding: 10px; + border-radius: 5px; + background-color: #fff; + box-sizing: border-box; +} + +#idResutl { + border: 1px solid #3498db; + padding: 10px; + border-radius: 5px; + background-color: #fff; + margin-top: 10px; + height: 290px; + max-height: 290px; /* Set a maximum height for the scroll box */ + overflow-y: auto; /* Enable vertical scrolling if content exceeds the box height */ +} + +/* Responsive design */ +@media screen and (max-width: 600px) { + .tab li { + border-radius: 5px; + margin-bottom: 5px; + } + .tab-content { + border-radius: 5px; + } +} +#call { + display: -ms-inline-flexbox; + flex-wrap: wrap; + +} + +/* Style for side-by-side input boxes */ +.flowcall-container { + display: flex; +} + +.input-box, .text-box { + width: 150px; /* Set the desired width */ + margin-right: 10px; /* Optional: Add margin for spacing between input boxes */ + border-radius: 5px; + border: 1px solid #3498db; +} + +.queryoption-container{ + display: flex; +} + + + +#requestBody { + flex: 1; + box-sizing: border-box; + width: 100px; /* Set the desired width */ + height: 300px; /* Set the desired height */ + padding: 10px; /* Optional: Add padding for better aesthetics */ + margin-right: 10px; /* Add some margin between the input and button */ +} + +#postBtn { + flex: 0 0 auto; /* Don't allow the button to grow or shrink */ + margin-top: 10px; /* Add some margin between the button and result box */ + margin-right: 10px; /* Add some margin between the button and result box */ + width: 500px; /* Set the desired width */ + height: 50px; /* Set the desired height */ +} + +#queryResult { + flex: 1; + box-sizing: border-box; + width: 100px; /* Set the desired width */ + height: 300px; /* Set the desired height */ + padding: 10px; /* Optional: Add padding for better aesthetics */ +} +.content-box{ + padding: 10px; /* Add padding to the content boxes */ +} + + diff --git a/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/templates/index.html b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/templates/index.html new file mode 100644 index 0000000..44df7ac --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/FlowManagementUI/templates/index.html @@ -0,0 +1,87 @@ + + + + + + + + + + Flask Frontend Example + + + + + + + + + + + + +

Flow Management APIs

+
+ + + + + + +

Please select a flow initiator

+ + +
    + +
  • Flow Call
  • +
  • Flow Query
  • + + +
    +
+ + +
+ +
+ + + + +
+
+ + +
Result will be displayed here
+
+ +
+
+ + +
+ + + +
+ + + + + + + + +
+ + + + + +
Result will be displayed here
+
+ + diff --git a/kotlin-samples/sendAndRecieveTransaction/README.md b/kotlin-samples/sendAndRecieveTransaction/README.md new file mode 100644 index 0000000..12d358c --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/README.md @@ -0,0 +1,120 @@ +# sendAndReceiveTransaction + +When working with the Corda platform, every transaction is stored in the participants' +vaults. The vault is a where all the transactions involving the owner are securely saved. +Each vault is unique and accessible only by its owner, +serving as a ledger to track all the owner's transactions. +However, there are scenarios where you may want a third party to receive a copy of the +transaction. This is where the SendAndRecieveTransaction function becomes essential. +For instance, if Alice conducts a transaction with Bob and wants Charlie to receive a copy, +Alice can simply run a flow using the transaction ID to send a copy to Charlie's vault. +Another application of this function can be an automated reporting tool, +which can be utilized at the end of each transaction finalizing flow to automatically +report to a specific vnode. This functionality can act like a bookkeeper, +meticulously tracking each transaction and ensuring accurate record-keeping. +` + +### Setting up + +1. We will begin our test deployment with clicking the `startCorda`. This task will load up the combined Corda workers in docker. + A successful deployment will allow you to open the REST APIs at: https://localhost:8888/api/v5_2/swagger#/. You can test out some + functions to check connectivity. (GET /cpi function call should return an empty list as for now.) +2. We will now deploy the cordapp with a click of `5-vNodeSetup` task. Upon successful deployment of the CPI, the GET /cpi function call should now return the meta data of the cpi you just upload + + + +### Running the app + +In Corda 5, flows will be triggered via `POST /flow/{holdingidentityshorthash}` and flow result will need to be view at `GET /flow/{holdingidentityshorthash}/{clientrequestid}` +* holdingidentityshorthash: the id of the network participants, ie Bob, Alice, Charlie. You can view all the short hashes of the network member with another gradle task called `ListVNodes` +* clientrequestid: the id you specify in the flow requestBody when you trigger a flow. + +#### Step 1: Create IOUState between two parties +Pick a VNode identity to initiate the IOU creation, and get its short hash. (Let's pick Alice. Don't pick Bob because Bob is the person who alice will borrow from). + +Go to `POST /flow/{holdingidentityshorthash}`, enter the identity short hash(Alice's hash) and request body: +``` +{ + "clientRequestId": "createiou-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.IOUIssueFlow", + "requestBody": { + "amount":"20", + "lender":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB" + } +} +``` + +After trigger the create-IOU flow, hop to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and enter the short hash(Alice's hash) and client request id to view the flow result +The stateRef of the transaction will be returned as a result of the flow query. Which is the "flowResult". + +#### Step 2: Sending a copy of the transaction to a third party. +If a member needs to share a copy of their transaction with another member, +they can do so using the process outlined below. For instance, +if Alice wishes to send a copy of the transaction to Dave, +we can execute the following request body with her short hash: + +``` +{ + "clientRequestId": "sendAndRecieve-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.sendAndRecieveTransactionFlow", + "requestBody": { + "stateRef": "STATEREF ID HERE", + "members": ["CN=Dave, OU=Test Dept, O=R3, L=London, C=GB"], + "forceBackchain": "false" + } +} +``` + +Ensure to replace the stateRef variable with the stateRef of the transaction. The stateRef will be found when you run the GET call, +QueryAll in the swaggerAPI. The stateRef is labeled as `flowResult` in response body, begins with SHA-256D:XXXXX.. +``` +[ + { + "holdingIdentityShortHash": "A93A019B324E", + "clientRequestId": "createiou-1", + "flowId": "26ea3f95-141b-4aaa-9b58-e3a685dc54d3", + "flowStatus": "COMPLETED", + "flowResult": "SHA-256D:B3D87C8B446C277B5658BBB2A18DC7491539D898B70F074418878091AE315B4A", + "flowError": null, + "timestamp": "2024-07-08T04:37:16.175Z" + } +] +``` +After running this flow Dave will have the transaction in his vault. + + +Results + +Currently, Alice has two transactions stored in her vault. +The transaction we aim to send to Charlie is identified by the ID SHA-256D:8DFDD672…. +You can view Alice's transactions in the image provided below: + +

+ Encumbrance Flow +

+ +On the other hand, Charlie’s vault currently holds no transactions, +as illustrated in the image below: + +

+ Encumbrance Flow +

+ +Once Alice executes the flow, Charlie’s vault is updated to include the transaction, +which is displayed as follows: + +

+ Encumbrance Flow +

+ +All images of the vault were sourced through DBeaver by establishing a connection +to the Cordapp using PostgreSQL. The credentials utilized for this connection are as follows: + +POSTGRES_DB = cordacluster
+POSTGRES_USER = postgres
+POSTGRES_PASSWORD = password + +To access the vault, navigate through the hierarchy in +PostgreSQL: Databases > cordacluster > Schemas > vnode_vault_(HASH_ID_OF_VNODE) > +Tables > utxo_transaction. To view the transactions, +simply double-click on utxo_transaction. \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/aliceVault.png b/kotlin-samples/sendAndRecieveTransaction/aliceVault.png new file mode 100644 index 0000000000000000000000000000000000000000..d377488d4b50cbdc78d2c8199dabf5fbad10b0e7 GIT binary patch literal 17869 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 AK>z>% literal 0 HcmV?d00001 diff --git a/kotlin-samples/sendAndRecieveTransaction/build.gradle b/kotlin-samples/sendAndRecieveTransaction/build.gradle new file mode 100644 index 0000000..210776a --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/build.gradle @@ -0,0 +1,88 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +import static org.gradle.api.JavaVersion.VERSION_17 + +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'net.corda.cordapp.cordapp-configuration' + id 'org.jetbrains.kotlin.plugin.jpa' + id 'java' + id 'maven-publish' + id 'net.corda.gradle.plugin' +} + +allprojects { + group 'com.r3.developers.cordapptemplate' + version '1.0-SNAPSHOT' + + def javaVersion = VERSION_17 + + // Configure Corda runtime gradle plugin + cordaRuntimeGradlePlugin { + notaryVersion = cordaNotaryPluginsVersion + notaryCpiName = "NotaryServer" + corDappCpiName = "MyCorDapp" + cpiUploadTimeout = "30000" + vnodeRegistrationTimeout = "60000" + cordaProcessorTimeout = "300000" + workflowsModuleName = "workflows" + cordaClusterURL = "https://localhost:8888" + cordaRestUser = "admin" + cordaRestPasswd ="admin" + composeFilePath = "config/combined-worker-compose.yaml" + networkConfigFile = "config/static-network-config.json" + r3RootCertFile = "config/r3-ca-key.pem" + skipTestsDuringBuildCpis = "false" + cordaRuntimePluginWorkspaceDir = "workspace" + cordaBinDir = "${System.getProperty("user.home")}/.corda/corda5" + cordaCliBinDir = "${System.getProperty("user.home")}/.corda/cli" + } + + // Declare the set of Kotlin compiler options we need to build a CorDapp. + tasks.withType(KotlinCompile).configureEach { + kotlinOptions { + allWarningsAsErrors = false + + // Specify the version of Kotlin that we are that we will be developing. + languageVersion = '1.7' + // Specify the Kotlin libraries that code is compatible with + apiVersion = '1.7' + // Note that we Need to use a version of Kotlin that will be compatible with the Corda API. + // Currently that is developed in Kotlin 1.7 so picking the same version ensures compatibility with that. + + // Specify the version of Java to target. + jvmTarget = javaVersion + + // Needed for reflection to work correctly. + javaParameters = true + + // -Xjvm-default determines how Kotlin supports default methods. + // JetBrains currently recommends developers use -Xjvm-default=all + // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-default/ + freeCompilerArgs += [ + "-Xjvm-default=all" + ] + } + } + + repositories { + // All dependencies are held in Maven Central + mavenLocal() + mavenCentral() + } + + tasks.withType(Test).configureEach { + useJUnitPlatform() + } + +} + +publishing { + publications { + maven(MavenPublication) { + artifactId "corda-dev-template-kotlin-sample" + groupId project.group + artifact jar + } + } +} \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/charlieAfter.png b/kotlin-samples/sendAndRecieveTransaction/charlieAfter.png new file mode 100644 index 0000000000000000000000000000000000000000..51d47a777d1b32d6f19f0685528d83a161cc0225 GIT binary patch literal 50718 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 KMNUMnLSTZBOgaPr literal 0 HcmV?d00001 diff --git a/kotlin-samples/sendAndRecieveTransaction/charlieBefore.png b/kotlin-samples/sendAndRecieveTransaction/charlieBefore.png new file mode 100644 index 0000000000000000000000000000000000000000..e9cb66106212225fd128fc207a1b7fab7c57a63a GIT binary patch literal 9976 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 huL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/config/r3-ca-key.pem b/kotlin-samples/sendAndRecieveTransaction/config/r3-ca-key.pem new file mode 100644 index 0000000..a803613 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/config/r3-ca-key.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/config/static-network-config.json b/kotlin-samples/sendAndRecieveTransaction/config/static-network-config.json new file mode 100644 index 0000000..b0f2519 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/config/static-network-config.json @@ -0,0 +1,23 @@ +[ + { + "x500Name" : "CN=Alice, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "MyCorDapp" + }, + { + "x500Name" : "CN=Bob, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "MyCorDapp" + }, + { + "x500Name" : "CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "MyCorDapp" + }, + { + "x500Name" : "CN=Dave, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "MyCorDapp" + }, + { + "x500Name" : "CN=NotaryRep1, OU=Test Dept, O=R3, L=London, C=GB", + "cpi" : "NotaryServer", + "serviceX500Name": "CN=NotaryService, OU=Test Dept, O=R3, L=London, C=GB" + } +] diff --git a/kotlin-samples/sendAndRecieveTransaction/contracts/build.gradle b/kotlin-samples/sendAndRecieveTransaction/contracts/build.gradle new file mode 100644 index 0000000..c1f6ed6 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/contracts/build.gradle @@ -0,0 +1,88 @@ + +plugins { + // Include the cordapp-cpb plugin. This automatically includes the cordapp-cpk plugin as well. + // These extend existing build environment so that CPB and CPK files can be built. + // This includes a CorDapp DSL that allows the developer to supply metadata for the CorDapp + // required by Corda. + id 'net.corda.plugins.cordapp-cpb2' + id 'org.jetbrains.kotlin.jvm' + id 'maven-publish' +} + +// Declare dependencies for the modules we will use. +// A cordaProvided declaration is required for anything that we use that the Corda API provides. +// This is required to allow us to build CorDapp modules as OSGi bundles that CPI and CPB files are built on. +dependencies { + + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + + // Declare a "platform" so that we use the correct set of dependency versions for the version of the + // Corda API specified. + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + + // If using transistive dependencies this will provide most of Corda-API: + // cordaProvided 'net.corda:corda-application' + + // Alternatively we can explicitly specify all our Corda-API dependencies: + cordaProvided 'net.corda:corda-base' + cordaProvided 'net.corda:corda-application' + cordaProvided 'net.corda:corda-crypto' + cordaProvided 'net.corda:corda-membership' + // cordaProvided 'net.corda:corda-persistence' + cordaProvided 'net.corda:corda-serialization' + cordaProvided 'net.corda:corda-ledger-utxo' + cordaProvided 'net.corda:corda-ledger-consensual' + + // CorDapps that use the UTXO ledger must include at least one notary client plugin + cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" + + // The CorDapp uses the slf4j logging framework. Corda-API provides this so we need a 'cordaProvided' declaration. + cordaProvided 'org.slf4j:slf4j-api' + + // 3rd party libraries + // Required + testImplementation "org.slf4j:slf4j-simple:$slf4jVersion" + testImplementation "org.junit.jupiter:junit-jupiter:$junitVersion" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + + // Optional but used by example tests. + testImplementation "org.mockito:mockito-core:$mockitoVersion" + testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion" + testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion" +// testImplementation "com.r3.corda.ledger.utxo:contract-testing:$contractTestingVersion" +// testImplementation "com.r3.corda.ledger.utxo:contract-testing-kotlin:$contractTestingVersion" +} + +// The CordApp section. +// This is part of the DSL provided by the corda plugins to define metadata for our CorDapp. +// Each component of the CorDapp would get its own CorDapp section in the build.gradle file for the component’s +// subproject. +// This is required by the corda plugins to build the CorDapp. +cordapp { + // "targetPlatformVersion" and "minimumPlatformVersion" are intended to specify the preferred + // and earliest versions of the Corda platform that the CorDapp will run on respectively. + // Enforced versioning has not implemented yet so we need to pass in a dummy value for now. + // The platform version will correspond to and be roughly equivalent to the Corda API version. + targetPlatformVersion = platformVersion.toInteger() + minimumPlatformVersion = platformVersion.toInteger() + + // The cordapp section contains either a workflow or contract subsection depending on the type of component. + // Declares the type and metadata of the CPK (this CPB has one CPK). + contract { + name contractsModule + versionId 1 + licence cordappLicense + vendor cordappVendorName + } +} + +// Use the name of the contract module as the name of the generated CPK and CPB. +archivesBaseName = cordapp.contract.name.isPresent() ? cordapp.contract.name.get() : contractsModule + +publishing { + publications { + maven(MavenPublication) { + from components.cordapp + } + } +} \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/contracts/src/main/kotlin/com/r3/developers/samples/obligation/contracts/IOUContract.kt b/kotlin-samples/sendAndRecieveTransaction/contracts/src/main/kotlin/com/r3/developers/samples/obligation/contracts/IOUContract.kt new file mode 100644 index 0000000..25338ee --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/contracts/src/main/kotlin/com/r3/developers/samples/obligation/contracts/IOUContract.kt @@ -0,0 +1,55 @@ +package com.r3.developers.samples.obligation.contracts + +import com.r3.developers.samples.obligation.states.IOUState +import net.corda.v5.base.exceptions.CordaRuntimeException +import net.corda.v5.ledger.utxo.Command +import net.corda.v5.ledger.utxo.Contract +import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction + +class IOUContract : Contract { + + //IOU Commands + class Issue: Command + class Settle: Command + class Transfer: Command + + override fun verify(transaction: UtxoLedgerTransaction) { + + // Ensures that there is only one command in the transaction + val command = transaction.commands.singleOrNull() ?: throw CordaRuntimeException("Requires a single command.") + + "The output state should have two and only two participants." using { + val output = transaction.outputContractStates.first() as IOUState + output.participants.size== 2 + } + // Switches case based on the command + when(command) { + // Rules applied only to transactions with the Issue Command. + is Issue -> { + "When command is Create there should be one and only one output state." using (transaction.outputContractStates.size == 1) + } + // Rules applied only to transactions with the Settle Command. + is Settle -> { + "When command is Update there should be one and only one output state." using (transaction.outputContractStates.size == 1) + } + // Rules applied only to transactions with the Transfer Command. + is Transfer -> { + "When command is Update there should be one and only one output state." using (transaction.outputContractStates.size == 1) + } + else -> { + throw CordaRuntimeException("Command not allowed.") + } + } + } + + // Helper function to allow writing constraints in the Corda 4 '"text" using (boolean)' style + private infix fun String.using(expr: Boolean) { + if (!expr) throw CordaRuntimeException("Failed requirement: $this") + } + + // Helper function to allow writing constraints in '"text" using {lambda}' style where the last expression + // in the lambda is a boolean. + private infix fun String.using(expr: () -> Boolean) { + if (!expr.invoke()) throw CordaRuntimeException("Failed requirement: $this") + } +} \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/contracts/src/main/kotlin/com/r3/developers/samples/obligation/states/IOUState.kt b/kotlin-samples/sendAndRecieveTransaction/contracts/src/main/kotlin/com/r3/developers/samples/obligation/states/IOUState.kt new file mode 100644 index 0000000..7dbb518 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/contracts/src/main/kotlin/com/r3/developers/samples/obligation/states/IOUState.kt @@ -0,0 +1,36 @@ +package com.r3.developers.samples.obligation.states + +import com.r3.developers.samples.obligation.contracts.IOUContract +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.utxo.BelongsToContract +import net.corda.v5.ledger.utxo.ContractState +import java.security.PublicKey +import java.util.* + +//Link with the Contract class +@BelongsToContract(IOUContract::class) +data class IOUState ( + + //private variables + val amount: Int, + val lender: MemberX500Name, + val borrower: MemberX500Name, + val paid: Int, + val linearId: UUID, + private val participants: List +) : ContractState { + + //Helper method for settle flow + fun pay(amountToPay: Int) : IOUState { + return IOUState(amount,lender,borrower,paid+amountToPay,linearId,participants) + } + + //Helper method for transfer flow + fun withNewLender(newLender:MemberX500Name, newParticipants:List ): IOUState { + return IOUState(amount,newLender,borrower,paid,linearId,newParticipants) + } + + override fun getParticipants(): List { + return participants + } +} \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/gradle.properties b/kotlin-samples/sendAndRecieveTransaction/gradle.properties new file mode 100644 index 0000000..2549e5f --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/gradle.properties @@ -0,0 +1,73 @@ +kotlin.code.style=official + +# Specify the version of the Corda-API to use. +# This needs to match the version supported by the Corda Cluster the CorDapp will run on. +cordaApiVersion=5.2.0.52 + +# Specify the version of the notary plugins to use. +# Currently packaged as part of corda-runtime-os, so should be set to a corda-runtime-os version. +cordaNotaryPluginsVersion=5.2.0.0 + +# Specify the version of the cordapp-cpb and cordapp-cpk plugins +cordaPluginsVersion=7.0.4 + +# Specify the version of the Corda runtime Gradle plugin to use +cordaGradlePluginVersion=5.2.0.0 + +# Specify the name of the workflows module +# This will be the name of the generated cpk and cpb files +workflowsModule=workflows + +# Specify the name of the contracts module +# This will be the name of the generated cpk and cpb files +contractsModule=contracts + +# Specify the location of where Corda 5 binaries can be downloaded +# Relative path from user.home +cordaBinariesDirectory = .corda/corda5 + +# Specify the location of where Corda 5 CLI binaries can be downloaded +# Relative path from user.home +cordaCliBinariesDirectory = .corda/cli + +# Metadata for the CorDapp. +cordappLicense="Apache License, Version 2.0" +cordappVendorName="R3" + +# For the time being this just needs to be set to a dummy value. +platformVersion = 999 + +# Version of Kotlin to use. +# We recommend using a version close to that used by Corda-API. +kotlinVersion = 1.7.21 + +# Do not use default dependencies. +kotlin.stdlib.default.dependency=false + +# Test Tooling Dependency Versions +junitVersion = 5.10.0 +mockitoKotlinVersion=4.0.0 +mockitoVersion=4.6.1 +hamcrestVersion=2.2 +assertjVersion = 3.24.1 +contractTestingVersion=1.0.0-beta-+ +jacksonVersion=2.15.2 +slf4jVersion=1.7.36 + +# Specify the maximum amount of time allowed for the CPI upload +# As your CorDapp grows you might need to increase this +# Value is in milliseconds +cpiUploadDefault=10000 + +# Specify the length of time, in milliseconds, that Corda waits for an individual event to process. +# Keep at -1 to use the default. Refer to the Corda Api Docs for the exact value. +processorTimeout=-1 + +# Specify the maximum amount of time allowed to check all vNodes are registered +# Value is in milliseconds +vnodeRegistrationTimeoutDefault=30000 + +# Specify if you want to run the contracts and workflows tests as part of the corda-runtime-plugin-cordapp > buildCpis task +# False by default, will execute the tests every time you stand the template up - gives extra protection +# Set to true to skip the tests, making the launching process quicker. You will be responsible for running workflow tests yourself +skipTestsDuringBuildCpis=false \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.jar b/kotlin-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..943f0cbfa754578e88a3dae77fce6e3dea56edbf GIT binary patch literal 61574 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 ScNgau(U?ep-K_E5zy1%ZQTdPn literal 0 HcmV?d00001 diff --git a/kotlin-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.properties b/kotlin-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..5083229 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/kotlin-samples/sendAndRecieveTransaction/gradlew b/kotlin-samples/sendAndRecieveTransaction/gradlew new file mode 100644 index 0000000..65dcd68 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/kotlin-samples/sendAndRecieveTransaction/gradlew.bat b/kotlin-samples/sendAndRecieveTransaction/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/kotlin-samples/sendAndRecieveTransaction/settings.gradle b/kotlin-samples/sendAndRecieveTransaction/settings.gradle new file mode 100644 index 0000000..ee7121e --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + // Declare the repositories where plugins are stored. + repositories { + gradlePluginPortal() + mavenCentral() + mavenLocal() + } + + // The plugin dependencies with versions of the plugins congruent with the specified CorDapp plugin version, + // Corda API version, and Kotlin version. + plugins { + id 'net.corda.plugins.cordapp-cpk2' version cordaPluginsVersion + id 'net.corda.plugins.cordapp-cpb2' version cordaPluginsVersion + id 'net.corda.cordapp.cordapp-configuration' version cordaApiVersion + id 'org.jetbrains.kotlin.jvm' version kotlinVersion + id 'org.jetbrains.kotlin.plugin.jpa' version kotlinVersion + id 'org.jetbrains.kotlin.plugin.allopen' version kotlinVersion + id 'net.corda.gradle.plugin' version cordaGradlePluginVersion + } +} + +// Root project name, used in naming the project as a whole and used in naming objects built by the project. +rootProject.name = 'sendAndReceiveTransaction' +include ':workflows' +include ':contracts' \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/workflows/build.gradle b/kotlin-samples/sendAndRecieveTransaction/workflows/build.gradle new file mode 100644 index 0000000..c8bde66 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/workflows/build.gradle @@ -0,0 +1,98 @@ +plugins { + // Include the cordapp-cpb plugin. This automatically includes the cordapp-cpk plugin as well. + // These extend existing build environment so that CPB and CPK files can be built. + // This includes a CorDapp DSL that allows the developer to supply metadata for the CorDapp + // required by Corda. + id 'net.corda.plugins.cordapp-cpb2' + id 'org.jetbrains.kotlin.jvm' + id 'maven-publish' +} + +// Declare dependencies for the modules we will use. +// A cordaProvided declaration is required for anything that we use that the Corda API provides. +// This is required to allow us to build CorDapp modules as OSGi bundles that CPI and CPB files are built on. +dependencies { + constraints { + testImplementation('org.slf4j:slf4j-api') { + version { + // Corda cannot use SLF4J 2.x yet. + strictly '1.7.36' + } + } + } + + // From other subprojects: + cordapp project(':contracts') + + cordaProvided 'org.jetbrains.kotlin:kotlin-osgi-bundle' + + // Declare a "platform" so that we use the correct set of dependency versions for the version of the + // Corda API specified. + cordaProvided platform("net.corda:corda-api:$cordaApiVersion") + + // If using transistive dependencies this will provide most of Corda-API: + // cordaProvided 'net.corda:corda-application' + + // Alternatively we can explicitly specify all our Corda-API dependencies: + cordaProvided 'net.corda:corda-base' + cordaProvided 'net.corda:corda-application' + cordaProvided 'net.corda:corda-crypto' + cordaProvided 'net.corda:corda-membership' + // cordaProvided 'net.corda:corda-persistence' + cordaProvided 'net.corda:corda-serialization' + cordaProvided 'net.corda:corda-ledger-utxo' + cordaProvided 'net.corda:corda-ledger-consensual' + + // CorDapps that use the UTXO ledger must include at least one notary client plugin + cordapp "com.r3.corda.notary.plugin.nonvalidating:notary-plugin-non-validating-client:$cordaNotaryPluginsVersion" + + // The CorDapp uses the slf4j logging framework. Corda-API provides this so we need a 'cordaProvided' declaration. + cordaProvided 'org.slf4j:slf4j-api' + + // 3rd party libraries + // Required + testImplementation "org.slf4j:slf4j-simple:$slf4jVersion" + testImplementation "org.junit.jupiter:junit-jupiter:$junitVersion" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + + // Optional but used by example tests. + testImplementation "org.mockito:mockito-core:$mockitoVersion" + testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion" + testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion" + testImplementation "org.assertj:assertj-core:$assertjVersion" + testImplementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion" +} + +// The CordApp section. +// This is part of the DSL provided by the corda plugins to define metadata for our CorDapp. +// Each component of the CorDapp would get its own CorDapp section in the build.gradle file for the component’s +// subproject. +// This is required by the corda plugins to build the CorDapp. +cordapp { + // "targetPlatformVersion" and "minimumPlatformVersion" are intended to specify the preferred + // and earliest versions of the Corda platform that the CorDapp will run on respectively. + // Enforced versioning has not implemented yet so we need to pass in a dummy value for now. + // The platform version will correspond to and be roughly equivalent to the Corda API version. + targetPlatformVersion = platformVersion.toInteger() + minimumPlatformVersion = platformVersion.toInteger() + + // The cordapp section contains either a workflow or contract subsection depending on the type of component. + // Declares the type and metadata of the CPK (this CPB has one CPK). + workflow { + name workflowsModule + versionId 1 + licence cordappLicense + vendor cordappVendorName + } +} + +// Use the name of the workflow module as the name of the generated CPK and CPB. +archivesBaseName = cordapp.workflow.name.isPresent() ? cordapp.workflow.name.get() : workflowsModule + +publishing { + publications { + maven(MavenPublication) { + from components.cordapp + } + } +} \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/FinalizeIOUFlow.kt b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/FinalizeIOUFlow.kt new file mode 100644 index 0000000..9c09db4 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/FinalizeIOUFlow.kt @@ -0,0 +1,98 @@ +package com.r3.developers.samples.obligation.workflows + +import com.r3.developers.samples.obligation.states.IOUState +import net.corda.v5.application.flows.* +import net.corda.v5.application.messaging.FlowMessaging +import net.corda.v5.application.messaging.FlowSession +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.exceptions.CordaRuntimeException +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.utxo.UtxoLedgerService +import net.corda.v5.ledger.utxo.transaction.UtxoSignedTransaction +import org.slf4j.LoggerFactory + +// @InitiatingFlow declares the protocol which will be used to link the initiator to the responder. +@InitiatingFlow(protocol = "finalize-iou-protocol") +class FinalizeIOUSubFlow(private val signedTransaction: UtxoSignedTransaction, private val otherMember: List): SubFlow { + + private companion object { + val log = LoggerFactory.getLogger(this::class.java.enclosingClass) + } + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + lateinit var ledgerService: UtxoLedgerService + + @CordaInject + lateinit var flowMessaging: FlowMessaging + + @CordaInject + lateinit var utxoLedgerService: UtxoLedgerService + + + @Suspendable + override fun call(): String { + + log.info("FinalizeIOUSubFlow.call() called") + + // Initiates a session with the other Member. + val sessions = otherMember.map { flowMessaging.initiateFlow(it) } + + + return try { + // Calls the Corda provided finalise() function which gather signatures from the counterparty, + // notarises the transaction and persists the transaction to each party's vault. + // On success returns the id of the transaction created. + val finalizedSignedTransaction = ledgerService.finalize( + signedTransaction, + sessions + ) + // Returns the transaction id converted to a string. + finalizedSignedTransaction.transaction.id.toString().also { + log.info("Success! Response: $it") + } + } + // Soft fails the flow and returns the error message without throwing a flow exception. + catch (e: Exception) { + log.warn("Finality failed", e) + "Finality failed, ${e.message}" + } + } +} + +//@InitiatingBy declares the protocol which will be used to link the initiator to the responder. +@InitiatedBy(protocol = "finalize-iou-protocol") +class FinalizeIOUResponderFlow: ResponderFlow { + + private companion object { + val log = LoggerFactory.getLogger(this::class.java.enclosingClass) + } + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + lateinit var ledgerService: UtxoLedgerService + + @Suspendable + override fun call(session: FlowSession) { + + log.info("FinalizeIOUResponderFlow.call() called") + + try { + // Calls receiveFinality() function which provides the responder to the finalise() function + // in the Initiating Flow. Accepts a lambda validator containing the business logic to decide whether + // responder should sign the Transaction. + val finalizedSignedTransaction = ledgerService.receiveFinality(session) { ledgerTransaction -> + + // Note, this exception will only be shown in the logs if Corda Logging is set to debug. + val state = ledgerTransaction.getOutputStates(IOUState::class.java).singleOrNull() ?: throw CordaRuntimeException("Failed verification - transaction did not have exactly one output IOUState.") + + log.info("Verified the transaction- ${ledgerTransaction.id}") + } + log.info("Finished responder flow - ${finalizedSignedTransaction.transaction.id}") + } + // Soft fails the flow and log the exception. + catch (e: Exception) { + log.warn("Exceptionally finished responder flow", e) + } + } +} \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUIssueFlow.kt b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUIssueFlow.kt new file mode 100644 index 0000000..53b6a41 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUIssueFlow.kt @@ -0,0 +1,119 @@ +package com.r3.developers.samples.obligation.workflows + +import com.r3.developers.samples.obligation.contracts.IOUContract +import com.r3.developers.samples.obligation.states.IOUState +import net.corda.v5.application.flows.ClientRequestBody +import net.corda.v5.application.flows.ClientStartableFlow +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.FlowEngine +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.exceptions.CordaRuntimeException +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.common.NotaryLookup +import net.corda.v5.ledger.utxo.UtxoLedgerService +import org.slf4j.LoggerFactory +import java.time.Duration +import java.time.Instant +import java.util.* + + +// A class to hold the deserialized arguments required to start the flow. +data class IOUIssueFlowArgs(val amount: String, val lender: String) + +class IOUIssueFlow: ClientStartableFlow { + + private companion object { + val log = LoggerFactory.getLogger(this::class.java.enclosingClass) + } + + // Injects the JsonMarshallingService to read and populate JSON parameters. + @CordaInject + lateinit var jsonMarshallingService: JsonMarshallingService + + // Injects the MemberLookup to look up the VNode identities. + @CordaInject + lateinit var memberLookup: MemberLookup + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + lateinit var ledgerService: UtxoLedgerService + + // Injects the NotaryLookup to look up the notary identity. + @CordaInject + lateinit var notaryLookup: NotaryLookup + + // FlowEngine service is required to run SubFlows. + @CordaInject + lateinit var flowEngine: FlowEngine + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + log.info("IOUIssueFlow.call() called") + + try { + // Obtain the deserialized input arguments to the flow from the requestBody. + val flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, IOUIssueFlowArgs::class.java) + + // Get MemberInfos for the Vnode running the flow and the otherMember. + // Good practice in Kotlin CorDapps is to only throw RuntimeException. + // Note, in Java CorDapps only unchecked RuntimeExceptions can be thrown not + // declared checked exceptions as this changes the method signature and breaks override. + val myInfo = memberLookup.myInfo() + val lenderInfo = memberLookup.lookup(MemberX500Name.parse(flowArgs.lender)) + ?: throw CordaRuntimeException("MemberLookup can't find otherMember specified in flow arguments.") + + // Create the IOUState from the input arguments and member information. + val iou = IOUState( + amount = flowArgs.amount.toInt(), + lender = lenderInfo.name, + borrower = myInfo.name, + paid = 0, + linearId = UUID.randomUUID(), + listOf(myInfo.ledgerKeys[0],lenderInfo.ledgerKeys[0]) + ) + + // Obtain the notary. + val notary = notaryLookup.lookup(MemberX500Name.parse("CN=NotaryService, OU=Test Dept, O=R3, L=London, C=GB")) + ?: throw CordaRuntimeException("NotaryLookup can't find notary specified in flow arguments.") + + // Use UTXOTransactionBuilder to build up the draft transaction. + val txBuilder= ledgerService.createTransactionBuilder() + .setNotary(notary.name) + .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) + .addOutputState(iou) + .addCommand(IOUContract.Issue()) + .addSignatories(iou.participants) + + // Convert the transaction builder to a UTXOSignedTransaction. Verifies the content of the + // UtxoTransactionBuilder and signs the transaction with any required signatories that belong to + // the current node. + val signedTransaction = txBuilder.toSignedTransaction() + + // Call FinalizeIOUSubFlow which will finalise the transaction. + // If successful the flow will return a String of the created transaction id, + // if not successful it will return an error message. + return flowEngine.subFlow(FinalizeIOUSubFlow(signedTransaction, listOf(lenderInfo.name))) + + + } + // Catch any exceptions, log them and rethrow the exception. + catch (e: Exception) { + log.warn("Failed to process utxo flow for request body '$requestBody' because:'${e.message}'") + throw e + } } + +} + +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "createiou-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.IOUIssueFlow", + "requestBody": { + "amount":"20", + "lender":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB" + } +} + */ diff --git a/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUSettleFlow.kt b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUSettleFlow.kt new file mode 100644 index 0000000..ff61817 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUSettleFlow.kt @@ -0,0 +1,119 @@ +package com.r3.developers.samples.obligation.workflows + +import com.r3.developers.samples.obligation.contracts.IOUContract +import com.r3.developers.samples.obligation.states.IOUState +import net.corda.v5.application.flows.ClientRequestBody +import net.corda.v5.application.flows.ClientStartableFlow +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.FlowEngine +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.exceptions.CordaRuntimeException +import net.corda.v5.ledger.utxo.UtxoLedgerService +import org.slf4j.LoggerFactory +import java.time.Duration +import java.time.Instant +import java.util.* + + +// A class to hold the deserialized arguments required to start the flow. +data class IOUSettleFlowArgs(val amountSettle: String, val iouID: UUID) + +class IOUSettleFlow: ClientStartableFlow { + + private companion object { + val log = LoggerFactory.getLogger(this::class.java.enclosingClass) + } + + // Injects the JsonMarshallingService to read and populate JSON parameters. + @CordaInject + lateinit var jsonMarshallingService: JsonMarshallingService + + // Injects the MemberLookup to look up the VNode identities. + @CordaInject + lateinit var memberLookup: MemberLookup + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + lateinit var ledgerService: UtxoLedgerService + + // FlowEngine service is required to run SubFlows. + @CordaInject + lateinit var flowEngine: FlowEngine + + @CordaInject + lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + log.info("IOUSettleFlow.call() called") + + try { + // Obtain the deserialized input arguments to the flow from the requestBody. + val flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, IOUSettleFlowArgs::class.java) + + // Get flow args from the input JSON + val iouID = flowArgs.iouID + val amountSettle = flowArgs.amountSettle.toInt() + + //query the IOU input + val iouStateAndRefs = ledgerService.findUnconsumedStatesByExactType(IOUState::class.java,100, Instant.now()).results + val iouStateAndRefsWithId = iouStateAndRefs.filter { it.state.contractState.linearId.equals(iouID)} + + if (iouStateAndRefsWithId.size != 1) throw CordaRuntimeException("Multiple or zero IOU states with id \" + iouID + \" found") + val iouStateAndRef = iouStateAndRefsWithId[0] + val iouInput = iouStateAndRef.state.contractState + + //flow logic checks + val myInfo = memberLookup.myInfo() + if (!(myInfo.name.equals(iouInput.borrower))) throw CordaRuntimeException("Only IOU borrower can settle the IOU.") + val lenderInfo = memberLookup.lookup(iouInput.lender) ?: throw CordaRuntimeException("MemberLookup can't find otherMember specified in flow arguments.") + + // Create the IOUState from the input arguments and member information. + val iouOutput = iouInput.pay(amountSettle) + + //get notary from input + val notary = iouStateAndRef.state.notaryName + + // Use UTXOTransactionBuilder to build up the draft transaction. + val txBuilder= ledgerService.createTransactionBuilder() + .setNotary(notary) + .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) + .addInputState(iouStateAndRef.ref) + .addOutputState(iouOutput) + .addCommand(IOUContract.Settle()) + .addSignatories(iouOutput.participants) + + // Convert the transaction builder to a UTXOSignedTransaction. Verifies the content of the + // UtxoTransactionBuilder and signs the transaction with any required signatories that belong to + // the current node. + val signedTransaction = txBuilder.toSignedTransaction() + + + + + // Call FinalizeIOUSubFlow which will finalise the transaction. + // If successful the flow will return a String of the created transaction id, + // if not successful it will return an error message. + return flowEngine.subFlow(FinalizeIOUSubFlow(signedTransaction, listOf(lenderInfo.name))) + } + // Catch any exceptions, log them and rethrow the exception. + catch (e: Exception) { + log.warn("Failed to process utxo flow for request body '$requestBody' because:'${e.message}'") + throw e + } } + +} +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "settleiou-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.IOUSettleFlow", + "requestBody": { + "amountSettle":"10", + "iouID":"4ea35048-879e-43f0-9593-343388715627" + } +} +4ea35048-879e-43f0-9593-343388715627 +*/ diff --git a/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUTransferFlow.kt b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUTransferFlow.kt new file mode 100644 index 0000000..62fc6e5 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/IOUTransferFlow.kt @@ -0,0 +1,116 @@ +package com.r3.developers.samples.obligation.workflows + +import com.r3.developers.samples.obligation.contracts.IOUContract +import com.r3.developers.samples.obligation.states.IOUState +import net.corda.v5.application.flows.ClientRequestBody +import net.corda.v5.application.flows.ClientStartableFlow +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.flows.FlowEngine +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.exceptions.CordaRuntimeException +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.utxo.UtxoLedgerService +import org.slf4j.LoggerFactory +import java.time.Duration +import java.time.Instant +import java.util.* + +// A class to hold the deserialized arguments required to start the flow. +data class IOUTransferFlowArgs(val newLender: String, val iouID: UUID) + +class IOUTransferFlow: ClientStartableFlow { + + private companion object { + val log = LoggerFactory.getLogger(this::class.java.enclosingClass) + } + + // Injects the JsonMarshallingService to read and populate JSON parameters. + @CordaInject + lateinit var jsonMarshallingService: JsonMarshallingService + + // Injects the MemberLookup to look up the VNode identities. + @CordaInject + lateinit var memberLookup: MemberLookup + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + lateinit var ledgerService: UtxoLedgerService + + // FlowEngine service is required to run SubFlows. + @CordaInject + lateinit var flowEngine: FlowEngine + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + log.info("IOUTransferFlow.call() called") + + try { + // Obtain the deserialized input arguments to the flow from the requestBody. + val flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, IOUTransferFlowArgs::class.java) + + // Get flow args from the input JSON + val iouID = flowArgs.iouID + + //query the IOU input + val iouStateAndRefs = ledgerService.findUnconsumedStatesByExactType(IOUState::class.java,100, Instant.now()).results + val iouStateAndRefsWithId = iouStateAndRefs.filter { it.state.contractState.linearId.equals(iouID)} + if (iouStateAndRefsWithId.size != 1) throw CordaRuntimeException("Multiple or zero IOU states with id \" + iouID + \" found") + val iouStateAndRef = iouStateAndRefsWithId[0] + val iouInput = iouStateAndRef.state.contractState + + //flow logic checks + val myInfo = memberLookup.myInfo() + if (!(myInfo.name.equals(iouInput.lender))) throw CordaRuntimeException("Only IOU borrower can settle the IOU.") + + // Get MemberInfos for the Vnode running the flow and the otherMember. + val borrower = memberLookup.lookup(iouInput.borrower) ?: throw CordaRuntimeException("MemberLookup can't find otherMember specified in flow arguments.") + val newLenderInfo = memberLookup.lookup(MemberX500Name.parse(flowArgs.newLender)) ?: throw CordaRuntimeException("MemberLookup can't find otherMember specified in flow arguments.") + + // Create the IOUState from the input arguments and member information. + val iouOutput = iouInput.withNewLender(newLenderInfo.name, listOf(borrower.ledgerKeys[0],newLenderInfo.ledgerKeys[0])) + + //get notary from input + val notary = iouStateAndRef.state.notaryName + + // Use UTXOTransactionBuilder to build up the draft transaction. + val txBuilder= ledgerService.createTransactionBuilder() + .setNotary(notary) + .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) + .addInputState(iouStateAndRef.ref) + .addOutputState(iouOutput) + .addCommand(IOUContract.Settle()) + .addSignatories(iouOutput.participants + listOf(myInfo.ledgerKeys[0])) + + // Convert the transaction builder to a UTXOSignedTransaction. Verifies the content of the + // UtxoTransactionBuilder and signs the transaction with any required signatories that belong to + // the current node. + val signedTransaction = txBuilder.toSignedTransaction() + + // Call FinalizeIOUSubFlow which will finalise the transaction. + // If successful the flow will return a String of the created transaction id, + // if not successful it will return an error message. + return flowEngine.subFlow(FinalizeIOUSubFlow(signedTransaction, listOf(borrower.name,newLenderInfo.name))) + + + } + // Catch any exceptions, log them and rethrow the exception. + catch (e: Exception) { + log.warn("Failed to process utxo flow for request body '$requestBody' because:'${e.message}'") + throw e + } } + +} +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "transferiou-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.IOUTransferFlow", + "requestBody": { + "newLender":"CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB", + "iouID":"4ea35048-879e-43f0-9593-343388715627" + } +} +4ea35048-879e-43f0-9593-343388715627 +*/ diff --git a/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/ListIOUFlow.kt b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/ListIOUFlow.kt new file mode 100644 index 0000000..1f49677 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/ListIOUFlow.kt @@ -0,0 +1,58 @@ +package com.r3.developers.samples.obligation.workflows + +import com.r3.developers.samples.obligation.states.IOUState +import net.corda.v5.application.flows.ClientRequestBody +import net.corda.v5.application.flows.ClientStartableFlow +import net.corda.v5.application.flows.CordaInject +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.ledger.utxo.UtxoLedgerService +import org.slf4j.LoggerFactory +import java.time.Instant +import java.util.* + +// A class to hold the deserialized arguments required to start the flow. +data class ListIOUFlowResults(val id: UUID,val amount: Int,val borrower: String,val lender: String,val paid: Int) + + +class ListIOUFlow: ClientStartableFlow { + + private companion object { + val log = LoggerFactory.getLogger(this::class.java.enclosingClass) + } + // Injects the JsonMarshallingService to read and populate JSON parameters. + @CordaInject + lateinit var jsonMarshallingService: JsonMarshallingService + + // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. + @CordaInject + lateinit var ledgerService: UtxoLedgerService + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + log.info("ListIOUFlow.call() called") + + + // Queries the VNode's vault for unconsumed states and converts the result to a serializable DTO. + val states = ledgerService.findUnconsumedStatesByExactType(IOUState::class.java,100, Instant.now()).results + val results = states.map { stateAndRef -> + ListIOUFlowResults( + stateAndRef.state.contractState.linearId, + stateAndRef.state.contractState.amount, + stateAndRef.state.contractState.borrower.toString(), + stateAndRef.state.contractState.lender.toString(), + stateAndRef.state.contractState.paid, + ) + } + // Uses the JsonMarshallingService's format() function to serialize the DTO to Json. + return jsonMarshallingService.format(results) + } +} +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "list-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.ListIOUFlow", + "requestBody": {} +} +*/ \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/sendAndRecieveTransactionFlow.kt b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/sendAndRecieveTransactionFlow.kt new file mode 100644 index 0000000..3c6e4d2 --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/workflows/src/main/kotlin/com/r3/developers/samples/obligation/workflows/sendAndRecieveTransactionFlow.kt @@ -0,0 +1,110 @@ +package com.r3.developers.samples.obligation.workflows + +import net.corda.v5.application.crypto.DigestService +import net.corda.v5.application.flows.* +import net.corda.v5.application.marshalling.JsonMarshallingService +import net.corda.v5.application.membership.MemberLookup +import net.corda.v5.application.messaging.FlowMessaging +import net.corda.v5.application.messaging.FlowSession +import net.corda.v5.base.annotations.Suspendable +import net.corda.v5.base.types.MemberX500Name +import net.corda.v5.ledger.utxo.StateRef +import net.corda.v5.ledger.utxo.UtxoLedgerService +import org.slf4j.LoggerFactory + +@InitiatingFlow(protocol = "utxo-transaction-transmission-protocol") +class sendAndRecieveTransactionFlow : ClientStartableFlow { + data class Request( + val stateRef: String, + val members: List, + val forceBackchain: Boolean = false + ) + + @CordaInject + lateinit var flowMessaging: FlowMessaging + + @CordaInject + lateinit var utxoLedgerService: UtxoLedgerService + + @CordaInject + lateinit var jsonMarshallingService: JsonMarshallingService + + @CordaInject + lateinit var memberLookup: MemberLookup + + @CordaInject + lateinit var digestService: DigestService + + private val log = LoggerFactory.getLogger(sendAndRecieveTransactionFlow::class.java) + + @Suspendable + override fun call(requestBody: ClientRequestBody): String { + val request = requestBody.getRequestBodyAs(jsonMarshallingService, Request::class.java) + + // Parse the state reference to obtain the transaction ID. + val transactionId = StateRef.parse(request.stateRef + ":0", digestService).transactionId + + // Retrieve the signed transaction from the ledger. + val transaction = requireNotNull(utxoLedgerService.findSignedTransaction(transactionId)) { + "Transaction is not found or verified." + } + + // Map the X500 names in the request to Member objects, ensuring each member exists. + val members = request.members.map { x500 -> + requireNotNull(memberLookup.lookup(MemberX500Name.parse(x500))) { + "Member $x500 does not exist in the membership group" + } + } + + // Initialize the sessions with the memebers that will be used to send the transaction. + val sessions = members.map { flowMessaging.initiateFlow(it.name) } + + // Send the transaction with or without backchain depending on the request. + try { + if (request.forceBackchain) { + utxoLedgerService.sendTransactionWithBackchain(transaction, sessions) + } else { + utxoLedgerService.sendTransaction(transaction, sessions) + } + } catch (e: Exception) { + // Log and rethrow any exceptions encountered during transaction sending. + log.warn("Sending transaction for $transactionId failed.", e) + throw e + } + + // Format and log the successful transaction response. + return jsonMarshallingService.format(transactionId.toString()).also { + log.info("SendTransaction is successful. Response: $it") + } + } + + + @InitiatedBy(protocol = "utxo-transaction-transmission-protocol") + class ReceiveTransactionFlow : ResponderFlow { + private val log = LoggerFactory.getLogger(ReceiveTransactionFlow::class.java) + + @CordaInject + lateinit var utxoLedgerService: UtxoLedgerService + + @Suspendable + override fun call(session: FlowSession) { + // Receive the transaction and log its details. + val transaction = utxoLedgerService.receiveTransaction(session) + log.info("Received transaction - ${transaction.id}") + } + } +} + +/* +RequestBody for triggering the flow via http-rpc: +{ + "clientRequestId": "sendAndRecieve-1", + "flowClassName": "com.r3.developers.samples.obligation.workflows.sendAndRecieveTransactionFlow", + "requestBody": { + "stateRef": "STATE REF ID HERE", + "members": ["CN=Charlie, OU=Test Dept, O=R3, L=London, C=GB"], + "forceBackchain": "false" + + } +} +*/ \ No newline at end of file diff --git a/kotlin-samples/sendAndRecieveTransaction/workflows/src/test/kotlin/com/r3/developers/samples/obligation/MyFirstFlowTest.kt b/kotlin-samples/sendAndRecieveTransaction/workflows/src/test/kotlin/com/r3/developers/samples/obligation/MyFirstFlowTest.kt new file mode 100644 index 0000000..dd513fb --- /dev/null +++ b/kotlin-samples/sendAndRecieveTransaction/workflows/src/test/kotlin/com/r3/developers/samples/obligation/MyFirstFlowTest.kt @@ -0,0 +1,45 @@ +package com.r3.developers.samples.obligation + +//import net.corda.simulator.RequestData +//import net.corda.simulator.Simulator +//import net.corda.v5.base.types.MemberX500Name +//import org.junit.jupiter.api.Test + +// Note: this simulator test has been commented out pending the merging of the UTXO code into the Gecko Branch. + + + +//class MyFirstFlowTest { +// +// // Names picked to match the corda network in config/static-network-config.json +// private val aliceX500 = MemberX500Name.parse("CN=Alice, OU=Test Dept, O=R3, L=London, C=GB") +// private val bobX500 = MemberX500Name.parse("CN=Bob, OU=Test Dept, O=R3, L=London, C=GB") +// +// @Test +// fun `test that MyFirstFLow returns correct message`() { +// +// // Instantiate an instance of the Simulator +// val simulator = Simulator() +// +// // Create Alice and Bob's virtual nodes, including the Class's of the flows which will be registered on each node. +// // We don't assign Bob's virtual node to a val because we don't need it for this particular test. +// val aliceVN = simulator.createVirtualNode(aliceX500, MyFirstFlow::class.java) +// simulator.createVirtualNode(bobX500, MyFirstFlowResponder::class.java) +// +// // Create an instance of the MyFirstFlowStartArgs which contains the request arguments for starting the flow +// val myFirstFlowStartArgs = MyFirstFlowStartArgs(bobX500) +// +// // Create a requestData object +// val requestData = RequestData.create( +// "request no 1", // A unique reference for the instance of the flow request +// MyFirstFlow::class.java, // The name of the flow class which is to be started +// myFirstFlowStartArgs // The object which contains the start arguments of the flow +// ) +// +// // Call the Flow on Alice's virtual node and capture the response from the flow +// val flowResponse = aliceVN.callFlow(requestData) +// +// // Check that the flow has returned the expected string +// assert(flowResponse == "Hello Alice, best wishes from Bob") +// } +//}