├── .ci └── Jenkinsfile ├── .gitignore ├── .idea └── runConfigurations │ ├── Debug_CorDapp.xml │ ├── Run_Contract_Tests.xml │ ├── Run_Flow_Tests.xml │ ├── Run_Integration_Tests.xml │ ├── Run_Template_Client.xml │ ├── Run_Template_Server.xml │ └── Unit_tests.xml ├── .settings └── org.eclipse.jdt.core.prefs ├── LICENCE ├── README.md ├── TRADEMARK ├── build.gradle ├── clients ├── build.gradle └── src │ └── main │ ├── kotlin │ └── com │ │ └── template │ │ ├── Client.kt │ │ └── webserver │ │ ├── Controller.kt │ │ ├── NodeRPCConnection.kt │ │ └── Server.kt │ └── resources │ └── static │ ├── app.js │ └── index.html ├── config ├── dev │ └── log4j2.xml └── test │ └── log4j2.xml ├── constants.properties ├── contracts ├── build.gradle └── src │ ├── main │ └── kotlin │ │ └── com │ │ └── template │ │ ├── contracts │ │ └── TemplateContract.kt │ │ └── states │ │ └── TemplateState.kt │ └── test │ └── kotlin │ └── com │ └── template │ └── contracts │ ├── ContractTests.kt │ └── StateTests.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── repositories.gradle ├── settings.gradle └── workflows ├── build.gradle └── src ├── integrationTest └── kotlin │ └── com │ └── template │ └── DriverBasedTest.kt ├── main └── kotlin │ └── com │ └── template │ └── flows │ └── Flows.kt └── test └── kotlin └── com └── template └── FlowTests.kt /.ci/Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!groovy 2 | /** 3 | * Jenkins pipeline to build the kotlin CorDapp template 4 | */ 5 | 6 | /** 7 | * Kill already started job. 8 | * Assume new commit takes precedence and results from previousunfinished builds are not required. 9 | * This feature doesn't play well with disableConcurrentBuilds() option 10 | */ 11 | @Library('corda-shared-build-pipeline-steps') 12 | import static com.r3.build.BuildControl.killAllExistingBuildsForJob 13 | killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) 14 | 15 | pipeline { 16 | agent { 17 | label 'standard' 18 | } 19 | options { 20 | ansiColor('xterm') 21 | timestamps() 22 | timeout(3*60) // 3 hours 23 | buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '7')) 24 | } 25 | environment { 26 | JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto" 27 | } 28 | stages { 29 | stage('Build') { 30 | steps { 31 | sh './gradlew --no-daemon -s clean build test deployNodes' 32 | } 33 | } 34 | } 35 | post { 36 | cleanup { 37 | deleteDir() 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse, ctags, Mac metadata, log files 2 | .classpath 3 | .project 4 | tags 5 | .DS_Store 6 | *.log 7 | *.log.gz 8 | *.orig 9 | 10 | .gradle 11 | 12 | # General build files 13 | **/build/* 14 | !docs/build/* 15 | 16 | lib/dokka.jar 17 | 18 | ### JetBrains template 19 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 20 | 21 | *.iml 22 | 23 | ## Directory-based project format: 24 | #.idea 25 | 26 | # if you remove the above rule, at least ignore the following: 27 | 28 | # Specific files to avoid churn 29 | .idea/*.xml 30 | .idea/copyright 31 | .idea/jsLibraryMappings.xml 32 | 33 | # User-specific stuff: 34 | .idea/tasks.xml 35 | .idea/dictionaries 36 | 37 | # Sensitive or high-churn files: 38 | .idea/dataSources.ids 39 | .idea/dataSources.xml 40 | .idea/sqlDataSources.xml 41 | .idea/dynamic.xml 42 | .idea/uiDesigner.xml 43 | 44 | # Gradle: 45 | .idea/libraries 46 | 47 | # Mongo Explorer plugin: 48 | .idea/mongoSettings.xml 49 | 50 | ## File-based project format: 51 | *.ipr 52 | *.iws 53 | 54 | ## Plugin-specific files: 55 | 56 | # IntelliJ 57 | /out/ 58 | workflows/out/ 59 | contracts/out/ 60 | clients/out/ 61 | 62 | # mpeltonen/sbt-idea plugin 63 | .idea_modules/ 64 | 65 | # JIRA plugin 66 | atlassian-ide-plugin.xml 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | 73 | # docs related 74 | docs/virtualenv/ 75 | 76 | # if you use the installQuasar task 77 | lib 78 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Debug_CorDapp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Run_Contract_Tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Run_Flow_Tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Run_Integration_Tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Run_Template_Client.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Run_Template_Server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Unit_tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | org.eclipse.jdt.core.compiler.codegen.methodParameters=generate -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright 2016, R3 Limited. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Corda 3 |

4 | 5 | # CorDapp Template - Kotlin 6 | 7 | Welcome to the Kotlin CorDapp template. The CorDapp template is a stubbed-out CorDapp that you can use to bootstrap 8 | your own CorDapps. 9 | 10 | **This is the Kotlin version of the CorDapp template. The Java equivalent is 11 | [here](https://github.com/corda/cordapp-template-java/).** 12 | 13 | # Pre-Requisites 14 | 15 | See https://docs.corda.net/getting-set-up.html. 16 | 17 | # Usage 18 | 19 | ## Running tests inside IntelliJ 20 | 21 | We recommend editing your IntelliJ preferences so that you use the Gradle runner - this means that the quasar utils 22 | plugin will make sure that some flags (like ``-javaagent`` - see below) are 23 | set for you. 24 | 25 | To switch to using the Gradle runner: 26 | 27 | * Navigate to ``Build, Execution, Deployment -> Build Tools -> Gradle -> Runner`` (or search for `runner`) 28 | * Windows: this is in "Settings" 29 | * MacOS: this is in "Preferences" 30 | * Set "Delegate IDE build/run actions to gradle" to true 31 | * Set "Run test using:" to "Gradle Test Runner" 32 | 33 | If you would prefer to use the built in IntelliJ JUnit test runner, you can run ``gradlew installQuasar`` which will 34 | copy your quasar JAR file to the lib directory. You will then need to specify ``-javaagent:lib/quasar.jar`` 35 | and set the run directory to the project root directory for each test. 36 | 37 | ## Running the nodes 38 | 39 | See https://docs.corda.net/tutorial-cordapp.html#running-the-example-cordapp. 40 | 41 | ## Interacting with the nodes 42 | 43 | ### Shell 44 | 45 | When started via the command line, each node will display an interactive shell: 46 | 47 | Welcome to the Corda interactive shell. 48 | Useful commands include 'help' to see what is available, and 'bye' to shut down the node. 49 | 50 | Tue Nov 06 11:58:13 GMT 2018>>> 51 | 52 | You can use this shell to interact with your node. For example, enter `run networkMapSnapshot` to see a list of 53 | the other nodes on the network: 54 | 55 | Tue Nov 06 11:58:13 GMT 2018>>> run networkMapSnapshot 56 | [ 57 | { 58 | "addresses" : [ "localhost:10002" ], 59 | "legalIdentitiesAndCerts" : [ "O=Notary, L=London, C=GB" ], 60 | "platformVersion" : 3, 61 | "serial" : 1541505484825 62 | }, 63 | { 64 | "addresses" : [ "localhost:10005" ], 65 | "legalIdentitiesAndCerts" : [ "O=PartyA, L=London, C=GB" ], 66 | "platformVersion" : 3, 67 | "serial" : 1541505382560 68 | }, 69 | { 70 | "addresses" : [ "localhost:10008" ], 71 | "legalIdentitiesAndCerts" : [ "O=PartyB, L=New York, C=US" ], 72 | "platformVersion" : 3, 73 | "serial" : 1541505384742 74 | } 75 | ] 76 | 77 | Tue Nov 06 12:30:11 GMT 2018>>> 78 | 79 | You can find out more about the node shell [here](https://docs.corda.net/shell.html). 80 | 81 | ### Client 82 | 83 | `clients/src/main/kotlin/com/template/Client.kt` defines a simple command-line client that connects to a node via RPC 84 | and prints a list of the other nodes on the network. 85 | 86 | #### Running the client 87 | 88 | ##### Via the command line 89 | 90 | Run the `runTemplateClient` Gradle task. By default, it connects to the node with RPC address `localhost:10006` with 91 | the username `user1` and the password `test`. 92 | 93 | ##### Via IntelliJ 94 | 95 | Run the `Run Template Client` run configuration. By default, it connects to the node with RPC address `localhost:10006` 96 | with the username `user1` and the password `test`. 97 | 98 | ### Webserver 99 | 100 | `clients/src/main/kotlin/com/template/webserver/` defines a simple Spring webserver that connects to a node via RPC and 101 | allows you to interact with the node over HTTP. 102 | 103 | The API endpoints are defined here: 104 | 105 | clients/src/main/kotlin/com/template/webserver/Controller.kt 106 | 107 | And a static webpage is defined here: 108 | 109 | clients/src/main/resources/static/ 110 | 111 | #### Running the webserver 112 | 113 | ##### Via the command line 114 | 115 | Run the `runTemplateServer` Gradle task. By default, it connects to the node with RPC address `localhost:10006` with 116 | the username `user1` and the password `test`, and serves the webserver on port `localhost:10050`. 117 | 118 | ##### Via IntelliJ 119 | 120 | Run the `Run Template Server` run configuration. By default, it connects to the node with RPC address `localhost:10006` 121 | with the username `user1` and the password `test`, and serves the webserver on port `localhost:10050`. 122 | 123 | #### Interacting with the webserver 124 | 125 | The static webpage is served on: 126 | 127 | http://localhost:10050 128 | 129 | While the sole template endpoint is served on: 130 | 131 | http://localhost:10050/templateendpoint 132 | 133 | # Extending the template 134 | 135 | You should extend this template as follows: 136 | 137 | * Add your own state and contract definitions under `contracts/src/main/kotlin/` 138 | * Add your own flow definitions under `workflows/src/main/kotlin/` 139 | * Extend or replace the client and webserver under `clients/src/main/kotlin/` 140 | 141 | For a guided example of how to extend this template, see the Hello, World! tutorial 142 | [here](https://docs.corda.net/hello-world-introduction.html). 143 | -------------------------------------------------------------------------------- /TRADEMARK: -------------------------------------------------------------------------------- 1 | Corda and the Corda logo are trademarks of R3CEV LLC and its affiliates. All rights reserved. 2 | 3 | For R3CEV LLC's trademark and logo usage information, please consult our Trademark Usage Policy at 4 | https://www.r3.com/trademark-policy/. 5 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import static org.gradle.api.JavaVersion.VERSION_17 2 | 3 | buildscript { //properties that you need to build the project 4 | 5 | Properties constants = new Properties() 6 | file("$projectDir/./constants.properties").withInputStream { constants.load(it) } 7 | 8 | ext { 9 | corda_release_group = constants.getProperty("cordaReleaseGroup") 10 | corda_core_release_group = constants.getProperty("cordaCoreReleaseGroup") 11 | corda_release_version = constants.getProperty("cordaVersion") 12 | corda_core_release_version = constants.getProperty("cordaCoreVersion") 13 | corda_gradle_plugins_version = constants.getProperty("gradlePluginsVersion") 14 | kotlin_version = constants.getProperty("kotlinVersion") 15 | junit_version = constants.getProperty("junitVersion") 16 | quasar_version = constants.getProperty("quasarVersion") 17 | log4j_version = constants.getProperty("log4jVersion") 18 | slf4j_version = constants.getProperty("slf4jVersion") 19 | hibernate_version = constants.getProperty("hibernateVersion") 20 | corda_platform_version = constants.getProperty("platformVersion").toInteger() 21 | snappy_version = constants.getProperty("snappyVersion") 22 | //springboot 23 | spring_boot_version = '3.2.5' 24 | spring_boot_gradle_plugin_version = '3.2.5' 25 | 26 | test_add_opens = ['--add-opens', 'java.base/java.time=ALL-UNNAMED', '--add-opens', 'java.base/java.io=ALL-UNNAMED', 27 | '--add-opens', 'java.base/java.util=ALL-UNNAMED', '--add-opens', 'java.base/java.net=ALL-UNNAMED', 28 | '--add-opens', 'java.base/java.nio=ALL-UNNAMED', '--add-opens', 'java.base/java.lang.invoke=ALL-UNNAMED', 29 | '--add-opens', 'java.base/java.security.cert=ALL-UNNAMED', '--add-opens', 'java.base/java.security=ALL-UNNAMED', 30 | '--add-opens', 'java.base/javax.net.ssl=ALL-UNNAMED', '--add-opens', 'java.base/java.lang=ALL-UNNAMED', 31 | '--add-opens', 'java.base/java.util.concurrent=ALL-UNNAMED', '--add-opens', 'java.sql/java.sql=ALL-UNNAMED',] 32 | 33 | test_add_opens_fx = [ '--add-exports', 'java.base/sun.nio.ch=ALL-UNNAMED', 34 | '--add-exports', 'javafx.base/com.sun.javafx=ALL-UNNAMED', 35 | '--add-exports', 'javafx.base/com.sun.javafx.collections=ALL-UNNAMED'] 36 | } 37 | 38 | repositories { 39 | mavenLocal() 40 | mavenCentral() 41 | maven { url 'https://download.corda.net/maven/corda-dependencies' } 42 | maven { url 'https://download.corda.net/maven/corda-releases' } 43 | maven { url 'https://download.corda.net/maven/corda-dev' } 44 | } 45 | 46 | dependencies { 47 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 48 | classpath "net.corda.plugins:cordapp:$corda_gradle_plugins_version" 49 | classpath "net.corda.plugins:cordformation:$corda_gradle_plugins_version" 50 | classpath "net.corda.plugins:quasar-utils:$corda_gradle_plugins_version" 51 | classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_gradle_plugin_version" 52 | } 53 | } 54 | 55 | allprojects { //Properties that you need to compile your project (The application) 56 | apply from: "${rootProject.projectDir}/repositories.gradle" 57 | apply plugin: 'kotlin' 58 | 59 | repositories { 60 | mavenLocal() 61 | mavenCentral() 62 | maven { url 'https://download.corda.net/maven/corda-dependencies' } 63 | maven { url 'https://download.corda.net/maven/corda-releases' } 64 | maven { url 'https://jitpack.io' } 65 | } 66 | 67 | tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) { 68 | kotlinOptions { 69 | languageVersion = "1.9" 70 | apiVersion = "1.9" 71 | jvmTarget = VERSION_17 72 | javaParameters = true // Useful for reflection. 73 | } 74 | } 75 | 76 | jar { 77 | // This makes the JAR's SHA-256 hash repeatable. 78 | preserveFileTimestamps = false 79 | reproducibleFileOrder = true 80 | } 81 | } 82 | 83 | apply plugin: 'net.corda.plugins.cordapp' 84 | apply plugin: 'net.corda.plugins.cordformation' 85 | apply plugin: 'net.corda.plugins.quasar-utils' 86 | 87 | sourceSets { 88 | main { 89 | resources { 90 | srcDir rootProject.file("config/dev") 91 | } 92 | } 93 | } 94 | 95 | //Module dependencis 96 | dependencies { 97 | // Corda dependencies. 98 | cordaProvided "$corda_core_release_group:corda-core:$corda_core_release_version" 99 | cordaBootstrapper ("$corda_release_group:corda-node-api:$corda_release_version") { 100 | exclude group: "ch.qos.logback", module: "logback-classic" 101 | } 102 | corda "$corda_release_group:corda:$corda_release_version" 103 | 104 | // CorDapp dependencies. 105 | cordapp project(":workflows") 106 | cordapp project(":contracts") 107 | 108 | cordaProvided "org.apache.logging.log4j:log4j-slf4j2-impl:${log4j_version}" 109 | cordaProvided "org.apache.logging.log4j:log4j-web:${log4j_version}" 110 | cordaProvided "org.slf4j:jul-to-slf4j:$slf4j_version" 111 | cordaDriver "net.corda:corda-shell:$corda_release_version" 112 | } 113 | 114 | cordapp { 115 | targetPlatformVersion corda_platform_version.toInteger() 116 | } 117 | 118 | //Task to deploy the nodes in order to bootstrap a network 119 | task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) { 120 | 121 | /* This property will load the CorDapps to each of the node by default, including the Notary. You can find them 122 | * in the cordapps folder of the node at build/nodes/Notary/cordapps. However, the notary doesn't really understand 123 | * the notion of cordapps. In production, Notary does not need cordapps as well. This is just a short cut to load 124 | * the Corda network bootstrapper. 125 | */ 126 | nodeDefaults { 127 | projectCordapp { 128 | deploy = false 129 | } 130 | cordapp project(':contracts') 131 | cordapp project(':workflows') 132 | runSchemaMigration = true //This configuration is for any CorDapps with custom schema, We will leave this as true to avoid 133 | //problems for developers who are not familiar with Corda. If you are not using custom schemas, you can change 134 | //it to false for quicker project compiling time. 135 | } 136 | node { 137 | name "O=Notary,L=London,C=GB" 138 | notary = [validating : false] 139 | p2pPort 10002 140 | rpcSettings { 141 | address("localhost:10003") 142 | adminAddress("localhost:10043") 143 | } 144 | } 145 | node { 146 | name "O=PartyA,L=London,C=GB" 147 | p2pPort 10005 148 | rpcSettings { 149 | address("localhost:10006") 150 | adminAddress("localhost:10046") 151 | } 152 | rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]] 153 | } 154 | node { 155 | name "O=PartyB,L=New York,C=US" 156 | p2pPort 10008 157 | rpcSettings { 158 | address("localhost:10009") 159 | adminAddress("localhost:10049") 160 | } 161 | rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]] 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /clients/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.springframework.boot' 2 | 3 | sourceSets { 4 | main { 5 | resources { 6 | srcDir rootProject.file("config/dev") 7 | } 8 | } 9 | } 10 | 11 | dependencies { 12 | // Corda dependencies. 13 | implementation "$corda_core_release_group:corda-core:$corda_core_release_version" 14 | implementation "$corda_release_group:corda-rpc:$corda_release_version" 15 | 16 | // CorDapp dependencies. 17 | implementation project(":contracts") 18 | implementation project(":workflows") 19 | implementation("org.springframework.boot:spring-boot-starter-websocket:$spring_boot_version") { 20 | exclude group: "org.springframework.boot", module: "spring-boot-starter-logging" 21 | } 22 | implementation "org.apache.logging.log4j:log4j-slf4j2-impl:${log4j_version}" 23 | implementation "org.apache.logging.log4j:log4j-web:${log4j_version}" 24 | implementation "org.slf4j:jul-to-slf4j:$slf4j_version" 25 | } 26 | 27 | springBoot { 28 | mainClass = "com.template.webserver.ServerKt" 29 | } 30 | 31 | /* The Client is the communication channel between the external and the node. This task will help you immediately 32 | * execute your rpc methods in the main method of the client.kt. You can somewhat see this as a quick test of making 33 | * RPC calls to your nodes. 34 | */ 35 | task runTemplateClient(type: JavaExec, dependsOn: assemble) { 36 | classpath = sourceSets.main.runtimeClasspath 37 | main = 'com.template.ClientKt' 38 | args 'localhost:10006', 'user1', 'test' 39 | } 40 | 41 | /* This task will start the springboot server that connects to your node (via RPC connection). All of the http requests 42 | * are in the Controller file. You can leave the Server.kt and NodeRPCConnection.kt file untouched for your use. 43 | */ 44 | task runTemplateServer(type: JavaExec, dependsOn: assemble) { 45 | classpath = sourceSets.main.runtimeClasspath 46 | main = 'com.template.webserver.ServerKt' 47 | args '--server.port=10050', '--config.rpc.host=localhost', '--config.rpc.port=10006', '--config.rpc.username=user1', '--config.rpc.password=test' 48 | } -------------------------------------------------------------------------------- /clients/src/main/kotlin/com/template/Client.kt: -------------------------------------------------------------------------------- 1 | package com.template 2 | 3 | import net.corda.client.rpc.CordaRPCClient 4 | import net.corda.core.utilities.NetworkHostAndPort.Companion.parse 5 | import net.corda.core.utilities.loggerFor 6 | 7 | /** 8 | * Connects to a Corda node via RPC and performs RPC operations on the node. 9 | * 10 | * The RPC connection is configured using command line arguments. 11 | */ 12 | fun main(args: Array) = Client().main(args) 13 | 14 | private class Client { 15 | companion object { 16 | val logger = loggerFor() 17 | } 18 | 19 | fun main(args: Array) { 20 | // Create an RPC connection to the node. 21 | require(args.size == 3) { "Usage: Client " } 22 | val nodeAddress = parse(args[0]) 23 | val rpcUsername = args[1] 24 | val rpcPassword = args[2] 25 | val client = CordaRPCClient(nodeAddress) 26 | val clientConnection = client.start(rpcUsername, rpcPassword) 27 | val proxy = clientConnection.proxy 28 | 29 | // Interact with the node. 30 | // Example #1, here we print the nodes on the network. 31 | val nodes = proxy.networkMapSnapshot() 32 | println("\n-- Here is the networkMap snapshot --") 33 | logger.info("{}", nodes) 34 | 35 | // Example #2, here we print the PartyA's node info 36 | val me = proxy.nodeInfo().legalIdentities.first().name 37 | println("\n-- Here is the node info of the node that the client connected to --") 38 | logger.info("{}", me) 39 | 40 | //Close the client connection 41 | clientConnection.close() 42 | } 43 | } -------------------------------------------------------------------------------- /clients/src/main/kotlin/com/template/webserver/Controller.kt: -------------------------------------------------------------------------------- 1 | package com.template.webserver 2 | 3 | import org.slf4j.LoggerFactory 4 | import org.springframework.web.bind.annotation.GetMapping 5 | import org.springframework.web.bind.annotation.RequestMapping 6 | import org.springframework.web.bind.annotation.RestController 7 | 8 | /** 9 | * Define your API endpoints here. 10 | */ 11 | @RestController 12 | @RequestMapping("/") // The paths for HTTP requests are relative to this base path. 13 | class Controller(rpc: NodeRPCConnection) { 14 | 15 | companion object { 16 | private val logger = LoggerFactory.getLogger(RestController::class.java) 17 | } 18 | 19 | private val proxy = rpc.proxy 20 | 21 | @GetMapping(value = ["/templateendpoint"], produces = ["text/plain"]) 22 | private fun templateendpoint(): String { 23 | return "Define an endpoint here." 24 | } 25 | } -------------------------------------------------------------------------------- /clients/src/main/kotlin/com/template/webserver/NodeRPCConnection.kt: -------------------------------------------------------------------------------- 1 | package com.template.webserver 2 | 3 | import net.corda.client.rpc.CordaRPCClient 4 | import net.corda.client.rpc.CordaRPCConnection 5 | import net.corda.core.messaging.CordaRPCOps 6 | import net.corda.core.utilities.NetworkHostAndPort 7 | import org.springframework.beans.factory.annotation.Value 8 | import org.springframework.stereotype.Component 9 | import jakarta.annotation.PostConstruct 10 | import jakarta.annotation.PreDestroy 11 | 12 | private const val CORDA_USER_NAME = "config.rpc.username" 13 | private const val CORDA_USER_PASSWORD = "config.rpc.password" 14 | private const val CORDA_NODE_HOST = "config.rpc.host" 15 | private const val CORDA_RPC_PORT = "config.rpc.port" 16 | 17 | /** 18 | * Wraps an RPC connection to a Corda node. 19 | * 20 | * The RPC connection is configured using command line arguments. 21 | * 22 | * @param host The host of the node we are connecting to. 23 | * @param rpcPort The RPC port of the node we are connecting to. 24 | * @param username The username for logging into the RPC client. 25 | * @param password The password for logging into the RPC client. 26 | * @property proxy The RPC proxy. 27 | */ 28 | @Component 29 | open class NodeRPCConnection( 30 | @Value("\${$CORDA_NODE_HOST}") private val host: String, 31 | @Value("\${$CORDA_USER_NAME}") private val username: String, 32 | @Value("\${$CORDA_USER_PASSWORD}") private val password: String, 33 | @Value("\${$CORDA_RPC_PORT}") private val rpcPort: Int): AutoCloseable { 34 | 35 | lateinit var rpcConnection: CordaRPCConnection 36 | private set 37 | lateinit var proxy: CordaRPCOps 38 | private set 39 | 40 | @PostConstruct 41 | fun initialiseNodeRPCConnection() { 42 | val rpcAddress = NetworkHostAndPort(host, rpcPort) 43 | val rpcClient = CordaRPCClient(rpcAddress) 44 | val rpcConnection = rpcClient.start(username, password) 45 | proxy = rpcConnection.proxy 46 | } 47 | 48 | @PreDestroy 49 | override fun close() { 50 | rpcConnection.notifyServerAndClose() 51 | } 52 | } -------------------------------------------------------------------------------- /clients/src/main/kotlin/com/template/webserver/Server.kt: -------------------------------------------------------------------------------- 1 | package com.template.webserver 2 | 3 | import org.springframework.boot.Banner 4 | import org.springframework.boot.SpringApplication 5 | import org.springframework.boot.WebApplicationType.SERVLET 6 | import org.springframework.boot.autoconfigure.SpringBootApplication 7 | 8 | /** 9 | * Our Spring Boot application. 10 | */ 11 | @SpringBootApplication 12 | private open class Starter 13 | 14 | /** 15 | * Starts our Spring Boot application. 16 | */ 17 | fun main(args: Array) { 18 | val app = SpringApplication(Starter::class.java) 19 | app.setBannerMode(Banner.Mode.OFF) 20 | app.webApplicationType = SERVLET 21 | app.run(*args) 22 | } 23 | -------------------------------------------------------------------------------- /clients/src/main/resources/static/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Define your client-side logic here. -------------------------------------------------------------------------------- /clients/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example front-end. 6 | 7 | 8 |
9 | Corda 10 |

CorDapp Template (Kotlin Version)

11 |

Learn more about how to build CorDapps at sample-kotlin

12 |
13 | 14 | -------------------------------------------------------------------------------- /config/dev/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | logs 6 | node-${hostName} 7 | ${log-path}/archive 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | %highlight{%level{length=1} %d{HH:mm:ss} %T %c{1}.%M - %msg%n}{INFO=white,WARN=red,FATAL=bright red blink} 17 | > 18 | 19 | 20 | 21 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /config/test/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | [%-5level] %d{HH:mm:ss.SSS} [%t] %c{1}.%M - %msg%n 8 | > 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /constants.properties: -------------------------------------------------------------------------------- 1 | cordaReleaseGroup=net.corda 2 | cordaCoreReleaseGroup=net.corda 3 | cordaVersion=4.12 4 | cordaCoreVersion=4.12 5 | gradlePluginsVersion=5.1.1 6 | kotlinVersion=1.9.22 7 | junitVersion=4.12 8 | quasarVersion=0.9.0_r3 9 | log4jVersion=2.23.1 10 | platformVersion=12 11 | slf4jVersion=2.0.12 12 | nettyVersion=4.1.22.Final 13 | hibernateVersion=5.6.14.Final 14 | snappyVersion=0.4 -------------------------------------------------------------------------------- /contracts/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'net.corda.plugins.cordapp' 2 | 3 | cordapp { 4 | targetPlatformVersion corda_platform_version 5 | minimumPlatformVersion corda_platform_version 6 | contract { 7 | name "Template Contracts" 8 | vendor "Corda Open Source" 9 | licence "Apache License, Version 2.0" 10 | versionId 1 11 | } 12 | } 13 | 14 | dependencies { 15 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 16 | // Corda dependencies. 17 | cordaProvided "$corda_core_release_group:corda-core:$corda_core_release_version" 18 | cordaProvided "$corda_core_release_group:corda-core-test-utils:$corda_core_release_version" 19 | cordaProvided "$corda_core_release_group:corda-test-utils:$corda_core_release_version" 20 | testImplementation "$corda_release_group:corda-node-driver:$corda_release_version" 21 | 22 | testImplementation "junit:junit:$junit_version" 23 | } 24 | 25 | test { 26 | jvmArgs = rootProject.ext.test_add_opens 27 | } 28 | -------------------------------------------------------------------------------- /contracts/src/main/kotlin/com/template/contracts/TemplateContract.kt: -------------------------------------------------------------------------------- 1 | package com.template.contracts 2 | 3 | import com.template.states.TemplateState 4 | import net.corda.core.contracts.CommandData 5 | import net.corda.core.contracts.Contract 6 | import net.corda.core.contracts.requireSingleCommand 7 | import net.corda.core.transactions.LedgerTransaction 8 | import net.corda.core.contracts.requireThat 9 | // ************ 10 | // * Contract * 11 | // ************ 12 | class TemplateContract : Contract { 13 | companion object { 14 | // Used to identify our contract when building a transaction. 15 | const val ID = "com.template.contracts.TemplateContract" 16 | } 17 | 18 | // A transaction is valid if the verify() function of the contract of all the transaction's input and output states 19 | // does not throw an exception. 20 | override fun verify(tx: LedgerTransaction) { 21 | // Verification logic goes here. 22 | val command = tx.commands.requireSingleCommand() 23 | val output = tx.outputsOfType().first() 24 | when (command.value) { 25 | is Commands.Create -> requireThat { 26 | "No inputs should be consumed when sending the Hello-World message.".using(tx.inputStates.isEmpty()) 27 | "The message must be Hello-World".using(output.msg == "Hello-World") 28 | } 29 | } 30 | } 31 | 32 | // Used to indicate the transaction's intent. 33 | interface Commands : CommandData { 34 | class Create : Commands 35 | } 36 | } -------------------------------------------------------------------------------- /contracts/src/main/kotlin/com/template/states/TemplateState.kt: -------------------------------------------------------------------------------- 1 | package com.template.states 2 | 3 | import com.template.contracts.TemplateContract 4 | import net.corda.core.contracts.BelongsToContract 5 | import net.corda.core.contracts.ContractState 6 | import net.corda.core.identity.AbstractParty 7 | import net.corda.core.identity.Party 8 | 9 | // ********* 10 | // * State * 11 | // ********* 12 | @BelongsToContract(TemplateContract::class) 13 | data class TemplateState(val msg: String, 14 | val sender: Party, 15 | val receiver: Party, 16 | override val participants: List = listOf(sender,receiver) 17 | ) : ContractState 18 | -------------------------------------------------------------------------------- /contracts/src/test/kotlin/com/template/contracts/ContractTests.kt: -------------------------------------------------------------------------------- 1 | package com.template.contracts 2 | 3 | import net.corda.core.identity.CordaX500Name 4 | import net.corda.testing.core.TestIdentity 5 | import net.corda.testing.node.MockServices 6 | import net.corda.testing.node.ledger 7 | import org.junit.Test 8 | import com.template.states.TemplateState 9 | 10 | class ContractTests { 11 | private val ledgerServices: MockServices = MockServices(listOf("com.template")) 12 | var alice = TestIdentity(CordaX500Name("Alice", "TestLand", "US")) 13 | var bob = TestIdentity(CordaX500Name("Bob", "TestLand", "US")) 14 | 15 | @Test 16 | fun dummytest() { 17 | val state = TemplateState("Hello-World", alice.party, bob.party) 18 | ledgerServices.ledger { 19 | // Should fail bid price is equal to previous highest bid 20 | transaction { 21 | //failing transaction 22 | input(TemplateContract.ID, state) 23 | output(TemplateContract.ID, state) 24 | command(alice.publicKey, TemplateContract.Commands.Create()) 25 | fails() 26 | } 27 | //pass 28 | transaction { 29 | //passing transaction 30 | output(TemplateContract.ID, state) 31 | command(alice.publicKey, TemplateContract.Commands.Create()) 32 | verifies() 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /contracts/src/test/kotlin/com/template/contracts/StateTests.kt: -------------------------------------------------------------------------------- 1 | package com.template.contracts 2 | 3 | import com.template.states.TemplateState 4 | import org.junit.Test 5 | import kotlin.test.assertEquals 6 | 7 | class StateTests { 8 | @Test 9 | fun hasFieldOfCorrectType() { 10 | // Does the field exist? 11 | TemplateState::class.java.getDeclaredField("msg") 12 | // Is the field of the correct type? 13 | assertEquals(TemplateState::class.java.getDeclaredField("msg").type, String()::class.java) 14 | } 15 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | name=Test 2 | group=com.template 3 | version=0.1 4 | kotlin.incremental=false 5 | org.gradle.jvmargs=-Dfile.encoding=UTF-8 --add-opens 'java.base/java.time=ALL-UNNAMED' --add-opens 'java.base/java.io=ALL-UNNAMED' -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corda/cordapp-template-kotlin/fad66e3c151aef5c449556ad151d57aa03a12ee2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | if ! command -v java >/dev/null 2>&1 134 | then 135 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 136 | 137 | Please set the JAVA_HOME variable in your environment to match the 138 | location of your Java installation." 139 | fi 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | 201 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 202 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 203 | 204 | # Collect all arguments for the java command; 205 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 206 | # shell script including quotes and variable substitutions, so put them in 207 | # double quotes to make sure that they get re-expanded; and 208 | # * put everything else in single quotes, so that it's not re-expanded. 209 | 210 | set -- \ 211 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 212 | -classpath "$CLASSPATH" \ 213 | org.gradle.wrapper.GradleWrapperMain \ 214 | "$@" 215 | 216 | # Stop when "xargs" is not available. 217 | if ! command -v xargs >/dev/null 2>&1 218 | then 219 | die "xargs is not available" 220 | fi 221 | 222 | # Use "xargs" to parse quoted args. 223 | # 224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 225 | # 226 | # In Bash we could simply go: 227 | # 228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 229 | # set -- "${ARGS[@]}" "$@" 230 | # 231 | # but POSIX shell has neither arrays nor command substitution, so instead we 232 | # post-process each arg (as a line of input to sed) to backslash-escape any 233 | # character that might be a shell metacharacter, then use eval to reverse 234 | # that process (while maintaining the separation between arguments), and wrap 235 | # the whole thing up as a single "set" statement. 236 | # 237 | # This will of course break if any of these variables contains a newline or 238 | # an unmatched quote. 239 | # 240 | 241 | eval "set -- $( 242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 243 | xargs -n1 | 244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 245 | tr '\n' ' ' 246 | )" '"$@"' 247 | 248 | exec "$JAVACMD" "$@" 249 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /repositories.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenLocal() 3 | mavenCentral() 4 | maven { url 'https://jitpack.io' } 5 | maven { url 'https://download.corda.net/maven/corda-dependencies' } 6 | maven { url 'https://download.corda.net/maven/corda-releases' } 7 | maven { url 'https://repo.gradle.org/gradle/libs-releases' } 8 | } 9 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'workflows' 2 | include 'contracts' 3 | include 'clients' -------------------------------------------------------------------------------- /workflows/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'net.corda.plugins.cordapp' 2 | apply plugin: 'net.corda.plugins.quasar-utils' 3 | 4 | cordapp { 5 | targetPlatformVersion corda_platform_version 6 | minimumPlatformVersion corda_platform_version 7 | workflow { 8 | name "Template Flows" 9 | vendor "Corda Open Source" 10 | licence "Apache License, Version 2.0" 11 | versionId 1 12 | } 13 | } 14 | 15 | sourceSets { 16 | main { 17 | resources { 18 | srcDir rootProject.file("config/dev") 19 | } 20 | } 21 | test { 22 | resources { 23 | srcDir rootProject.file("config/test") 24 | } 25 | } 26 | integrationTest { 27 | kotlin { 28 | compileClasspath += main.output + test.output 29 | runtimeClasspath += main.output + test.output 30 | srcDir file('src/integrationTest/kotlin') 31 | } 32 | } 33 | } 34 | 35 | configurations { 36 | integrationTestImplementation.extendsFrom testImplementation 37 | integrationTestRuntimeOnly.extendsFrom testRuntimeOny 38 | } 39 | 40 | dependencies { 41 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 42 | testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" 43 | testImplementation "junit:junit:$junit_version" 44 | 45 | // Corda dependencies. 46 | cordaProvided "$corda_core_release_group:corda-core:$corda_core_release_version" 47 | cordaProvided "$corda_release_group:corda:$corda_release_version" 48 | testImplementation "$corda_release_group:corda-node-driver:$corda_release_version" 49 | testImplementation "$corda_core_release_group:corda-core-test-utils:$corda_core_release_version" 50 | 51 | // CorDapp dependencies. 52 | cordapp project(":contracts") 53 | 54 | testRuntimeOnly "org.iq80.snappy:snappy:$snappy_version" 55 | } 56 | 57 | task integrationTest(type: Test, dependsOn: []) { 58 | testClassesDirs = sourceSets.integrationTest.output.classesDirs 59 | classpath = sourceSets.integrationTest.runtimeClasspath 60 | jvmArgs = rootProject.ext.test_add_opens + test_add_opens_fx 61 | } 62 | 63 | test { 64 | jvmArgs = rootProject.ext.test_add_opens 65 | } 66 | -------------------------------------------------------------------------------- /workflows/src/integrationTest/kotlin/com/template/DriverBasedTest.kt: -------------------------------------------------------------------------------- 1 | package com.template 2 | 3 | import net.corda.core.identity.CordaX500Name 4 | import net.corda.core.utilities.getOrThrow 5 | import net.corda.testing.core.TestIdentity 6 | import net.corda.testing.driver.DriverDSL 7 | import net.corda.testing.driver.DriverParameters 8 | import net.corda.testing.driver.NodeHandle 9 | import net.corda.testing.driver.driver 10 | import org.junit.Test 11 | import java.util.concurrent.Future 12 | import kotlin.test.assertEquals 13 | 14 | class DriverBasedTest { 15 | private val bankA = TestIdentity(CordaX500Name("BankA", "", "GB")) 16 | private val bankB = TestIdentity(CordaX500Name("BankB", "", "US")) 17 | 18 | @Test 19 | fun `node test`() = withDriver { 20 | // Start a pair of nodes and wait for them both to be ready. 21 | val (partyAHandle, partyBHandle) = startNodes(bankA, bankB) 22 | 23 | // From each node, make an RPC call to retrieve another node's name from the network map, to verify that the 24 | // nodes have started and can communicate. 25 | 26 | // This is a very basic test: in practice tests would be starting flows, and verifying the states in the vault 27 | // and other important metrics to ensure that your CorDapp is working as intended. 28 | assertEquals(bankB.name, partyAHandle.resolveName(bankB.name)) 29 | assertEquals(bankA.name, partyBHandle.resolveName(bankA.name)) 30 | } 31 | 32 | // Runs a test inside the Driver DSL, which provides useful functions for starting nodes, etc. 33 | private fun withDriver(test: DriverDSL.() -> Unit) = driver( 34 | DriverParameters(isDebug = true, startNodesInProcess = true) 35 | ) { test() } 36 | 37 | // Makes an RPC call to retrieve another node's name from the network map. 38 | private fun NodeHandle.resolveName(name: CordaX500Name) = rpc.wellKnownPartyFromX500Name(name)!!.name 39 | 40 | // Resolves a list of futures to a list of the promised values. 41 | private fun List>.waitForAll(): List = map { it.getOrThrow() } 42 | 43 | // Starts multiple nodes simultaneously, then waits for them all to be ready. 44 | private fun DriverDSL.startNodes(vararg identities: TestIdentity) = identities 45 | .map { startNode(providedName = it.name) } 46 | .waitForAll() 47 | } -------------------------------------------------------------------------------- /workflows/src/main/kotlin/com/template/flows/Flows.kt: -------------------------------------------------------------------------------- 1 | package com.template.flows 2 | 3 | import co.paralleluniverse.fibers.Suspendable 4 | import net.corda.core.flows.* 5 | import net.corda.core.identity.* 6 | import net.corda.core.utilities.ProgressTracker 7 | import net.corda.core.flows.FinalityFlow 8 | 9 | import net.corda.core.flows.CollectSignaturesFlow 10 | 11 | import net.corda.core.transactions.SignedTransaction 12 | 13 | import java.util.stream.Collectors 14 | 15 | import net.corda.core.flows.FlowSession 16 | 17 | import net.corda.core.identity.Party 18 | 19 | import com.template.contracts.TemplateContract 20 | 21 | import net.corda.core.transactions.TransactionBuilder 22 | 23 | import com.template.states.TemplateState 24 | import net.corda.core.contracts.requireThat 25 | import net.corda.core.identity.AbstractParty 26 | 27 | 28 | // ********* 29 | // * Flows * 30 | // ********* 31 | @InitiatingFlow 32 | @StartableByRPC 33 | class Initiator(private val receiver: Party) : FlowLogic() { 34 | override val progressTracker = ProgressTracker() 35 | 36 | @Suspendable 37 | override fun call(): SignedTransaction { 38 | //Hello World message 39 | val msg = "Hello-World" 40 | val sender = ourIdentity 41 | 42 | // Step 1. Get a reference to the notary service on our network and our key pair. 43 | // Note: ongoing work to support multiple notary identities is still in progress. 44 | val notary = serviceHub.networkMapCache.getNotary( CordaX500Name.parse("O=Notary,L=London,C=GB")) 45 | 46 | //Compose the State that carries the Hello World message 47 | val output = TemplateState(msg, sender, receiver) 48 | 49 | // Step 3. Create a new TransactionBuilder object. 50 | val builder = TransactionBuilder(notary) 51 | .addCommand(TemplateContract.Commands.Create(), listOf(sender.owningKey, receiver.owningKey)) 52 | .addOutputState(output) 53 | 54 | // Step 4. Verify and sign it with our KeyPair. 55 | builder.verify(serviceHub) 56 | val ptx = serviceHub.signInitialTransaction(builder) 57 | 58 | 59 | // Step 6. Collect the other party's signature using the SignTransactionFlow. 60 | val otherParties: MutableList = output.participants.stream().map { el: AbstractParty? -> el as Party? }.collect(Collectors.toList()) 61 | otherParties.remove(ourIdentity) 62 | val sessions = otherParties.stream().map { el: Party? -> initiateFlow(el!!) }.collect(Collectors.toList()) 63 | 64 | val stx = subFlow(CollectSignaturesFlow(ptx, sessions)) 65 | 66 | // Step 7. Assuming no exceptions, we can now finalise the transaction 67 | return subFlow(FinalityFlow(stx, sessions)) 68 | } 69 | } 70 | 71 | @InitiatedBy(Initiator::class) 72 | class Responder(val counterpartySession: FlowSession) : FlowLogic() { 73 | @Suspendable 74 | override fun call(): SignedTransaction { 75 | val signTransactionFlow = object : SignTransactionFlow(counterpartySession) { 76 | override fun checkTransaction(stx: SignedTransaction) = requireThat { 77 | //Addition checks 78 | } 79 | } 80 | val txId = subFlow(signTransactionFlow).id 81 | return subFlow(ReceiveFinalityFlow(counterpartySession, expectedTxId = txId)) 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /workflows/src/test/kotlin/com/template/FlowTests.kt: -------------------------------------------------------------------------------- 1 | package com.template 2 | 3 | import net.corda.testing.node.* 4 | import org.junit.After 5 | import org.junit.Before 6 | import org.junit.Test 7 | import com.template.states.TemplateState 8 | import java.util.concurrent.Future; 9 | import net.corda.core.node.services.vault.QueryCriteria 10 | import net.corda.core.transactions.SignedTransaction 11 | import com.template.flows.Initiator 12 | import net.corda.core.identity.CordaX500Name 13 | import net.corda.core.node.services.Vault.StateStatus 14 | 15 | 16 | class FlowTests { 17 | private lateinit var network: MockNetwork 18 | private lateinit var a: StartedMockNode 19 | private lateinit var b: StartedMockNode 20 | 21 | @Before 22 | fun setup() { 23 | network = MockNetwork(MockNetworkParameters(cordappsForAllNodes = listOf( 24 | TestCordapp.findCordapp("com.template.contracts"), 25 | TestCordapp.findCordapp("com.template.flows") 26 | ), 27 | notarySpecs = listOf(MockNetworkNotarySpec(CordaX500Name("Notary","London","GB"))))) 28 | a = network.createPartyNode() 29 | b = network.createPartyNode() 30 | network.runNetwork() 31 | } 32 | 33 | @After 34 | fun tearDown() { 35 | network.stopNodes() 36 | } 37 | @Test 38 | fun `DummyTest`() { 39 | val flow = Initiator(b.info.legalIdentities[0]) 40 | val future: Future = a.startFlow(flow) 41 | network.runNetwork() 42 | 43 | //successful query means the state is stored at node b's vault. Flow went through. 44 | val inputCriteria: QueryCriteria = QueryCriteria.VaultQueryCriteria().withStatus(StateStatus.UNCONSUMED) 45 | val states = b.services.vaultService.queryBy(TemplateState::class.java, inputCriteria).states[0].state.data 46 | } 47 | } --------------------------------------------------------------------------------