diff --git a/kotlin-samples/customeStateJSONandQuery/.ci/Jenkinsfile b/kotlin-samples/customStateJSONandQuery/.ci/Jenkinsfile similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/.ci/Jenkinsfile rename to kotlin-samples/customStateJSONandQuery/.ci/Jenkinsfile diff --git a/kotlin-samples/customeStateJSONandQuery/.ci/nightly/JenkinsfileSnykScan b/kotlin-samples/customStateJSONandQuery/.ci/nightly/JenkinsfileSnykScan similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/.ci/nightly/JenkinsfileSnykScan rename to kotlin-samples/customStateJSONandQuery/.ci/nightly/JenkinsfileSnykScan diff --git a/kotlin-samples/customeStateJSONandQuery/.gitignore b/kotlin-samples/customStateJSONandQuery/.gitignore similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/.gitignore rename to kotlin-samples/customStateJSONandQuery/.gitignore diff --git a/kotlin-samples/customeStateJSONandQuery/.run/runConfigurations/DebugCorDapp.run.xml b/kotlin-samples/customStateJSONandQuery/.run/runConfigurations/DebugCorDapp.run.xml similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/.run/runConfigurations/DebugCorDapp.run.xml rename to kotlin-samples/customStateJSONandQuery/.run/runConfigurations/DebugCorDapp.run.xml diff --git a/kotlin-samples/customeStateJSONandQuery/.snyk b/kotlin-samples/customStateJSONandQuery/.snyk similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/.snyk rename to kotlin-samples/customStateJSONandQuery/.snyk diff --git a/kotlin-samples/customeStateJSONandQuery/FlowManagementUI/Dockerfile b/kotlin-samples/customStateJSONandQuery/FlowManagementUI/Dockerfile similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/FlowManagementUI/Dockerfile rename to kotlin-samples/customStateJSONandQuery/FlowManagementUI/Dockerfile diff --git a/kotlin-samples/customeStateJSONandQuery/FlowManagementUI/README.md b/kotlin-samples/customStateJSONandQuery/FlowManagementUI/README.md similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/FlowManagementUI/README.md rename to kotlin-samples/customStateJSONandQuery/FlowManagementUI/README.md diff --git a/kotlin-samples/customeStateJSONandQuery/FlowManagementUI/app.py b/kotlin-samples/customStateJSONandQuery/FlowManagementUI/app.py similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/FlowManagementUI/app.py rename to kotlin-samples/customStateJSONandQuery/FlowManagementUI/app.py diff --git a/kotlin-samples/customeStateJSONandQuery/FlowManagementUI/requirements.txt b/kotlin-samples/customStateJSONandQuery/FlowManagementUI/requirements.txt similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/FlowManagementUI/requirements.txt rename to kotlin-samples/customStateJSONandQuery/FlowManagementUI/requirements.txt diff --git a/kotlin-samples/customeStateJSONandQuery/FlowManagementUI/static/Scripts/script.js b/kotlin-samples/customStateJSONandQuery/FlowManagementUI/static/Scripts/script.js similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/FlowManagementUI/static/Scripts/script.js rename to kotlin-samples/customStateJSONandQuery/FlowManagementUI/static/Scripts/script.js diff --git a/kotlin-samples/customeStateJSONandQuery/FlowManagementUI/static/css/main.css b/kotlin-samples/customStateJSONandQuery/FlowManagementUI/static/css/main.css similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/FlowManagementUI/static/css/main.css rename to kotlin-samples/customStateJSONandQuery/FlowManagementUI/static/css/main.css diff --git a/kotlin-samples/customeStateJSONandQuery/FlowManagementUI/templates/index.html b/kotlin-samples/customStateJSONandQuery/FlowManagementUI/templates/index.html similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/FlowManagementUI/templates/index.html rename to kotlin-samples/customStateJSONandQuery/FlowManagementUI/templates/index.html diff --git a/kotlin-samples/customStateJSONandQuery/README.md b/kotlin-samples/customStateJSONandQuery/README.md new file mode 100644 index 0000000..2c344c3 --- /dev/null +++ b/kotlin-samples/customStateJSONandQuery/README.md @@ -0,0 +1,112 @@ +# Custom StateJSON and Query Cordapp -kotlin (Corda v5.2) + +## This sample repository provides: + +A sample CorDapp that demonstrate the functionalities of storing custom stateJSON into the vNode's database and the functionalities of querying +specific states via the custom stateJSON. The functions are demo on the Chat app that are in our template app. +We will be seeing both Alice and Dave sending a chat entry to Bob, and we will query only Alice's chat entry via the custom +query. + +## Prerequisite +1. Java 17 +2. Corda-cli (v5.2), Download [here](https://github.com/corda/corda-runtime-os/releases/tag/release-5.2.0.0). You need to install Java 17 first. +3. Docker Desktop + +## 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 of the + 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 `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 + +## Flow Management Tool[Optional] +We had developed a simple GUI for you to interact with the cordapp. You can access the website by using https://localhost:5000 or https://127.0.0.1:5000. The Flow Management Tool will automatically connect with the CorDapp running locally from your Corda cluster. 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. You can easily trigger and query a Corda flow. + + + +![image](https://github.com/corda/cordapp-template-kotlin/assets/51169685/88e6568e-49b4-46a8-b1e1-34140bcf03a9) + + +## Running the sample app +We will use the Chat app to demonstrate the functionalities of custom StateJSON and custom queries. + +### Running the flows + +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 Chat Entry from Alice +Pick a VNode identity to initiate the chat, and get its short hash. (Let's pick Alice. Dont pick Bob because Bob is the person who we will have the chat with). + +Go to `POST /flow/{holdingidentityshorthash}`, enter the identity short hash(Alice's hash) and request body: +``` +{ + "clientRequestId": "create-1", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.CreateNewChatFlow", + "requestBody": { + "chatName":"Chat with Bob", + "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB", + "message": "Hello Bob" + } +} +``` + +After trigger the create-chat flow, hop to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and enter the short hash(Alice's hash) and clientrequestid to view the flow result + +#### Step 2: View the Custom StateJson in Database +By using database tool such DBeaver or similar app, we connect to the postgre Database. Then, we locate to the Alice's vNode. We select +`utxo_visible_transaction_output` table, and we should be able to see the transaction record of Alice. We can see one column is called `custom_representation`, and +it will hold the custom StateJSON of the chat state that Alice created for Bob. For example, +``` +{ + "net.corda.v5.ledger.utxo.ContractState":{ + "stateRef":"SHA-256D:3817854BD2DA35817360DFF00076DB9105DFF0F7B0824F4DF91F1BBA0E1039C4:0" + }, + "com.r3.developers.customStateJSONandQuery.states.ChatState":{ + "Id":"d47b10d2-f3bd-4571-8ed2-3ac651492874", + "chatName":"Chat with Bob", + "messageContent":"Hello Bob", + "messageContentFrom":"CN=Alice, OU=Test Dept, O=R3, L=London, C=GB" + } +} +``` + +#### Step 3: Create Chat Entry from Dave +In order to demonstrate the custom query functionality, we will need to create another chat entry to Bob from a different person. We will choose Dave, +``` +{ + "clientRequestId": "create-1", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.CreateNewChatFlow", + "requestBody": { + "chatName":"Chat with Bob from Dave", + "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB", + "message": "Hello Bob from Dave" + } +} +``` +Notice, the `chatName` and `message` are annotated with "from Dave". + +#### Step 4: Perform Custom Query at Bob +Now, we will perform the custom query, we will select Bob to trigger the flow with the following requestBody. Note that the `ListChatsByCustomQueryFlow` is hard coded +to query only Alice's chat entries. +``` +{ + "clientRequestId": "customlist-1", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.ListChatsByCustomQueryFlow", + "requestBody": {} +} +``` + + +#### Step 5: Perform Generic Query at Bob +As a contrast, we will perform a generic query at Bob to query all the chat entries that Bob receives. This will print out all the chats that Bob has, including the message from Dave. +``` +{ + "clientRequestId": "list-1", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.ListChatsFlow", + "requestBody": {} +} +``` + +Thus, we have concluded a full run through of the Custom StateJson and Query sample app. diff --git a/kotlin-samples/customeStateJSONandQuery/build.gradle b/kotlin-samples/customStateJSONandQuery/build.gradle similarity index 98% rename from kotlin-samples/customeStateJSONandQuery/build.gradle rename to kotlin-samples/customStateJSONandQuery/build.gradle index e9af621..391ec81 100644 --- a/kotlin-samples/customeStateJSONandQuery/build.gradle +++ b/kotlin-samples/customStateJSONandQuery/build.gradle @@ -12,7 +12,7 @@ plugins { } allprojects { - group 'com.r3.developers.cordapptemplate' + group 'com.r3.developers.customStateJSONandQuery' version '1.0-SNAPSHOT' def javaVersion = VERSION_17 diff --git a/kotlin-samples/customeStateJSONandQuery/config/combined-worker-compose.yaml b/kotlin-samples/customStateJSONandQuery/config/combined-worker-compose.yaml similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/config/combined-worker-compose.yaml rename to kotlin-samples/customStateJSONandQuery/config/combined-worker-compose.yaml diff --git a/kotlin-samples/customeStateJSONandQuery/config/gradle-plugin-default-key.pem b/kotlin-samples/customStateJSONandQuery/config/gradle-plugin-default-key.pem similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/config/gradle-plugin-default-key.pem rename to kotlin-samples/customStateJSONandQuery/config/gradle-plugin-default-key.pem diff --git a/kotlin-samples/customeStateJSONandQuery/config/log4j2.xml b/kotlin-samples/customStateJSONandQuery/config/log4j2.xml similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/config/log4j2.xml rename to kotlin-samples/customStateJSONandQuery/config/log4j2.xml diff --git a/kotlin-samples/customeStateJSONandQuery/config/r3-ca-key.pem b/kotlin-samples/customStateJSONandQuery/config/r3-ca-key.pem similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/config/r3-ca-key.pem rename to kotlin-samples/customStateJSONandQuery/config/r3-ca-key.pem diff --git a/kotlin-samples/customeStateJSONandQuery/config/static-network-config.json b/kotlin-samples/customStateJSONandQuery/config/static-network-config.json similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/config/static-network-config.json rename to kotlin-samples/customStateJSONandQuery/config/static-network-config.json diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/build.gradle b/kotlin-samples/customStateJSONandQuery/contracts/build.gradle similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/contracts/build.gradle rename to kotlin-samples/customStateJSONandQuery/contracts/build.gradle diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/contracts/ChatContract.kt b/kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/contracts/ChatContract.kt similarity index 96% rename from kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/contracts/ChatContract.kt rename to kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/contracts/ChatContract.kt index b053018..1d075f2 100644 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/contracts/ChatContract.kt +++ b/kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/contracts/ChatContract.kt @@ -1,6 +1,6 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.contracts +package com.r3.developers.customStateJSONandQuery.contracts -import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState +import com.r3.developers.customStateJSONandQuery.states.ChatState import net.corda.v5.base.exceptions.CordaRuntimeException import net.corda.v5.ledger.utxo.Command import net.corda.v5.ledger.utxo.Contract diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/states/ChatJsonFactory.kt b/kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/states/ChatJsonFactory.kt similarity index 95% rename from kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/states/ChatJsonFactory.kt rename to kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/states/ChatJsonFactory.kt index 40e561b..d24fab8 100644 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/states/ChatJsonFactory.kt +++ b/kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/states/ChatJsonFactory.kt @@ -1,4 +1,4 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.states +package com.r3.developers.customStateJSONandQuery.states import net.corda.v5.application.marshalling.JsonMarshallingService import net.corda.v5.ledger.utxo.query.json.ContractStateVaultJsonFactory diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/states/ChatState.kt b/kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/states/ChatState.kt similarity index 89% rename from kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/states/ChatState.kt rename to kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/states/ChatState.kt index 12c6c44..48cd923 100644 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/states/ChatState.kt +++ b/kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/states/ChatState.kt @@ -1,6 +1,6 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.states +package com.r3.developers.customStateJSONandQuery.states -import com.r3.developers.cordapptemplate.customStateJSONandQuery.contracts.ChatContract +import com.r3.developers.customStateJSONandQuery.contracts.ChatContract import net.corda.v5.base.types.MemberX500Name import net.corda.v5.ledger.utxo.BelongsToContract import net.corda.v5.ledger.utxo.ContractState diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/states/CustomChatQuery.kt b/kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/states/CustomChatQuery.kt similarity index 73% rename from kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/states/CustomChatQuery.kt rename to kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/states/CustomChatQuery.kt index dec500a..07775a5 100644 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/states/CustomChatQuery.kt +++ b/kotlin-samples/customStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/customStateJSONandQuery/states/CustomChatQuery.kt @@ -1,4 +1,4 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.states +package com.r3.developers.customStateJSONandQuery.states import net.corda.v5.ledger.utxo.query.VaultNamedQueryFactory import net.corda.v5.ledger.utxo.query.registration.VaultNamedQueryBuilderFactory @@ -7,13 +7,13 @@ class ChatCustomQueryFactory : VaultNamedQueryFactory { override fun create(vaultNamedQueryBuilderFactory: VaultNamedQueryBuilderFactory) { vaultNamedQueryBuilderFactory.create("GET_ALL_MSG") .whereJson( - "WHERE visible_states.custom_representation ? 'com.r3.developers.cordapptemplate.utxoexample.states.ChatState' " + "WHERE visible_states.custom_representation ? 'com.r3.developers.customStateJSONandQuery.states.ChatState' " ) .register() vaultNamedQueryBuilderFactory.create("GET_MSG_FROM") .whereJson( - "WHERE visible_states.custom_representation -> 'com.r3.developers.cordapptemplate.utxoexample.states.ChatState' ->> 'messageContentFrom' = :nameOfSender" + "WHERE visible_states.custom_representation -> 'com.r3.developers.customStateJSONandQuery.states.ChatState' ->> 'messageContentFrom' = :nameOfSender" ) .register() } diff --git a/kotlin-samples/customeStateJSONandQuery/gradle.properties b/kotlin-samples/customStateJSONandQuery/gradle.properties similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/gradle.properties rename to kotlin-samples/customStateJSONandQuery/gradle.properties diff --git a/kotlin-samples/customeStateJSONandQuery/gradle/wrapper/gradle-wrapper.jar b/kotlin-samples/customStateJSONandQuery/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/gradle/wrapper/gradle-wrapper.jar rename to kotlin-samples/customStateJSONandQuery/gradle/wrapper/gradle-wrapper.jar diff --git a/kotlin-samples/customeStateJSONandQuery/gradle/wrapper/gradle-wrapper.properties b/kotlin-samples/customStateJSONandQuery/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/gradle/wrapper/gradle-wrapper.properties rename to kotlin-samples/customStateJSONandQuery/gradle/wrapper/gradle-wrapper.properties diff --git a/kotlin-samples/customeStateJSONandQuery/gradlew b/kotlin-samples/customStateJSONandQuery/gradlew old mode 100644 new mode 100755 similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/gradlew rename to kotlin-samples/customStateJSONandQuery/gradlew diff --git a/kotlin-samples/customeStateJSONandQuery/gradlew.bat b/kotlin-samples/customStateJSONandQuery/gradlew.bat similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/gradlew.bat rename to kotlin-samples/customStateJSONandQuery/gradlew.bat diff --git a/kotlin-samples/customeStateJSONandQuery/settings.gradle b/kotlin-samples/customStateJSONandQuery/settings.gradle similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/settings.gradle rename to kotlin-samples/customStateJSONandQuery/settings.gradle diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/build.gradle b/kotlin-samples/customStateJSONandQuery/workflows/build.gradle similarity index 100% rename from kotlin-samples/customeStateJSONandQuery/workflows/build.gradle rename to kotlin-samples/customStateJSONandQuery/workflows/build.gradle diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/CreateNewChatFlow.kt b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/CreateNewChatFlow.kt similarity index 92% rename from kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/CreateNewChatFlow.kt rename to kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/CreateNewChatFlow.kt index ec56869..a74e5bf 100644 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/CreateNewChatFlow.kt +++ b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/CreateNewChatFlow.kt @@ -1,7 +1,7 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows +package com.r3.developers.customStateJSONandQuery.workflows -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.contracts.ChatContract -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState +import com.r3.developers.customStateJSONandQuery.contracts.ChatContract +import com.r3.developers.customStateJSONandQuery.states.ChatState import net.corda.v5.application.flows.* import net.corda.v5.application.marshalling.JsonMarshallingService import net.corda.v5.application.membership.MemberLookup @@ -102,7 +102,7 @@ class CreateNewChatFlow: ClientStartableFlow { RequestBody for triggering the flow via REST: { "clientRequestId": "create-1", - "flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.CreateNewChatFlow", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.CreateNewChatFlow", "requestBody": { "chatName":"Chat with Bob", "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB", diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/FinalizeChatSubFlow.kt b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/FinalizeChatSubFlow.kt similarity index 96% rename from kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/FinalizeChatSubFlow.kt rename to kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/FinalizeChatSubFlow.kt index 642fed0..1844802 100644 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/FinalizeChatSubFlow.kt +++ b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/FinalizeChatSubFlow.kt @@ -1,6 +1,6 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows +package com.r3.developers.customStateJSONandQuery.workflows -import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState +import com.r3.developers.customStateJSONandQuery.states.ChatState import net.corda.v5.application.flows.* import net.corda.v5.application.messaging.FlowMessaging import net.corda.v5.application.messaging.FlowSession diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/GetChatFlow.kt b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/GetChatFlow.kt similarity index 95% rename from kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/GetChatFlow.kt rename to kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/GetChatFlow.kt index 0f69a47..638673c 100644 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/GetChatFlow.kt +++ b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/GetChatFlow.kt @@ -1,6 +1,6 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows +package com.r3.developers.customStateJSONandQuery.workflows -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState +import com.r3.developers.customStateJSONandQuery.states.ChatState import net.corda.v5.application.flows.ClientRequestBody import net.corda.v5.application.flows.ClientStartableFlow import net.corda.v5.application.flows.CordaInject @@ -108,7 +108,7 @@ class GetChatFlow: ClientStartableFlow { RequestBody for triggering the flow via REST: { "clientRequestId": "get-1", - "flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.GetChatFlow", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.GetChatFlow", "requestBody": { "id":"** fill in id **", "numberOfRecords":"4" diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/ListChatsByCustomQueryFlow.kt b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/ListChatsByCustomQueryFlow.kt similarity index 88% rename from kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/ListChatsByCustomQueryFlow.kt rename to kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/ListChatsByCustomQueryFlow.kt index a740c33..d09254d 100644 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/ListChatsByCustomQueryFlow.kt +++ b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/ListChatsByCustomQueryFlow.kt @@ -1,6 +1,6 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows +package com.r3.developers.customStateJSONandQuery.workflows -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState +import com.r3.developers.customStateJSONandQuery.states.ChatState import net.corda.v5.application.flows.ClientRequestBody import net.corda.v5.application.flows.ClientStartableFlow import net.corda.v5.application.flows.CordaInject @@ -37,7 +37,7 @@ class ListChatsByCustomQueryFlow : ClientStartableFlow { .execute() //from custom query to states, we can operate - val resultState = resultSet.results.map {it.state.contractState as ChatState} + val resultState = resultSet.results.map {it.state.contractState as ChatState } //from the states -> human-readable. val results = resultState.map { @@ -60,7 +60,7 @@ class ListChatsByCustomQueryFlow : ClientStartableFlow { RequestBody for triggering the flow via REST: { "clientRequestId": "customlist-1", - "flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.ListChatsByCustomQueryFlow", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.ListChatsByCustomQueryFlow", "requestBody": {} } */ diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/ListChatsFlow.kt b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/ListChatsFlow.kt similarity index 90% rename from kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/ListChatsFlow.kt rename to kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/ListChatsFlow.kt index 7721411..0e1756d 100644 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/ListChatsFlow.kt +++ b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/ListChatsFlow.kt @@ -1,6 +1,6 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows +package com.r3.developers.customStateJSONandQuery.workflows -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState +import com.r3.developers.customStateJSONandQuery.states.ChatState import net.corda.v5.application.flows.ClientRequestBody import net.corda.v5.application.flows.ClientStartableFlow import net.corda.v5.application.flows.CordaInject @@ -56,7 +56,7 @@ class ListChatsFlow : ClientStartableFlow { RequestBody for triggering the flow via REST: { "clientRequestId": "list-1", - "flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.ListChatsFlow", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.ListChatsFlow", "requestBody": {} } */ diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/ResponderValidations.kt b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/ResponderValidations.kt similarity index 86% rename from kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/ResponderValidations.kt rename to kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/ResponderValidations.kt index d9b58b7..ec68862 100644 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/ResponderValidations.kt +++ b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/ResponderValidations.kt @@ -1,6 +1,6 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows +package com.r3.developers.customStateJSONandQuery.workflows -import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState +import com.r3.developers.customStateJSONandQuery.states.ChatState import net.corda.v5.base.annotations.Suspendable import net.corda.v5.base.exceptions.CordaRuntimeException import net.corda.v5.base.types.MemberX500Name diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/TestContractFlow.kt b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/TestContractFlow.kt similarity index 98% rename from kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/TestContractFlow.kt rename to kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/TestContractFlow.kt index 413be4c..89e5d92 100644 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/TestContractFlow.kt +++ b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/TestContractFlow.kt @@ -1,7 +1,7 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows +package com.r3.developers.customStateJSONandQuery.workflows -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.contracts.ChatContract -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState +import com.r3.developers.customStateJSONandQuery.contracts.ChatContract +import com.r3.developers.customStateJSONandQuery.states.ChatState import net.corda.v5.application.flows.* import net.corda.v5.application.marshalling.JsonMarshallingService import net.corda.v5.application.membership.MemberLookup @@ -483,7 +483,7 @@ class TestContractFlow: ClientStartableFlow { /* { "clientRequestId": "dummy-1", - "flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.TestContractFlow", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.TestContractFlow", "requestBody": { "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB" } diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/UpdateChatFlow.kt b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/UpdateChatFlow.kt similarity index 92% rename from kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/UpdateChatFlow.kt rename to kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/UpdateChatFlow.kt index a4b520d..8903f34 100644 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/UpdateChatFlow.kt +++ b/kotlin-samples/customStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/customStateJSONandQuery/workflows/UpdateChatFlow.kt @@ -1,7 +1,7 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows +package com.r3.developers.customStateJSONandQuery.workflows -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.contracts.ChatContract -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState +import com.r3.developers.customStateJSONandQuery.contracts.ChatContract +import com.r3.developers.customStateJSONandQuery.states.ChatState import net.corda.v5.application.flows.* import net.corda.v5.application.marshalling.JsonMarshallingService import net.corda.v5.application.membership.MemberLookup @@ -100,7 +100,7 @@ class UpdateChatFlow: ClientStartableFlow { RequestBody for triggering the flow via REST: { "clientRequestId": "update-2", - "flowClassName": "com.r3.developers.cordapptemplate.utxoexample.workflows.UpdateChatFlow", + "flowClassName": "com.r3.developers.customStateJSONandQuery.workflows.UpdateChatFlow", "requestBody": { "id":"** fill in id **", "message": "How are you today?" diff --git a/kotlin-samples/customeStateJSONandQuery/README.md b/kotlin-samples/customeStateJSONandQuery/README.md deleted file mode 100644 index c73edcb..0000000 --- a/kotlin-samples/customeStateJSONandQuery/README.md +++ /dev/null @@ -1,122 +0,0 @@ -# cordapp-template-kotlin (Corda v5.2) - -## This template repository provides: - -- A pre-setup Cordapp Project which you can use as a starting point to develop your own prototypes. - -- A base Gradle configuration which brings in the dependencies you need to write and test a Corda 5 Cordapp. - -- A set of Gradle helper tasks, provided by the [Corda runtime gradle plugin](https://github.com/corda/corda-runtime-os/tree/release/os/5.2/tools/corda-runtime-gradle-plugin#readme), which speed up and simplify the development and deployment process. - -- Debug configuration for debugging a local Corda cluster. - -- The MyFirstFlow code which forms the basis of this getting started documentation, this is located in package com.r3.developers.cordapptemplate.flowexample - -- A UTXO example in package com.r3.developers.cordapptemplate.customStateJSONandQuery packages - -- Ability to configure the Members of the Local Corda Network. - -To find out how to use the template, please refer to the *CorDapp Template* subsection within the *Developing Applications* section in the latest Corda 5 documentation at https://docs.r3.com/ - -## Prerequisite -1. Java 17 -2. Corda-cli (v5.2), Download [here](https://github.com/corda/corda-runtime-os/releases/tag/release-5.2.0.0). You need to install Java 17 first. -3. Docker Desktop - -## 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 of the - 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 `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 - -## Flow Management Tool[Optional] -We had developed a simple GUI for you to interact with the cordapp. You can access the website by using https://localhost:5000 or https://127.0.0.1:5000. The Flow Management Tool will automatically connect with the CorDapp running locally from your Corda cluster. 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. You can easily trigger and query a Corda flow. - - - -![image](https://github.com/corda/cordapp-template-kotlin/assets/51169685/88e6568e-49b4-46a8-b1e1-34140bcf03a9) - - -## Running the Chat app -We have built a simple one to one chat app to demo some functionalities of the next gen Corda platform. - -In this app you can: -1. Create a new chat with a counterparty. `CreateNewChatFlow` -2. List out the chat entries you had. `ListChatsFlow` -3. Individually query out the history of one chat entry. `GetChatFlowArgs` -4. Continue chatting within the chat entry with the counterparty. `UpdateChatFlow` - - - - -### Running the chat 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 Chat Entry -Pick a VNode identity to initiate the chat, and get its short hash. (Let's pick Alice. Dont pick Bob because Bob is the person who we will have the chat with). - -Go to `POST /flow/{holdingidentityshorthash}`, enter the identity short hash(Alice's hash) and request body: -``` -{ - "clientRequestId": "create-1", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.CreateNewChatFlow", - "requestBody": { - "chatName":"Chat with Bob", - "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB", - "message": "Hello Bob" - } -} -``` - -After trigger the create-chat flow, hop to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and enter the short hash(Alice's hash) and clientrequestid to view the flow result - -#### Step 2: List the chat -In order to continue the chat, we would need the chat ID. This step will bring out all the chat entries this entity (Alice) has. -Go to `POST /flow/{holdingidentityshorthash}`, enter the identity short hash(Alice's hash) and request body: -``` -{ - "clientRequestId": "list-1", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.ListChatsFlow", - "requestBody": {} -} -``` -After trigger the list-chats flow, again, we need to hop to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and check the result. As the screenshot shows, in the response body, -we will see a list of chat entries, but it currently only has one entry. And we can see the id of the chat entry. Let's record that id. - - -#### Step 3: Continue the chat with `UpdateChatFlow` -In this step, we will continue the chat between Alice and Bob. -Goto `POST /flow/{holdingidentityshorthash}`, enter the identity short hash and request body. Note that here we can have either Alice or Bob's short hash. If you enter Alice's hash, -this message will be recorded as a message from Alice, vice versa. And the id field is the chat entry id we got from the previous step. -``` -{ - "clientRequestId": "update-1", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.UpdateChatFlow", - "requestBody": { - "id":" ** fill in id **", - "message": "How are you today?" - } -} -``` -And as for the result of this flow, go to `GET /flow/{holdingidentityshorthash}/{clientrequestid}` and enter the required fields. - -#### Step 4: See the whole chat history of one chat entry -After a few back and forth of the messaging, you can view entire chat history by calling GetChatFlow. - -``` -{ - "clientRequestId": "get-1", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.GetChatFlow", - "requestBody": { - "id":" ** fill in id **", - "numberOfRecords":"4" - } -} -``` -And as for the result, you need to go to the Get API again and enter the short hash and client request ID. - -Thus, we have concluded a full run through of the chat app. diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/contracts/AppleCommands.kt b/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/contracts/AppleCommands.kt deleted file mode 100644 index d6eb117..0000000 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/contracts/AppleCommands.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.r3.developers.apples.contracts - -import net.corda.v5.ledger.utxo.Command - -interface AppleCommands : Command { - class Issue : AppleCommands - class Redeem : AppleCommands - class PackBasket : AppleCommands -} \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/contracts/AppleStampContract.kt b/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/contracts/AppleStampContract.kt deleted file mode 100644 index 301a211..0000000 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/contracts/AppleStampContract.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.r3.developers.apples.contracts - -import com.r3.developers.apples.states.AppleStamp -import net.corda.v5.ledger.utxo.Contract -import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction - -class AppleStampContract : Contract { - override fun verify(transaction: UtxoLedgerTransaction) { - // Extract the command from the transaction - // Verify the transaction according to the intention of the transaction - when (val command = transaction.commands.first()) { - is AppleCommands.Issue -> { - val output = transaction.getOutputStates(AppleStamp::class.java).first() - require(transaction.outputContractStates.size == 1) { - "This transaction should only have one AppleStamp state as output" - } - require(output.stampDesc.isNotBlank()) { - "The output AppleStamp state should have clear description of the type of redeemable goods" - } - } - is AppleCommands.Redeem -> { - // Transaction verification will happen in BasketOfApplesContract - } - else -> { - // Unrecognised Command type - throw IllegalArgumentException("Incorrect type of AppleStamp commands: ${command::class.java.name}") - } - } - } - -} \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/contracts/BasketOfApplesContract.kt b/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/contracts/BasketOfApplesContract.kt deleted file mode 100644 index 28bf490..0000000 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/contracts/BasketOfApplesContract.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.r3.developers.apples.contracts - -import com.r3.developers.apples.states.AppleStamp -import com.r3.developers.apples.states.BasketOfApples -import net.corda.v5.ledger.utxo.Contract -import net.corda.v5.ledger.utxo.transaction.UtxoLedgerTransaction - -class BasketOfApplesContract : Contract { - - override fun verify(transaction: UtxoLedgerTransaction) { - // Extract the command from the transaction - when (val command = transaction.commands.first()) { - is AppleCommands.PackBasket -> { - // Retrieve the output state of the transaction - val output = transaction.getOutputStates(BasketOfApples::class.java).first() - require(transaction.outputContractStates.size == 1) { - "This transaction should only output one BasketOfApples state" - } - require(output.description.isNotBlank()) { - "The output BasketOfApples state should have clear description of Apple product" - } - require(output.weight > 0) { - "The output BasketOfApples state should have non zero weight" - } - } - is AppleCommands.Redeem -> { - require(transaction.inputContractStates.size == 2) { - "This transaction should consume two states" - } - - // Retrieve the inputs to this transaction, which should be exactly one AppleStamp - // and one BasketOfApples - val stampInputs = transaction.getInputStates(AppleStamp::class.java) - val basketInputs = transaction.getInputStates(BasketOfApples::class.java) - - require(stampInputs.isNotEmpty() && basketInputs.isNotEmpty()) { - "This transaction should have exactly one AppleStamp and one BasketOfApples input state" - } - require(stampInputs.single().issuer == basketInputs.single().farm) { - "The issuer of the Apple stamp should be the producing farm of this basket of apple" - } - require(basketInputs.single().weight > 0) { - "The basket of apple has to weigh more than 0" - } - } - else -> { - throw IllegalArgumentException("Incorrect type of BasketOfApples commands: ${command::class.java.name}") - } - } - } -} \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/states/AppleStamp.kt b/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/states/AppleStamp.kt deleted file mode 100644 index 3db4b6a..0000000 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/states/AppleStamp.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.r3.developers.apples.states - -import com.r3.developers.apples.contracts.AppleStampContract -import net.corda.v5.ledger.utxo.BelongsToContract -import net.corda.v5.ledger.utxo.ContractState -import java.security.PublicKey -import java.util.* - -@BelongsToContract(AppleStampContract::class) -class AppleStamp( - val id: UUID, - val stampDesc: String, - val issuer: PublicKey, - val holder: PublicKey, - private val participants: List -) : ContractState { - override fun getParticipants(): List = participants - -} \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/states/BasketOfApples.kt b/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/states/BasketOfApples.kt deleted file mode 100644 index d6f82a4..0000000 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/apples/states/BasketOfApples.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.r3.developers.apples.states - -import com.r3.developers.apples.contracts.BasketOfApplesContract -import net.corda.v5.ledger.utxo.BelongsToContract -import net.corda.v5.ledger.utxo.ContractState -import java.security.PublicKey - -@BelongsToContract(BasketOfApplesContract::class) -class BasketOfApples( - val description: String, - val farm: PublicKey, - val owner: PublicKey, - val weight: Int, - private val participants: List -) : ContractState { - override fun getParticipants(): List = participants - - fun changeOwner(buyer: PublicKey): BasketOfApples { - val participants = listOf(farm, buyer) - return BasketOfApples(description, farm, buyer, weight, participants) - } -} \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/states/CustomChatQuery.kt b/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/states/CustomChatQuery.kt deleted file mode 100644 index e214bd2..0000000 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/states/CustomChatQuery.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.states - -import net.corda.v5.ledger.utxo.query.VaultNamedQueryFactory -import net.corda.v5.ledger.utxo.query.registration.VaultNamedQueryBuilderFactory - -class ChatCustomQueryFactory : VaultNamedQueryFactory { - override fun create(vaultNamedQueryBuilderFactory: VaultNamedQueryBuilderFactory) { - vaultNamedQueryBuilderFactory.create("GET_ALL_MSG") - .whereJson( - "WHERE visible_states.custom_representation ? 'com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState' " - ) - .register() - - vaultNamedQueryBuilderFactory.create("GET_MSG_FROM") - .whereJson( - "WHERE visible_states.custom_representation -> 'com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState' ->> 'messageContentFrom' = :nameOfSender" - ) - .register() - } -} \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/contracts/ChatContract.kt b/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/contracts/ChatContract.kt deleted file mode 100644 index dc2e192..0000000 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/contracts/ChatContract.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.contracts - -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState -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 ChatContract: Contract { - - // Use an internal scoped constant to hold the error messages - // This allows the tests to use them, meaning if they are updated you won't need to fix tests just because the wording was updated - internal companion object { - - const val REQUIRE_SINGLE_COMMAND = "Requires a single command." - const val UNKNOWN_COMMAND = "Command not allowed." - const val OUTPUT_STATE_SHOULD_ONLY_HAVE_TWO_PARTICIPANTS = "The output state should have two and only two participants." - const val TRANSACTION_SHOULD_BE_SIGNED_BY_ALL_PARTICIPANTS = "The transaction should have been signed by both participants." - - const val CREATE_COMMAND_SHOULD_HAVE_NO_INPUT_STATES = "When command is Create there should be no input states." - const val CREATE_COMMAND_SHOULD_HAVE_ONLY_ONE_OUTPUT_STATE = "When command is Create there should be one and only one output state." - - const val UPDATE_COMMAND_SHOULD_HAVE_ONLY_ONE_INPUT_STATE = "When command is Update there should be one and only one input state." - const val UPDATE_COMMAND_SHOULD_HAVE_ONLY_ONE_OUTPUT_STATE = "When command is Update there should be one and only one output state." - const val UPDATE_COMMAND_ID_SHOULD_NOT_CHANGE = "When command is Update id must not change." - const val UPDATE_COMMAND_CHATNAME_SHOULD_NOT_CHANGE = "When command is Update chatName must not change." - const val UPDATE_COMMAND_PARTICIPANTS_SHOULD_NOT_CHANGE = "When command is Update participants must not change." - } - - // Command Class used to indicate that the transaction should start a new chat. - class Create: Command - // Command Class used to indicate that the transaction should append a new ChatState to an existing chat. - class Update: Command - - // verify() function is used to apply contract rules to the transaction. - override fun verify(transaction: UtxoLedgerTransaction) { - - // Ensures that there is only one command in the transaction - val command = transaction.commands.singleOrNull() ?: throw CordaRuntimeException(REQUIRE_SINGLE_COMMAND) - - // Applies a universal constraint (applies to all transactions irrespective of command) - OUTPUT_STATE_SHOULD_ONLY_HAVE_TWO_PARTICIPANTS using { - val output = transaction.outputContractStates.first() as ChatState - output.participants.size== 2 - } - - TRANSACTION_SHOULD_BE_SIGNED_BY_ALL_PARTICIPANTS using { - val output = transaction.outputContractStates.first() as ChatState - transaction.signatories.containsAll(output.participants) - } - - // Switches case based on the command - when(command) { - // Rules applied only to transactions with the Create Command. - is Create -> { - CREATE_COMMAND_SHOULD_HAVE_NO_INPUT_STATES using (transaction.inputContractStates.isEmpty()) - CREATE_COMMAND_SHOULD_HAVE_ONLY_ONE_OUTPUT_STATE using (transaction.outputContractStates.size == 1) - } - // Rules applied only to transactions with the Update Command. - is Update -> { - UPDATE_COMMAND_SHOULD_HAVE_ONLY_ONE_INPUT_STATE using (transaction.inputContractStates.size == 1) - UPDATE_COMMAND_SHOULD_HAVE_ONLY_ONE_OUTPUT_STATE using (transaction.outputContractStates.size == 1) - - val input = transaction.inputContractStates.single() as ChatState - val output = transaction.outputContractStates.single() as ChatState - UPDATE_COMMAND_ID_SHOULD_NOT_CHANGE using (input.id == output.id) - UPDATE_COMMAND_CHATNAME_SHOULD_NOT_CHANGE using (input.chatName == output.chatName) - UPDATE_COMMAND_PARTICIPANTS_SHOULD_NOT_CHANGE using ( - input.participants.toSet().intersect(output.participants.toSet()).size == 2) - } - else -> { - throw CordaRuntimeException(UNKNOWN_COMMAND) - } - } - } - - // 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/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/states/ChatJsonFactory.kt b/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/states/ChatJsonFactory.kt deleted file mode 100644 index ff801b1..0000000 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/states/ChatJsonFactory.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.states - -import net.corda.v5.application.marshalling.JsonMarshallingService -import net.corda.v5.ledger.utxo.query.json.ContractStateVaultJsonFactory - -// This class represents a custom JSON factory for serializing/deserializing ChatState objects to JSON format after each use. -class ChatJsonFactory : ContractStateVaultJsonFactory { - - // This function specifies the type of state this factory is responsible for. - override fun getStateType(): Class = ChatState::class.java - - // Companion object containing constants used for JSON key names. - companion object { - const val ID = "Id" - const val CHATNAME = "chatName" - const val MESSAGE = "messageContent" - const val MESSAGEFROM = "messageContentFrom" - } - - // This function creates a JSON representation of a ChatState object. - override fun create(state: ChatState, jsonMarshallingService: JsonMarshallingService): String { - - // Constructs a map representing the ChatState object using the provided constants. - val jsonMap = mapOf( - Pair(ID, state.id), - Pair(CHATNAME, state.chatName), - Pair(MESSAGE, state.message), - Pair(MESSAGEFROM, state.messageFrom) - ) - - // Uses the JsonMarshallingService's format() function to serialize the map to JSON. - return jsonMarshallingService.format(jsonMap) - } -} diff --git a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/states/ChatState.kt b/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/states/ChatState.kt deleted file mode 100644 index 53d7aef..0000000 --- a/kotlin-samples/customeStateJSONandQuery/contracts/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/states/ChatState.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.states - -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.contracts.ChatContract -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.* - - -// The ChatState represents data stored on ledger. A chat consists of a linear series of messages between two -// participants and is represented by a UUID. Any given pair of participants can have multiple chats -// Each ChatState stores one message between the two participants in the chat. The backchain of ChatStates -// represents the history of the chat. - -@BelongsToContract(ChatContract::class) -data class ChatState( - // Unique identifier for the chat. - val id : UUID = UUID.randomUUID(), - // Non-unique name for the chat. - val chatName: String, - // The MemberX500Name of the participant who sent the message. - val messageFrom: MemberX500Name, - // The message - val message: String, - // The participants to the chat, represented by their public key. - private val participants: List) : ContractState { - - override fun getParticipants(): List { - return participants - } - - // Helper function to create a new ChatState from the previous (input) ChatState. - fun updateMessage(messageFrom: MemberX500Name, message: String) = - copy(messageFrom = messageFrom, message = message) -} - diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/CreateAndIssueAppleStampFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/CreateAndIssueAppleStampFlow.kt deleted file mode 100644 index 8f5538d..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/CreateAndIssueAppleStampFlow.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.r3.developers.apples.workflows - -import com.r3.developers.apples.contracts.AppleCommands -import com.r3.developers.apples.states.AppleStamp -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.InitiatingFlow -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.base.annotations.Suspendable -import net.corda.v5.base.types.MemberX500Name -import net.corda.v5.ledger.common.NotaryLookup -import net.corda.v5.ledger.utxo.UtxoLedgerService -import java.time.Instant -import java.time.temporal.ChronoUnit -import java.util.UUID - -@InitiatingFlow(protocol = "create-and-issue-apple-stamp") -class CreateAndIssueAppleStampFlow : ClientStartableFlow { - - internal data class CreateAndIssueAppleStampRequest( - val stampDescription: String, - val holder: MemberX500Name, - val notary: MemberX500Name - ) - - @CordaInject - lateinit var flowMessaging: FlowMessaging - - @CordaInject - lateinit var jsonMarshallingService: JsonMarshallingService - - @CordaInject - lateinit var memberLookup: MemberLookup - - @CordaInject - lateinit var notaryLookup: NotaryLookup - - @CordaInject - lateinit var utxoLedgerService: UtxoLedgerService - - @Suspendable - override fun call(requestBody: ClientRequestBody): String { - val request = requestBody.getRequestBodyAs( - jsonMarshallingService, - CreateAndIssueAppleStampRequest::class.java) - val stampDescription = request.stampDescription - val holderName = request.holder - - val notaryInfo = notaryLookup.lookup(request.notary) - ?: throw IllegalArgumentException("Notary ${request.notary} not found") - - val issuer = memberLookup.myInfo().ledgerKeys.first() - val holder = memberLookup.lookup(holderName)?.ledgerKeys?.first() - ?: throw IllegalArgumentException("The holder $holderName does not exist within the network") - - // Building the output AppleStamp state - val newStamp = AppleStamp( - id = UUID.randomUUID(), - stampDesc = stampDescription, - issuer = issuer, - holder = holder, - participants = listOf(issuer, holder) - ) - - val transaction = utxoLedgerService.createTransactionBuilder() - .setNotary(notaryInfo.name) - .addOutputState(newStamp) - .addCommand(AppleCommands.Issue()) - .setTimeWindowUntil(Instant.now().plus(1, ChronoUnit.DAYS)) - .addSignatories(listOf(issuer, holder)) - .toSignedTransaction() - - val session = flowMessaging.initiateFlow(holderName) - - return try { - // Send the transaction and state to the counterparty and let them sign it - // Then notarise and record the transaction in both parties' vaults. - utxoLedgerService.finalize(transaction, listOf(session)) - newStamp.id.toString() - } catch (e: Exception) { - "Flow failed, message: ${e.message}" - } - } -} diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/CreateAndIssueAppleStampResponderFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/CreateAndIssueAppleStampResponderFlow.kt deleted file mode 100644 index 6bb19dd..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/CreateAndIssueAppleStampResponderFlow.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.r3.developers.apples.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 - -@InitiatedBy(protocol = "create-and-issue-apple-stamp") -class CreateAndIssueAppleStampResponderFlow : ResponderFlow { - - @CordaInject - lateinit var utxoLedgerService: UtxoLedgerService - - @Suspendable - override fun call(session: FlowSession) { - // Receive, verify, validate, sign and record the transaction sent from the initiator - utxoLedgerService.receiveFinality(session) { - /* - * [receiveFinality] will automatically verify the transaction and its signatures before signing it. - * However, just because a transaction is contractually valid doesn't mean we necessarily want to sign. - * What if we don't want to deal with the counterparty in question, or the value is too high, - * or we're not happy with the transaction's structure? [UtxoTransactionValidator] (the lambda created - * here) allows us to define the additional checks. If any of these conditions are not met, - * we will not sign the transaction - even if the transaction and its signatures are contractually valid. - */ - } - } -} diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/PackageApplesFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/PackageApplesFlow.kt deleted file mode 100644 index 495e267..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/PackageApplesFlow.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.r3.developers.apples.workflows - -import com.r3.developers.apples.contracts.AppleCommands -import com.r3.developers.apples.states.BasketOfApples -import net.corda.v5.application.flows.CordaInject -import net.corda.v5.application.flows.ClientRequestBody -import net.corda.v5.application.flows.ClientStartableFlow -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.types.MemberX500Name -import net.corda.v5.ledger.common.NotaryLookup -import net.corda.v5.ledger.utxo.UtxoLedgerService -import java.time.Instant -import java.time.temporal.ChronoUnit - -class PackageApplesFlow : ClientStartableFlow { - - internal data class PackApplesRequest(val appleDescription: String, val weight: Int, val notary: MemberX500Name) - - @CordaInject - lateinit var jsonMarshallingService: JsonMarshallingService - - @CordaInject - lateinit var memberLookup: MemberLookup - - @CordaInject - lateinit var notaryLookup: NotaryLookup - - @CordaInject - lateinit var utxoLedgerService: UtxoLedgerService - - @Suspendable - override fun call(requestBody: ClientRequestBody): String { - val request = requestBody.getRequestBodyAs(jsonMarshallingService, PackApplesRequest::class.java) - val appleDescription = request.appleDescription - val weight = request.weight - val notary = notaryLookup.lookup(request.notary) - ?: throw IllegalArgumentException("Notary ${request.notary} not found") - val myKey = memberLookup.myInfo().ledgerKeys.first() - - // Building the output BasketOfApples state - val basket = BasketOfApples( - description = appleDescription, - farm = myKey, - owner = myKey, - weight = weight, - participants = listOf(myKey) - ) - - // Create the transaction - val transaction = utxoLedgerService.createTransactionBuilder() - .setNotary(notary.name) - .addOutputState(basket) - .addCommand(AppleCommands.PackBasket()) - .setTimeWindowUntil(Instant.now().plus(1, ChronoUnit.DAYS)) - .addSignatories(listOf(myKey)) - .toSignedTransaction() - - return try { - // Record the transaction, no sessions are passed in as the transaction is only being - // recorded locally - utxoLedgerService.finalize(transaction, emptyList()).transaction.id.toString() - } catch (e: Exception) { - "Flow failed, message: ${e.message}" - } - } -} diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/RedeemApplesFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/RedeemApplesFlow.kt deleted file mode 100644 index 6fddbd4..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/RedeemApplesFlow.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.r3.developers.apples.workflows - -import com.r3.developers.apples.contracts.AppleCommands -import com.r3.developers.apples.states.AppleStamp -import com.r3.developers.apples.states.BasketOfApples -import net.corda.v5.base.types.MemberX500Name -import net.corda.v5.application.flows.CordaInject -import net.corda.v5.application.flows.InitiatingFlow -import net.corda.v5.application.flows.ClientRequestBody -import net.corda.v5.application.flows.ClientStartableFlow -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.base.annotations.Suspendable -import net.corda.v5.ledger.common.NotaryLookup -import net.corda.v5.ledger.utxo.UtxoLedgerService -import java.time.Instant -import java.time.temporal.ChronoUnit -import java.util.UUID - -@InitiatingFlow(protocol = "redeem-apples") -class RedeemApplesFlow : ClientStartableFlow { - - internal data class RedeemApplesRequest(val buyer: MemberX500Name, val notary: MemberX500Name, val stampId: UUID) - - @CordaInject - lateinit var flowMessaging: FlowMessaging - - @CordaInject - lateinit var jsonMarshallingService: JsonMarshallingService - - @CordaInject - lateinit var memberLookup: MemberLookup - - @CordaInject - lateinit var notaryLookup: NotaryLookup - - @CordaInject - lateinit var utxoLedgerService: UtxoLedgerService - - @Suspendable - override fun call(requestBody: ClientRequestBody): String { - val request = requestBody.getRequestBodyAs(jsonMarshallingService, RedeemApplesRequest::class.java) - val buyerName = request.buyer - val stampId = request.stampId - - // Retrieve the notary's public key (this will change) - val notaryInfo = notaryLookup.lookup(request.notary) - ?: throw IllegalArgumentException("Notary ${request.notary} not found") - - val myKey = memberLookup.myInfo().ledgerKeys.first() - - val buyer = memberLookup.lookup(buyerName)?.ledgerKeys?.first() - ?: throw IllegalArgumentException("The buyer does not exist within the network") - - val appleStampStateAndRef = utxoLedgerService.findUnconsumedStatesByExactType(AppleStamp::class.java, 100, Instant.now()) - .results.firstOrNull { stateAndRef -> stateAndRef.state.contractState.id == stampId } - ?: throw IllegalArgumentException("No apple stamp matching the stamp id $stampId") - - val basketOfApplesStampStateAndRef = utxoLedgerService.findUnconsumedStatesByExactType(BasketOfApples::class.java, 100, Instant.now()) - .results.firstOrNull { basketStateAndRef -> basketStateAndRef.state.contractState.owner == - appleStampStateAndRef.state.contractState.issuer } - ?: throw IllegalArgumentException("There are no eligible baskets of apples") - - - val originalBasketOfApples = basketOfApplesStampStateAndRef.state.contractState - - val updatedBasket = originalBasketOfApples.changeOwner(buyer) - - // Create the transaction - val transaction = utxoLedgerService.createTransactionBuilder() - .setNotary(notaryInfo.name) - .addInputStates(appleStampStateAndRef.ref, basketOfApplesStampStateAndRef.ref) - .addOutputState(updatedBasket) - .addCommand(AppleCommands.Redeem()) - .setTimeWindowUntil(Instant.now().plus(1, ChronoUnit.DAYS)) - .addSignatories(listOf(myKey, buyer)) - .toSignedTransaction() - - val session = flowMessaging.initiateFlow(buyerName) - - return try { - // Send the transaction and state to the counterparty and let them sign it - // Then notarise and record the transaction in both parties' vaults. - utxoLedgerService.finalize(transaction, listOf(session)).transaction.id.toString() - } catch (e: Exception) { - "Flow failed, message: ${e.message}" - } - } -} diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/RedeemApplesResponderFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/RedeemApplesResponderFlow.kt deleted file mode 100644 index d53889e..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/apples/workflows/RedeemApplesResponderFlow.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.r3.developers.apples.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 - -@InitiatedBy(protocol = "redeem-apples") -class RedeemApplesResponderFlow : ResponderFlow { - - @CordaInject - lateinit var utxoLedgerService: UtxoLedgerService - - @Suspendable - override fun call(session: FlowSession) { - // Receive, verify, validate, sign and record the transaction sent from the initiator - utxoLedgerService.receiveFinality(session) { - /* - * [receiveFinality] will automatically verify the transaction and its signatures before signing it. - * However, just because a transaction is contractually valid doesn't mean we necessarily want to sign. - * What if we don't want to deal with the counterparty in question, or the value is too high, - * or we're not happy with the transaction's structure? [UtxoTransactionValidator] (the lambda created - * here) allows us to define the additional checks. If any of these conditions are not met, - * we will not sign the transaction - even if the transaction and its signatures are contractually valid. - */ - } - } -} diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/CreateNewChatFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/CreateNewChatFlow.kt deleted file mode 100644 index ad54117..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/CreateNewChatFlow.kt +++ /dev/null @@ -1,112 +0,0 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows - -import com.r3.developers.cordapptemplate.customStateJSONandQuery.contracts.ChatContract -import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState -import net.corda.v5.application.flows.* -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 - -// A class to hold the deserialized arguments required to start the flow. -data class CreateNewChatFlowArgs(val chatName: String, val message: String, val otherMember: String) - -// See Chat CorDapp Design section of the getting started docs for a description of this flow. -class CreateNewChatFlow: ClientStartableFlow { - - private companion object { - val log = LoggerFactory.getLogger(this::class.java.enclosingClass) - } - - @CordaInject - lateinit var jsonMarshallingService: JsonMarshallingService - - @CordaInject - lateinit var memberLookup: MemberLookup - - // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API. - @CordaInject - lateinit var ledgerService: UtxoLedgerService - - @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("CreateNewChatFlow.call() called") - - try { - // Obtain the deserialized input arguments to the flow from the requestBody. - val flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, CreateNewChatFlowArgs::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 otherMember = memberLookup.lookup(MemberX500Name.parse(flowArgs.otherMember)) ?: - throw CordaRuntimeException("MemberLookup can't find otherMember specified in flow arguments.") - - // Create the ChatState from the input arguments and member information. - val chatState = ChatState( - chatName = flowArgs.chatName, - messageFrom = myInfo.name, - message = flowArgs.message, - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - // Obtain the notary. - val notary = notaryLookup.notaryServices.single() - - // 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(chatState) - .addCommand(ChatContract.Create()) - .addSignatories(chatState.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 FinalizeChatSubFlow 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(FinalizeChatSubFlow(signedTransaction, otherMember.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 REST: -{ - "clientRequestId": "create-1", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.CreateNewChatFlow", - "requestBody": { - "chatName":"Chat with Bob", - "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB", - "message": "Hello Bob" - } -} - */ \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/GetChatFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/GetChatFlow.kt deleted file mode 100644 index 719ad08..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/GetChatFlow.kt +++ /dev/null @@ -1,117 +0,0 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows - -import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState -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.base.exceptions.CordaRuntimeException -import net.corda.v5.ledger.utxo.StateAndRef -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 GetChatFlowArgs(val id: UUID, val numberOfRecords: Int) - -// A class to pair the messageFrom and message together. -data class MessageAndSender(val messageFrom: String, val message: String) - -// See Chat CorDapp Design section of the getting started docs for a description of this flow. -class GetChatFlow: ClientStartableFlow { - - private companion object { - val log = LoggerFactory.getLogger(this::class.java.enclosingClass) - } - - @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("GetChatFlow.call() called") - - // Obtain the deserialized input arguments to the flow from the requestBody. - val flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, GetChatFlowArgs::class.java) - - // Look up the latest unconsumed ChatState with the given id. - // Note, this code brings all unconsumed states back, then filters them. - // This is an inefficient way to perform this operation when there are a large number of chats. - // Note, you will get this error if you input an id which has no corresponding ChatState (common error). - val states = ledgerService.findUnconsumedStatesByExactType(ChatState::class.java, 100, Instant.now()).results - val state = states.singleOrNull {it.state.contractState.id == flowArgs.id} - ?: throw CordaRuntimeException("Did not find an unique unconsumed ChatState with id ${flowArgs.id}") - - // Calls resolveMessagesFromBackchain() which retrieves the chat history from the backchain. - return jsonMarshallingService.format(resolveMessagesFromBackchain(state, flowArgs.numberOfRecords )) - } - - // resoveMessageFromBackchain() starts at the stateAndRef provided, which represents the unconsumed head of the - // backchain for this particular chat, then walks the chain backwards for the number of transaction specified in - // the numberOfRecords argument. For each transaction it adds the MessageAndSender representing the - // message and who sent it to a list which is then returned. - @Suspendable - private fun resolveMessagesFromBackchain(stateAndRef: StateAndRef, numberOfRecords: Int): List{ - - // Set up a MutableList to collect the MessageAndSender(s) - val messages = mutableListOf() - - // Set up initial conditions for walking the backchain. - var currentStateAndRef = stateAndRef - var recordsToFetch = numberOfRecords - var moreBackchain = true - - // Continue to loop until the start of the backchain or enough records have been retrieved. - while (moreBackchain) { - - // Obtain the transaction id from the current StateAndRef and fetch the transaction from the vault. - val transactionId = currentStateAndRef.ref.transactionId - val transaction = ledgerService.findLedgerTransaction(transactionId) - ?: throw CordaRuntimeException("Transaction $transactionId not found.") - - // Get the output state from the transaction and use it to create a MessageAndSender Object which - // is appended to the mutable list. - val output = transaction.getOutputStates(ChatState::class.java).singleOrNull() - ?: throw CordaRuntimeException("Expecting one and only one ChatState output for transaction $transactionId.") - messages.add(MessageAndSender(output.messageFrom.toString(), output.message)) - // Decrement the number of records to fetch. - recordsToFetch-- - - // Get the reference to the input states. - val inputStateAndRefs = transaction.inputStateAndRefs - - // Check if there are no more input states (start of chain) or we have retrieved enough records. - // Check the transaction is not malformed by having too many input states. - // Set the currentStateAndRef to the input StateAndRef, then repeat the loop. - if (inputStateAndRefs.isEmpty() || recordsToFetch == 0) { - moreBackchain = false - } else if (inputStateAndRefs.size > 1) { - throw CordaRuntimeException("More than one input state found for transaction $transactionId.") - } else { - @Suppress("UNCHECKED_CAST") - currentStateAndRef = inputStateAndRefs.single() as StateAndRef - } - } - // Convert to an immutable List. - return messages.toList() - } -} - -/* -RequestBody for triggering the flow via REST: -{ - "clientRequestId": "get-1", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.GetChatFlow", - "requestBody": { - "id":"** fill in id **", - "numberOfRecords":"4" - } -} - */ \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/ListChatsByCustomQueryFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/ListChatsByCustomQueryFlow.kt deleted file mode 100644 index 6fe5079..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/ListChatsByCustomQueryFlow.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows - -import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState -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.LoggerFactory -import java.time.Instant - -// See Chat CorDapp Design section of the getting started docs for a description of this flow. -class ListChatsByCustomQueryFlow : ClientStartableFlow { - - private companion object { - val log = LoggerFactory.getLogger(this::class.java.enclosingClass) - } - - @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("ListChatsByCustomQueryFlow.call() called") - - //this is our custom query - val resultSet = ledgerService.query("GET_MSG_FROM", StateAndRef::class.java) - .setParameter("nameOfSender", "CN=Alice, OU=Test Dept, O=R3, L=London, C=GB") - .setCreatedTimestampLimit(Instant.now()).setLimit(1000) - .execute() - - //from custom query to states, we can operate - val resultState = resultSet.results.map {it.state.contractState as ChatState} - - //from the states -> human-readable. - val results = resultState.map { - ChatStateResults( - it.id, - it.chatName, - it.messageFrom.toString(), - it.message) } - log.info("-------------results will be printed------") - log.warn(results.toString()) - - // Uses the JsonMarshallingService's format() function to serialize the DTO to Json. - return jsonMarshallingService.format(results.toString()) - } -} - - - -/* -RequestBody for triggering the flow via REST: -{ - "clientRequestId": "customlist-1", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.ListChatsByCustomQueryFlow", - "requestBody": {} -} -*/ diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/ListChatsFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/ListChatsFlow.kt deleted file mode 100644 index 04198ac..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/ListChatsFlow.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows - -import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState -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.* - - -// Data class to hold the Flow results. -// The ChatState(s) cannot be returned directly as the JsonMarshallingService can only serialize simple classes -// that the underlying Jackson serializer recognises, hence creating a DTO style object which consists only of Strings -// and a UUID. It is possible to create custom serializers for the JsonMarshallingService, but this beyond the scope -// of this simple example. -data class ChatStateResults(val id: UUID, val chatName: String,val messageFromName: String, val message: String) - -// See Chat CorDapp Design section of the getting started docs for a description of this flow. -class ListChatsFlow : ClientStartableFlow { - - private companion object { - val log = LoggerFactory.getLogger(this::class.java.enclosingClass) - } - - @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("ListChatsFlow.call() called") - - // Queries the VNode's vault for unconsumed states and converts the result to a serializable DTO. - val states = ledgerService.findUnconsumedStatesByExactType(ChatState::class.java, 100, Instant.now()).results - val results = states.map { - ChatStateResults( - it.state.contractState.id, - it.state.contractState.chatName, - it.state.contractState.messageFrom.toString(), - it.state.contractState.message) } - - // Uses the JsonMarshallingService's format() function to serialize the DTO to Json. - return jsonMarshallingService.format(results) - } -} - -/* -RequestBody for triggering the flow via REST: -{ - "clientRequestId": "list-1", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.ListChatsFlow", - "requestBody": {} -} -*/ diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/TestContractFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/TestContractFlow.kt deleted file mode 100644 index 84a8706..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/TestContractFlow.kt +++ /dev/null @@ -1,492 +0,0 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows - -import com.r3.developers.cordapptemplate.customStateJSONandQuery.contracts.ChatContract -import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState -import net.corda.v5.application.flows.* -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.Command -import net.corda.v5.ledger.utxo.StateRef -import net.corda.v5.ledger.utxo.UtxoLedgerService -import org.slf4j.LoggerFactory -import java.time.Duration -import java.time.Instant -import java.util.* - - -data class TestContractFlowArgs(val otherMember: String) - -class TestContractFlow: ClientStartableFlow { - - private companion object { - val log = LoggerFactory.getLogger(this::class.java.enclosingClass) - } - - @CordaInject - lateinit var jsonMarshallingService: JsonMarshallingService - - @CordaInject - lateinit var memberLookup: MemberLookup - - // Injects the UtxoLedgerService to enable the flow to make use of the Ledger API - @CordaInject - lateinit var ledgerService: UtxoLedgerService - - @CordaInject - lateinit var notaryLookup: NotaryLookup - - @CordaInject - lateinit var flowEngine: FlowEngine - - @Suspendable - override fun call(requestBody: ClientRequestBody): String { - - - val results = mutableMapOf() - - log.info("TestContractFlow.call() called") - - class FakeCommand : Command - - try { - val flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, TestContractFlowArgs::class.java) - - val myInfo = memberLookup.myInfo() - - val otherMember = memberLookup.lookup(MemberX500Name.parse(flowArgs.otherMember)) ?: - throw CordaRuntimeException("MemberLookup can't find otherMember specified in flow arguments.") - - // Obtain the Notary name and public key. - val notary = notaryLookup.notaryServices.first() - - // Create a well formed transaction with an output State which can be referenced - // as an input StateRef in the tests - lateinit var inputStateRef: StateRef - lateinit var chatId: UUID - - try { - val chatState = ChatState( - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - chatId = chatState.id - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addOutputState(chatState) - .addCommand(ChatContract.Create()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - inputStateRef = StateRef(signedTransaction.id, 0) - flowEngine.subFlow(FinalizeChatSubFlow(signedTransaction, otherMember.name)) - - } catch (e:Exception) { - throw CordaRuntimeException("Set up transaction could not be created because of exception: ${e.message}") - } - - - - - // ************* START TESTS **************** - - // Multiple Commands not permitted - results["Multiple Commands not permitted"] = try { - val chatState = ChatState( - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addOutputState(chatState) - .addCommand(ChatContract.Create()) - .addCommand(FakeCommand()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("Requires a single command.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - // ChatState with 3 Participants not permitted - results["ChatState with 3 Participants not permitted"] = try { - - val chatState = ChatState( - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addOutputState(chatState) - .addCommand(ChatContract.Create()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("The output state should have two and only two participants.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - // Input State on Create not permitted - results["Input State on Create not permitted"] = try { - val chatState = ChatState( - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addInputState(inputStateRef) - .addOutputState(chatState) - .addCommand(ChatContract.Create()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("When command is Create there should be no input states.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - // Zero output States on Create not permitted - - // Test omitted as it would fail on - // "The output state should have two and only two participants." first - - - // Two output States on Create not permitted - results["Two output States on Create not permitted"] = try { - val chatState = ChatState( - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addOutputState(chatState) - .addOutputState(chatState) - .addCommand(ChatContract.Create()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("When command is Create there should be one and only one output state.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - // Zero input State on Update not permitted - results["Zero input State on Update not permitted"] = try { - - val chatState = ChatState( - id = chatId, - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addOutputState(chatState) - .addCommand(ChatContract.Update()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("When command is Update there should be one and only one input state.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - // Two Input State on Update not permitted - results["Two Input State on Update not permitted"] = try { - - log.info("MB: test change") - val chatState = ChatState( - id = chatId, - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addInputState(inputStateRef) - .addInputState(inputStateRef) - .addOutputState(chatState) - .addCommand(ChatContract.Update()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("When command is Update there should be one and only one input state.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - // Zero output States on Update not permitted - - // Test omitted as it would fail on - // "The output state should have two and only two participants." first - - - // Two output States on Update not permitted - results["Two output States on Update not permitted"] = try { - val chatState = ChatState( - id = chatId, - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addInputState(inputStateRef) - .addOutputState(chatState) - .addOutputState(chatState) - .addCommand(ChatContract.Update()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("When command is Update there should be one and only one output state.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - // On Update id must not change - results["On Update id must not change"] = try { - val chatState = ChatState( - id = UUID.randomUUID(), - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addInputState(inputStateRef) - .addOutputState(chatState) - .addCommand(ChatContract.Update()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("When command is Update id must not change")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - // On Update chatName must not change - results["On Update chatName must not change"] = try { - val chatState = ChatState( - id = chatId, - chatName = "DummyChat Name has changed", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addInputState(inputStateRef) - .addOutputState(chatState) - .addCommand(ChatContract.Update()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("When command is Update chatName must not change.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - // On Update participants must not change - results["On Update participants must not change"] = try { - val chatState = ChatState( - id = chatId, - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), myInfo.ledgerKeys.first()) - ) - - val txBuilder = ledgerService.createTransactionBuilder() - .setNotary(notary.name) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addInputState(inputStateRef) - .addOutputState(chatState) - .addCommand(ChatContract.Update()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("When command is Update participants must not change.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - // FakeCommand not permitted - results["FakeCommand not permitted"] = try { - val chatState = ChatState( - chatName = "DummyChat", - messageFrom = myInfo.name, - message = "Dummy Message", - participants = listOf(myInfo.ledgerKeys.first(), otherMember.ledgerKeys.first()) - ) - - - // 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(chatState) - .addCommand(FakeCommand()) - .addSignatories(chatState.participants) - - @Suppress("DEPRECATION", "UNUSED_VARIABLE") - val signedTransaction = txBuilder.toSignedTransaction() - - "Fail" - - } catch (e:Exception) { - val exceptionMessage = e.message ?: "No exception message" - if (exceptionMessage.contains("Command not allowed.")) { - "Pass" } - else { - "Contract failed but with a different Exception: ${e.message}" - } - } - - - - return results.toString() - - // 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 - } - } - -} -/* -{ - "clientRequestId": "dummy-1", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.TestContractFlow", - "requestBody": { - "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB" - } -} - - */ \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/UpdateChatFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/UpdateChatFlow.kt deleted file mode 100644 index 8c4780f..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customStateJSONandQuery/workflows/UpdateChatFlow.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows - -import com.r3.developers.cordapptemplate.customStateJSONandQuery.contracts.ChatContract -import com.r3.developers.cordapptemplate.customStateJSONandQuery.states.ChatState -import net.corda.v5.application.flows.* -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 UpdateChatFlowArgs(val id: UUID, val message: String) - - -// See Chat CorDapp Design section of the getting started docs for a description of this flow. -class UpdateChatFlow: ClientStartableFlow { - - private companion object { - val log = LoggerFactory.getLogger(this::class.java.enclosingClass) - } - - @CordaInject - lateinit var jsonMarshallingService: JsonMarshallingService - - @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("UpdateNewChatFlow.call() called") - - try { - // Obtain the deserialized input arguments to the flow from the requestBody. - val flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, UpdateChatFlowArgs::class.java) - - // Look up the latest unconsumed ChatState with the given id. - // Note, this code brings all unconsumed states back, then filters them. - // This is an inefficient way to perform this operation when there are a large number of chats. - // Note, you will get this error if you input an id which has no corresponding ChatState (common error). - val stateAndRef = ledgerService.findUnconsumedStatesByExactType(ChatState::class.java, 100, Instant.now()).results.singleOrNull { - it.state.contractState.id == flowArgs.id - } ?: throw CordaRuntimeException("Multiple or zero Chat states with id ${flowArgs.id} found.") - - // Get MemberInfos for the Vnode running the flow and the otherMember. - val myInfo = memberLookup.myInfo() - val state = stateAndRef.state.contractState - - val members = state.participants.map { - memberLookup.lookup(it) ?: throw CordaRuntimeException("Member not found from public key $it.")} - val otherMember = (members - myInfo).singleOrNull() - ?: throw CordaRuntimeException("Should be only one participant other than the initiator.") - - // Create a new ChatState using the updateMessage helper function. - val newChatState = state.updateMessage(myInfo.name, flowArgs.message) - - // Use UTXOTransactionBuilder to build up the draft transaction. - val txBuilder= ledgerService.createTransactionBuilder() - .setNotary(stateAndRef.state.notaryName) - .setTimeWindowBetween(Instant.now(), Instant.now().plusMillis(Duration.ofDays(1).toMillis())) - .addOutputState(newChatState) - .addInputState(stateAndRef.ref) - .addCommand(ChatContract.Update()) - .addSignatories(newChatState.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 FinalizeChatSubFlow 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(FinalizeChatSubFlow(signedTransaction, otherMember.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 REST: -{ - "clientRequestId": "update-2", - "flowClassName": "com.r3.developers.cordapptemplate.customStateJSONandQuery.workflows.UpdateChatFlow", - "requestBody": { - "id":"** fill in id **", - "message": "How are you today?" - } -} - */ \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/FinalizeChatSubFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/FinalizeChatSubFlow.kt deleted file mode 100644 index c1114f7..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/FinalizeChatSubFlow.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows - -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState -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 - -// See Chat CorDapp Design section of the getting started docs for a description of this flow. - -// @InitiatingFlow declares the protocol which will be used to link the initiator to the responder. -@InitiatingFlow(protocol = "finalize-chat-protocol") -class FinalizeChatSubFlow(private val signedTransaction: UtxoSignedTransaction, private val otherMember: MemberX500Name): 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 - - @Suspendable - override fun call(): String { - - log.info("FinalizeChatFlow.call() called") - - // Initiates a session with the other Member. - val session = flowMessaging.initiateFlow(otherMember) - - 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. (This is different to the ChatState id) - val finalizedSignedTransaction = ledgerService.finalize( - signedTransaction, - listOf(session) - ) - // 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}" - } - } -} - -// See Chat CorDapp Design section of the getting started docs for a description of this flow. - -//@InitiatingBy declares the protocol which will be used to link the initiator to the responder. -@InitiatedBy(protocol = "finalize-chat-protocol") -class FinalizeChatResponderFlow: 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("FinalizeChatResponderFlow.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(ChatState::class.java).singleOrNull() ?: - throw CordaRuntimeException("Failed verification - transaction did not have exactly one output ChatState.") - - // Uses checkForBannedWords() and checkMessageFromMatchesCounterparty() functions - // to check whether to sign the transaction. - checkForBannedWords(state.message) - checkMessageFromMatchesCounterparty(state, session.counterparty) - - 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/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/ResponderValidations.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/ResponderValidations.kt deleted file mode 100644 index c88bc04..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/customeStateJSONandQuery/workflows/ResponderValidations.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.r3.developers.cordapptemplate.customeStateJSONandQuery.workflows - -import com.r3.developers.cordapptemplate.customeStateJSONandQuery.states.ChatState -import net.corda.v5.base.annotations.Suspendable -import net.corda.v5.base.exceptions.CordaRuntimeException -import net.corda.v5.base.types.MemberX500Name - -// Note, these exceptions will only be visible in the logs if Corda logging is set to debug. - -// Checks that the message does not contain banned words and throws and exception if it does. -@Suspendable -fun checkForBannedWords(str: String) { - val bannedWords = listOf("banana", "apple", "pear") - if (bannedWords.any { str.contains(it) }) - throw CordaRuntimeException("Failed verification - message contains banned words") -} - -// Checks that the messageFrom field in the ChatState matches the initiators (otherMember) -// memberX500Name, if not it throws an exception. -@Suspendable -fun checkMessageFromMatchesCounterparty(state: ChatState, otherMember: MemberX500Name) { - if( state.messageFrom != otherMember) - throw CordaRuntimeException("Failed verification - messageFrom does not equal flow initiator memberX500Name") -} \ No newline at end of file diff --git a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/flowexample/workflows/MyFirstFlow.kt b/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/flowexample/workflows/MyFirstFlow.kt deleted file mode 100644 index 47e46d3..0000000 --- a/kotlin-samples/customeStateJSONandQuery/workflows/src/main/kotlin/com/r3/developers/cordapptemplate/flowexample/workflows/MyFirstFlow.kt +++ /dev/null @@ -1,154 +0,0 @@ -package com.r3.developers.cordapptemplate.flowexample.workflows - -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.CordaSerializable -import net.corda.v5.base.annotations.Suspendable -import net.corda.v5.base.types.MemberX500Name -import org.slf4j.LoggerFactory - -// A class to hold the deserialized arguments required to start the flow. -class MyFirstFlowStartArgs(val otherMember: MemberX500Name) - - -// A class which will contain a message, It must be marked with @CordaSerializable for Corda -// to be able to send from one virtual node to another. -@CordaSerializable -class Message(val sender: MemberX500Name, val message: String) - - -// MyFirstFlow is an initiating flow, it's corresponding responder flow is called MyFirstFlowResponder (defined below) -// to link the two sides of the flow together they need to have the same protocol. -@InitiatingFlow(protocol = "my-first-flow") -// MyFirstFlow should inherit from ClientStartableFlow, which tells Corda it can be started via an REST call from a client -class MyFirstFlow: ClientStartableFlow { - - // It is useful to be able to log messages from the flows for debugging. - private companion object { - val log = LoggerFactory.getLogger(this::class.java.enclosingClass) - } - - // Corda has a set of injectable services which are injected into the flow at runtime. - // Flows declare them with @CordaInjectable, then the flows have access to their services. - - // JsonMarshallingService provides a Service for manipulating json - @CordaInject - lateinit var jsonMarshallingService: JsonMarshallingService - - // FlowMessaging provides a service for establishing flow sessions between Virtual Nodes and - // sending and receiving payloads between them - @CordaInject - lateinit var flowMessaging: FlowMessaging - - // MemberLookup provides a service for looking up information about members of the Virtual Network which - // this CorDapp is operating in. - @CordaInject - lateinit var memberLookup: MemberLookup - - - - // When a flow is invoked its call() method is called. - // call() methods must be marked as @Suspendable, this allows Corda to pause mid-execution to wait - // for a response from the other flows and services. - @Suspendable - override fun call(requestBody: ClientRequestBody): String { - - // Useful logging to follow what's happening in the console or logs - log.info("MFF: MyFirstFlow.call() called") - - // Show the requestBody in the logs - this can be used to help establish the format for starting a flow on corda - log.info("MFF: requestBody: ${requestBody.getRequestBody()}") - - // Deserialize the Json requestBody into the MyfirstFlowStartArgs class using the JsonSerialisation Service - val flowArgs = requestBody.getRequestBodyAs(jsonMarshallingService, MyFirstFlowStartArgs::class.java) - - // Obtain the MemberX500Name of counterparty - val otherMember = flowArgs.otherMember - - // Get our identity from the MemberLookup service. - val ourIdentity = memberLookup.myInfo().name - - // Create the message payload using the MessageClass we defined. - val message = Message(otherMember, "Hello from $ourIdentity.") - - // Log the message to be sent. - log.info("MFF: message.message: ${message.message}") - - // Start a flow session with the otherMember using the FlowMessaging service - // The otherMember's Virtual Node will run the corresponding MyFirstFlowResponder responder flow - val session = flowMessaging.initiateFlow(otherMember) - - // Send the Payload using the send method on the session to the MyFirstFlowResponder Responder flow - session.send(message) - - // Receive a response from the Responder flow - val response = session.receive(Message::class.java) - - // The return value of a ClientStartableFlow must always be a String, this String will be passed - // back as the REST response when the status of the flow is queried on Corda. - return response.message - } -} - -// MyFirstFlowResponder is a responder flow, it's corresponding initiating flow is called MyFirstFlow (defined above) -// to link the two sides of the flow together they need to have the same protocol. -@InitiatedBy(protocol = "my-first-flow") -// Responder flows must inherit from ResponderFlow -class MyFirstFlowResponder: ResponderFlow { - - // It is useful to be able to log messages from the flows for debugging. - private companion object { - val log = LoggerFactory.getLogger(this::class.java.enclosingClass) - } - - // MemberLookup provides a service for looking up information about members of the Virtual Network which - // this CorDapp is operating in. - @CordaInject - lateinit var memberLookup: MemberLookup - - - // Responder flows are invoked when an initiating flow makes a call via a session set up with the Virtual - // node hosting the Responder flow. When a responder flow is invoked, its call() method is called. - // call() methods must be marked as @Suspendable, this allows Corda to pause mid-execution to wait - // for a response from the other flows and services/ - // The Call method has the flow session passed in as a parameter by Corda so the session is available to - // responder flow code, you don't need to inject the FlowMessaging service. - @Suspendable - override fun call(session: FlowSession) { - - // Useful logging to follow what's happening in the console or logs - log.info("MFF: MyFirstResponderFlow.call() called") - - // Receive the payload and deserialize it into a Message class - val receivedMessage = session.receive(Message::class.java) - - // Log the message as a proxy for performing some useful operation on it. - log.info("MFF: Message received from ${receivedMessage.sender}: ${receivedMessage.message} ") - - // Get our identity from the MemberLookup service. - val ourIdentity = memberLookup.myInfo().name - - // Create a response to greet the sender - val response = Message(ourIdentity, - "Hello ${session.counterparty.commonName}, best wishes from ${ourIdentity.commonName}") - - // Log the response to be sent. - log.info("MFF: response.message: ${response.message}") - - // Send the response via the send method on the flow session - session.send(response) - } -} -/* -RequestBody for triggering the flow via REST: -{ - "clientRequestId": "r1", - "flowClassName": "com.r3.developers.cordapptemplate.flowexample.workflows.MyFirstFlow", - "requestBody": { - "otherMember":"CN=Bob, OU=Test Dept, O=R3, L=London, C=GB" - } -} - */ \ No newline at end of file