From b4a033de23115304e6786e56325d86b1c6fee73c Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Tue, 19 Feb 2019 11:45:04 +0000 Subject: [PATCH 01/75] RELEASE - Corda 4 RC06 prep --- build.gradle | 2 +- constants.properties | 2 +- core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 3fe642542e..1ad9637890 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { file("$projectDir/constants.properties").withInputStream { constants.load(it) } // Our version: bump this on release. - ext.corda_release_version = "5.0-SNAPSHOT" + ext.corda_release_version = "4.0-RC06" ext.corda_platform_version = constants.getProperty("platformVersion") ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion") diff --git a/constants.properties b/constants.properties index 14f4bd71ed..97045c2585 100644 --- a/constants.properties +++ b/constants.properties @@ -4,7 +4,7 @@ kotlinVersion=1.2.71 # When incrementing platformVersion make sure to update # # net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. # # ***************************************************************# -platformVersion=5 +platformVersion=4 guavaVersion=25.1-jre proguardVersion=6.0.3 bouncycastleVersion=1.60 diff --git a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt index 9515c3c17d..0d6d6dfd6e 100644 --- a/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/CordaUtils.kt @@ -25,7 +25,7 @@ import java.util.jar.JarInputStream // *Internal* Corda-specific utilities. -const val PLATFORM_VERSION = 5 +const val PLATFORM_VERSION = 4 fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) { checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature) From 37627ae256ac871547f5bdcd029e58cb7b530139 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Tue, 19 Feb 2019 19:42:32 +0100 Subject: [PATCH 02/75] Bugfix: register SwapIdentitiesHandler again. (#4789) --- client/rpc/build.gradle | 4 ---- node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt | 2 ++ .../kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt | 2 +- samples/irs-demo/cordapp/build.gradle | 4 +--- samples/simm-valuation-demo/build.gradle | 2 -- samples/trader-demo/workflows-trader/build.gradle | 1 - 6 files changed, 4 insertions(+), 11 deletions(-) diff --git a/client/rpc/build.gradle b/client/rpc/build.gradle index c30049ee08..5d0eb0a662 100644 --- a/client/rpc/build.gradle +++ b/client/rpc/build.gradle @@ -62,9 +62,6 @@ processSmokeTestResources { from(project(':finance:contracts').tasks['jar']) { rename '.*finance-contracts-.*', 'cordapp-finance-contracts.jar' } - from(project(':confidential-identities').tasks['jar']) { - rename '.*confidential-identities-.*', 'cordapp-confidential-identities.jar' - } } // To find potential version conflicts, run "gradle htmlDependencyReport" and then look in @@ -90,7 +87,6 @@ dependencies { smokeTestCompile project(':smoke-test-utils') smokeTestCompile project(':finance:contracts') smokeTestCompile project(':finance:workflows') - smokeTestCompile project(':confidential-identities') smokeTestCompile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" smokeTestCompile "org.apache.logging.log4j:log4j-core:$log4j_version" smokeTestCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index b1e428e69f..27345da35f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -4,6 +4,7 @@ import com.codahale.metrics.MetricRegistry import com.google.common.collect.MutableClassToInstanceMap import com.google.common.util.concurrent.MoreExecutors import com.zaxxer.hikari.pool.HikariPool +import net.corda.confidential.SwapIdentitiesFlow import net.corda.core.CordaException import net.corda.core.concurrent.CordaFuture import net.corda.core.context.InvocationContext @@ -684,6 +685,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, installFinalityHandler() flowManager.registerInitiatedCoreFlowFactory(NotaryChangeFlow::class, NotaryChangeHandler::class, ::NotaryChangeHandler) flowManager.registerInitiatedCoreFlowFactory(ContractUpgradeFlow.Initiate::class, NotaryChangeHandler::class, ::ContractUpgradeHandler) + flowManager.registerInitiatedCoreFlowFactory(SwapIdentitiesFlow::class, SwapIdentitiesHandler::class, ::SwapIdentitiesHandler) } // Ideally we should be disabling the FinalityHandler if it's not needed, to prevent any party from submitting transactions to us without diff --git a/node/src/main/kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt b/node/src/main/kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt index f1c8aff9f3..3b6b08b307 100644 --- a/node/src/main/kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt +++ b/node/src/main/kotlin/net/corda/node/internal/SwapIdentitiesHandler.kt @@ -12,7 +12,7 @@ import net.corda.core.internal.warnOnce * but it is a complex versioning problem because we don't know which peers we might interact with. Disabling it will probably have to be * gated on a minPlatformVersion bump. */ -@InitiatedBy(SwapIdentitiesFlow::class) +//@InitiatedBy(SwapIdentitiesFlow::class) class SwapIdentitiesHandler(private val otherSide: FlowSession) : FlowLogic() { @Suspendable override fun call() { diff --git a/samples/irs-demo/cordapp/build.gradle b/samples/irs-demo/cordapp/build.gradle index 2891083335..3ffb92b4b4 100644 --- a/samples/irs-demo/cordapp/build.gradle +++ b/samples/irs-demo/cordapp/build.gradle @@ -20,7 +20,7 @@ sourceSets { dependencies { cordapp project(':finance:contracts') cordapp project(':finance:workflows') - cordapp project(':confidential-identities') + // Corda integration dependencies cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaCompile project(':core') @@ -52,7 +52,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask]) } cordapp project(':samples:irs-demo:cordapp:contracts-irs') cordapp project(':samples:irs-demo:cordapp:workflows-irs') - cordapp project(':confidential-identities') } node { name "O=Notary Service,L=Zurich,C=CH" @@ -111,7 +110,6 @@ task prepareDockerNodes(type: net.corda.plugins.Dockerform, dependsOn: ['jar', n nodeDefaults{ cordapp project(':samples:irs-demo:cordapp:contracts-irs') cordapp project(':samples:irs-demo:cordapp:workflows-irs') - cordapp project(':confidential-identities') } node { name "O=Notary Service,L=Zurich,C=CH" diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle index a9708fdebd..b7db45d7aa 100644 --- a/samples/simm-valuation-demo/build.gradle +++ b/samples/simm-valuation-demo/build.gradle @@ -33,7 +33,6 @@ dependencies { cordapp project(':finance:workflows') cordapp project(path: ':samples:simm-valuation-demo:contracts-states', configuration: 'shrinkArtifacts') cordapp project(':samples:simm-valuation-demo:flows') - cordapp project(':confidential-identities') // Corda integration dependencies cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') @@ -72,7 +71,6 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, cordapp project(':finance:workflows') cordapp project(':samples:simm-valuation-demo:contracts-states') cordapp project(':samples:simm-valuation-demo:flows') - cordapp project(':confidential-identities') rpcUsers = [['username': "default", 'password': "default", 'permissions': [ 'ALL' ]]] } node { diff --git a/samples/trader-demo/workflows-trader/build.gradle b/samples/trader-demo/workflows-trader/build.gradle index 614a817ea1..05f6cf8178 100644 --- a/samples/trader-demo/workflows-trader/build.gradle +++ b/samples/trader-demo/workflows-trader/build.gradle @@ -24,7 +24,6 @@ dependencies { // The trader demo CorDapp depends upon Cash CorDapp features cordapp project(':finance:contracts') cordapp project(':finance:workflows') - cordapp project(':confidential-identities') cordapp project(':samples:bank-of-corda-demo') // Corda integration dependencies From fd1f3f7e974cc07d25508ac39d97a3c0c00ea3b5 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Tue, 19 Feb 2019 18:47:10 +0000 Subject: [PATCH 03/75] RELEASE - Seventh release candidate --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1ad9637890..d4d210491a 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { file("$projectDir/constants.properties").withInputStream { constants.load(it) } // Our version: bump this on release. - ext.corda_release_version = "4.0-RC06" + ext.corda_release_version = "4.0-RC07" ext.corda_platform_version = constants.getProperty("platformVersion") ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion") From 503a2fff772d46ffcbe6719647013870b5954278 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Wed, 20 Feb 2019 15:16:39 +0000 Subject: [PATCH 04/75] RELEASE - Version 4 final changes --- build.gradle | 2 +- docs/source/_static/versions | 4 ++++ docs/source/conf.py | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index d4d210491a..43b71d0801 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { file("$projectDir/constants.properties").withInputStream { constants.load(it) } // Our version: bump this on release. - ext.corda_release_version = "4.0-RC07" + ext.corda_release_version = "4.0" ext.corda_platform_version = constants.getProperty("platformVersion") ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion") diff --git a/docs/source/_static/versions b/docs/source/_static/versions index abeff3b667..775f9cb83f 100644 --- a/docs/source/_static/versions +++ b/docs/source/_static/versions @@ -11,5 +11,9 @@ "https://docs.corda.net/releases/release-V1.0": "V1.0", "https://docs.corda.net/releases/release-V2.0": "V2.0", "https://docs.corda.net/releases/release-V3.0": "V3.0", + "https://docs.corda.net/releases/release-V3.1": "V3.1", + "https://docs.corda.net/releases/release-V3.2": "V3.2", + "https://docs.corda.net/releases/release-V3.3": "V3.3", + "https://docs.corda.net/releases/release-V4.0": "V4.0", "https://docs.corda.net/head/": "Master" } diff --git a/docs/source/conf.py b/docs/source/conf.py index 961bb45bb6..8aea05c0ef 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -60,11 +60,11 @@ author = u'R3 DLG' # built documents. # # The short X.Y version. -version = 'Master' +version = 'V4.0' # The full version, including alpha/beta/rc tags. -release = 'Master' +release = 'V4.0' # The version for use in the dropdown html. -html_context = {'version': 'Master'} +html_context = {'version': 'V4.0'} # Properties. kotlin_version = '1.2.71' From 9a1ac7095967302fcfd959e24451519ef88f07ac Mon Sep 17 00:00:00 2001 From: Anthony Keenan Date: Wed, 20 Feb 2019 18:21:56 +0000 Subject: [PATCH 05/75] CORDA-2615 - Remove incorrect documentation (#4791) * Remove incorrect documentation * Tidying up * Another go at clarifying what will work * Added another line for clarity * More clarification * Some more clarification * Minor change --- docs/source/cordapp-build-systems.rst | 49 +++++++++++++++++++-------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/docs/source/cordapp-build-systems.rst b/docs/source/cordapp-build-systems.rst index 5ddfe945d2..5444790601 100644 --- a/docs/source/cordapp-build-systems.rst +++ b/docs/source/cordapp-build-systems.rst @@ -527,29 +527,48 @@ For a CorDapp that contains flows and/or services we specify the `workflow` tag: CorDapp Contract Attachments ---------------------------- -As of Corda 4, CorDapp Contract JARs must be installed on a node by a trusted uploader, either by +As of Corda 4, because there is no Sandbox to run the verification code we require that any jar with code that is downloaded from a peer to be +checked and explicitly whitelisted by the node operator. CorDapp contract JARs must be installed on a node by a trusted uploader, by - installing manually as per :ref:`Installing the CorDapp JAR ` and re-starting the node. - uploading the attachment JAR to the node via RPC, either programmatically (see :ref:`Connecting to a node via RPC `) - or via the :doc:`shell` by issuing the following command: -``>>> run uploadAttachment jar: path/to/the/file.jar`` - -Contract attachments that are received from a peer over the p2p network are considered **untrusted** and will throw a `UntrustedAttachmentsException` exception -when processed by a listening flow that cannot resolve that attachment from its local attachment storage. The flow will be aborted and sent to the nodes flow hospital for recovery and retry. -The untrusted attachment JAR will be stored in the nodes local attachment store for review by a node operator. It can be downloaded for viewing using the following CRaSH shell command: - -``>>> run openAttachment id: >> run uploadAttachment jar: path/to/the/trusted-file.jar`` - -and subsequently retry the failed flow (currently this requires a node re-start). +Which method to use depends on the reason for installing the CorDapp and is detailed below. .. note:: this behaviour is to protect the node from executing contract code that was not vetted. It is a temporary precaution until the Deterministic JVM is integrated into Corda whereby execution takes place in a sandboxed environment which protects the node from malicious code. +Installing Contract Attachments for Previously Unknown CorDapps +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When you are processing a transaction that contains states that need to be verified by contracts in CorDapps not currently installed on the node, the +Contract attachment will be received from a peer over the p2p network. CorDapps received this way are considered **untrusted** and will throw an `UntrustedAttachmentsException` +when processed by a listening flow that cannot resolve that attachment from its local attachment storage. The flow will be aborted and sent to the nodes flow hospital for recovery and retry. +The untrusted attachment JAR will be stored in the nodes local attachment store for review by a node operator. It can be downloaded for viewing using the following CRaSH shell command: + +``>>> run openAttachment id: `) +and subsequently retry the failed flow. Currently this requires a node-restart which will automatically retry the failed flows. + +.. note:: from Corda 4.1 you will also be able to upload the attachment to the store, as described below. + +Installing Contract Attachments for Older Versions of CorDapps +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you need to install older versions of a CorDapp in order to verify chains of states created with older versions of a contract, you can upload the +older CorDapp to the attachment store. Placing the older CorDapp in the ``cordapps`` directory will not work in this case, +as you can only have one CorDapp loaded per contract. The latest version of the CorDapp should be the one installed in the ``cordapps`` folder. + +As above, the untrusted attachment JAR will be stored in the nodes local attachment store for review by a node operator. It can be downloaded for +viewing using the following CRaSH shell command: + +``>>> run openAttachment id: >> run uploadAttachment jar: path/to/the/trusted-file.jar`` + +and subsequently retry the failed flow. Currently this requires a node-restart which will automatically retry the failed flows. From f9e07c01580736bfd877bf4fe289b719127e8dcd Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Thu, 21 Feb 2019 17:44:46 +0000 Subject: [PATCH 06/75] CORDA-2654 - Repair the DJVM CLI tool. (#4796) (#4799) * Replace SandboxedRunnable with Function interface. Remove DJVM from "Key Concepts" release notes. Update installation of shell tool. Fix broken sandbox package names. Make sure we only resolve each class once when loading. Also remove some unused default parameter values. Don't discard "bootstrap" sandbox.* classes because SourceClassLoader may need them. * Restore DJVM to the "Key Concepts" docs. * Remove all mention of "whitelisting" from the DJVM CLI. --- djvm/build.gradle | 16 ---- .../net/corda/djvm/tools/cli/ClassCommand.kt | 13 +-- .../net/corda/djvm/tools/cli/Commands.kt | 3 +- .../net/corda/djvm/tools/cli/NewCommand.kt | 8 +- .../net/corda/djvm/tools/cli/Utilities.kt | 4 +- .../corda/djvm/tools/cli/WhitelistCommand.kt | 14 --- .../tools/cli/WhitelistGenerateCommand.kt | 92 ------------------- .../djvm/tools/cli/WhitelistShowCommand.kt | 33 ------- djvm/shell/djvm | 2 +- djvm/shell/install | 2 +- .../corda/djvm/rewiring/SandboxClassLoader.kt | 2 +- .../net/corda/djvm/source/ClassSource.kt | 4 +- docs/source/key-concepts-djvm.rst | 34 ++----- 13 files changed, 21 insertions(+), 206 deletions(-) delete mode 100644 djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistCommand.kt delete mode 100644 djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistGenerateCommand.kt delete mode 100644 djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistShowCommand.kt diff --git a/djvm/build.gradle b/djvm/build.gradle index 64bd5bbd53..fd207b04a5 100644 --- a/djvm/build.gradle +++ b/djvm/build.gradle @@ -51,22 +51,6 @@ shadowJar { baseName 'corda-djvm' classifier '' relocate 'org.objectweb.asm', 'djvm.org.objectweb.asm' - - // These particular classes are only needed to "bootstrap" - // the compilation of the other sandbox classes. At runtime, - // we will generate better versions from deterministic-rt.jar. - exclude 'sandbox/java/lang/Appendable.class' - exclude 'sandbox/java/lang/CharSequence.class' - exclude 'sandbox/java/lang/Character\$Subset.class' - exclude 'sandbox/java/lang/Character\$Unicode*.class' - exclude 'sandbox/java/lang/Comparable.class' - exclude 'sandbox/java/lang/Enum.class' - exclude 'sandbox/java/lang/Iterable.class' - exclude 'sandbox/java/lang/StackTraceElement.class' - exclude 'sandbox/java/lang/StringBuffer.class' - exclude 'sandbox/java/lang/StringBuilder.class' - exclude 'sandbox/java/nio/**' - exclude 'sandbox/java/util/**' } assemble.dependsOn shadowJar diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt index e0fdb7f745..db6460a2a8 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/ClassCommand.kt @@ -32,14 +32,6 @@ abstract class ClassCommand : CommandBase() { @Option(names = ["--ignore-definition-providers"], description = ["Disable all definition providers."]) var ignoreDefinitionProviders: Boolean = false - @Option( - names = ["-w", "--whitelist"], - description = ["Override the default whitelist. Use provided whitelist instead. If NONE is provided, the " + - "whitelist will be ignored. If ALL is provided, all references will be whitelisted. LANG can be " + - "used to only whitelist select classes and their members from the java.lang package."] - ) - var whitelist: Path? = null - @Option(names = ["-c", "--classpath"], description = ["Additions to the default class path."], split = ":") var classPath: Array = emptyArray() @@ -65,8 +57,6 @@ abstract class ClassCommand : CommandBase() { protected var executor = SandboxExecutor(SandboxConfiguration.DEFAULT) - private var derivedWhitelist: Whitelist = Whitelist.MINIMAL - abstract fun processClasses(classes: List>) open fun printSuccess(classes: List>) {} @@ -74,8 +64,7 @@ abstract class ClassCommand : CommandBase() { override fun validateArguments() = filters.isNotEmpty() override fun handleCommand(): Boolean { - derivedWhitelist = whitelistFromPath(whitelist) - val configuration = getConfiguration(derivedWhitelist) + val configuration = getConfiguration(Whitelist.MINIMAL) classLoader = SourceClassLoader(getClasspath(), configuration.analysisConfiguration.classResolver) createExecutor(configuration) diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt index 65dd4c9ed4..b76fc3b6db 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Commands.kt @@ -15,8 +15,7 @@ import picocli.CommandLine.Command NewCommand::class, RunCommand::class, ShowCommand::class, - TreeCommand::class, - WhitelistCommand::class + TreeCommand::class ] ) @Suppress("KDocMissingDocumentation") diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt index 8e6302de4e..22c6e96aee 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/NewCommand.kt @@ -55,13 +55,13 @@ class NewCommand : CommandBase() { companion object { val TEMPLATE = """ - |package net.corda.djvm; + |package net.corda.sandbox; | - |import net.corda.djvm.execution.SandboxedRunnable; + |import java.util.function.Function; | - |public class [NAME] implements SandboxedRunnable<[FROM], [TO]> { + |public class [NAME] implements Function<[FROM], [TO]> { | @Override - | public [TO] run([FROM] input) { + | public [TO] apply([FROM] input) { | return [RETURN]; | } |} diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt index adc88a1423..d488aa7253 100644 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt +++ b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/Utilities.kt @@ -60,7 +60,7 @@ fun openOptions(force: Boolean) = if (force) { * Get the path of where any generated code will be placed. Create the directory if it does not exist. */ fun createCodePath(): Path { - return Paths.get("tmp", "net", "corda", "djvm").let { + return Paths.get("tmp", "net", "corda", "sandbox").let { Files.createDirectories(it) } } @@ -92,7 +92,7 @@ val userClassPath: String = System.getProperty("java.class.path") /** * Get a reference of each concrete class that implements interface or class [T]. */ -inline fun find(scanSpec: String = "net/corda/djvm"): List> { +inline fun find(scanSpec: String = "net/corda/sandbox"): List> { return ClassGraph() .whitelistPaths(scanSpec) .enableAllInfo() diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistCommand.kt deleted file mode 100644 index 7671f9c246..0000000000 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistCommand.kt +++ /dev/null @@ -1,14 +0,0 @@ -package net.corda.djvm.tools.cli - -import picocli.CommandLine.Command - -@Command( - name = "whitelist", - description = ["Utilities and commands related to the whitelist for the deterministic sandbox."], - subcommands = [ - WhitelistGenerateCommand::class, - WhitelistShowCommand::class - ] -) -@Suppress("KDocMissingDocumentation") -class WhitelistCommand : CommandBase() diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistGenerateCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistGenerateCommand.kt deleted file mode 100644 index 85a8084654..0000000000 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistGenerateCommand.kt +++ /dev/null @@ -1,92 +0,0 @@ -package net.corda.djvm.tools.cli - -import net.corda.djvm.analysis.AnalysisConfiguration -import net.corda.djvm.analysis.AnalysisContext -import net.corda.djvm.analysis.ClassAndMemberVisitor -import net.corda.djvm.references.ClassRepresentation -import net.corda.djvm.references.Member -import net.corda.djvm.source.ClassSource -import picocli.CommandLine.* -import java.io.PrintStream -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardOpenOption -import java.util.zip.GZIPOutputStream - -@Command( - name = "generate", - description = ["Generate and export whitelist from the class and member declarations provided in one or more " + - "JARs."] -) -@Suppress("KDocMissingDocumentation") -class WhitelistGenerateCommand : CommandBase() { - - @Parameters(description = ["The paths of the JARs that the whitelist is to be generated from."]) - var paths: Array = emptyArray() - - @Option( - names = ["-o", "--output"], - description = ["The file to which the whitelist will be written. If not provided, STDOUT will be used."] - ) - var output: Path? = null - - override fun validateArguments() = paths.isNotEmpty() - - override fun handleCommand(): Boolean { - val entries = AnalysisConfiguration.createRoot().use { configuration -> - val entries = mutableListOf() - val visitor = object : ClassAndMemberVisitor(configuration, null) { - override fun visitClass(clazz: ClassRepresentation): ClassRepresentation { - entries.add(clazz.name) - return super.visitClass(clazz) - } - - override fun visitMethod(clazz: ClassRepresentation, method: Member): Member { - visitMember(clazz, method) - return super.visitMethod(clazz, method) - } - - override fun visitField(clazz: ClassRepresentation, field: Member): Member { - visitMember(clazz, field) - return super.visitField(clazz, field) - } - - private fun visitMember(clazz: ClassRepresentation, member: Member) { - entries.add("${clazz.name}.${member.memberName}:${member.signature}") - } - } - val context = AnalysisContext.fromConfiguration(configuration) - for (path in paths) { - ClassSource.fromPath(path).getStreamIterator().forEach { - visitor.analyze(it, context) - } - } - entries - } - output?.also { - Files.newOutputStream(it, StandardOpenOption.CREATE).use { out -> - GZIPOutputStream(out).use { gzip -> - PrintStream(gzip).use { pout -> - pout.println(""" - |java/.* - |javax/.* - |jdk/.* - |com/sun/.* - |sun/.* - |--- - """.trimMargin().trim()) - printEntries(pout, entries) - } - } - } - } ?: printEntries(System.out, entries) - return true - } - - private fun printEntries(stream: PrintStream, entries: List) { - for (entry in entries.sorted().distinct()) { - stream.println(entry) - } - } - -} diff --git a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistShowCommand.kt b/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistShowCommand.kt deleted file mode 100644 index 85ec05fdc1..0000000000 --- a/djvm/cli/src/main/kotlin/net/corda/djvm/tools/cli/WhitelistShowCommand.kt +++ /dev/null @@ -1,33 +0,0 @@ -package net.corda.djvm.tools.cli - -import picocli.CommandLine.* -import java.nio.file.Path - -@Command( - name = "show", - description = ["Print the whitelist used for the deterministic sandbox."] -) -@Suppress("KDocMissingDocumentation") -class WhitelistShowCommand : CommandBase() { - - @Option( - names = ["-w", "--whitelist"], - description = ["Override the default whitelist. Use provided whitelist instead."] - ) - var whitelist: Path? = null - - @Parameters(description = ["Words or phrases to use to filter down the result."]) - var filters: Array = emptyArray() - - override fun validateArguments() = true - - override fun handleCommand(): Boolean { - val whitelist = whitelistFromPath(whitelist) - val filters = filters.map(String::toLowerCase) - whitelist.items - .filter { item -> filters.all { it in item.toLowerCase() } } - .forEach { println(it) } - return true - } - -} diff --git a/djvm/shell/djvm b/djvm/shell/djvm index 5388332d2f..ec3c19e5a7 100755 --- a/djvm/shell/djvm +++ b/djvm/shell/djvm @@ -4,7 +4,7 @@ file="${BASH_SOURCE[0]}" linked_file="$(test -L "$file" && readlink "$file" || echo "$file")" base_dir="$(cd "$(dirname "$linked_file")/../" && pwd)" version="$(cat $base_dir/../build.gradle | sed -n 's/^[ ]*ext\.corda_release_version[ =]*"\([^"]*\)".*$/\1/p')" -jar_file="$base_dir/cli/build/libs/corda-djvm-$version-all.jar" +jar_file="$base_dir/cli/build/libs/corda-djvm-$version-cli.jar" CLASSPATH="${CLASSPATH:-}" diff --git a/djvm/shell/install b/djvm/shell/install index 4874c00583..7e458e0bb4 100755 --- a/djvm/shell/install +++ b/djvm/shell/install @@ -10,7 +10,7 @@ cd "$base_dir/.." # Generate auto-completion file for Bash and ZSH cd "$base_dir" -java -cp "$base_dir/../cli/build/libs/corda-djvm-$version-all.jar" \ +java -cp "$base_dir/../cli/build/libs/corda-djvm-$version-cli.jar" \ picocli.AutoComplete -n djvm net.corda.djvm.tools.cli.Commands -f # Create a symbolic link to the `djvm` utility diff --git a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt b/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt index c1b3475534..16ed666419 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/rewiring/SandboxClassLoader.kt @@ -131,7 +131,7 @@ class SandboxClassLoader private constructor( if (!isSandboxClass || parent is SandboxClassLoader) { try { - clazz = super.loadClass(name, resolve) + clazz = super.loadClass(name, false) } catch (e: ClassNotFoundException) { } catch (e: SandboxClassLoadingException) { e.messages.clearProvisional() diff --git a/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt b/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt index 4cb15b9194..232519c9d5 100644 --- a/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt +++ b/djvm/src/main/kotlin/net/corda/djvm/source/ClassSource.kt @@ -11,8 +11,8 @@ import java.nio.file.Path * @property origin The origin of the class source, if any. */ class ClassSource private constructor( - val qualifiedClassName: String = "", - val origin: String? = null + val qualifiedClassName: String, + val origin: String? ) { val internalClassName: String = qualifiedClassName.asResourcePath diff --git a/docs/source/key-concepts-djvm.rst b/docs/source/key-concepts-djvm.rst index fec8be7483..a3c5755f1f 100644 --- a/docs/source/key-concepts-djvm.rst +++ b/docs/source/key-concepts-djvm.rst @@ -59,8 +59,8 @@ Abstraction ~~~~~~~~~~~ The sandbox is abstracted away as an executor which takes as input an implementation of the interface -``SandboxedRunnable``, dereferenced by a ``ClassSource``. This interface has a single method that -needs implementing, namely ``run(Input): Output``. +``Function``, dereferenced by a ``ClassSource``. This interface has a single method that +needs implementing, namely ``apply(Input): Output``. A ``ClassSource`` object referencing such an implementation can be passed into the ``SandboxExecutor`` together with an input of type ``Input``. The executor has operations for both execution and static @@ -68,7 +68,7 @@ validation, namely ``run()`` and ``validate()``. These methods both return a sum * In the case of execution, this summary object has information about: * Whether or not the runnable was successfully executed. - * If successful, the return value of ``SandboxedRunnable.run()``. + * If successful, the return value of ``Function.apply()``. * If failed, the exception that was raised. * And in both cases, a summary of all accrued costs during execution. @@ -80,7 +80,7 @@ validation, namely ``run()`` and ``validate()``. These methods both return a sum severity ``ERROR`` will prevent execution. The sandbox has a configuration that applies to the execution of a specific runnable. This configuration, on a higher -level, contains a set of rules, definition providers, emitters and a whitelist. +level, contains a set of rules, definition providers and emitters. .. image:: resources/djvm-overview.png @@ -107,7 +107,7 @@ work may well introduce additional constraints that we would want to place on th .. note:: It is worth noting that not only smart contract code is instrumented by the sandbox, but all code that it can - transitively reach. In particular this means that the Java runtime classes (that have not been whitelisted) and any + transitively reach. In particular this means that the Java runtime classes and any other library code used in the program are also instrumented and persisted ahead of time. @@ -129,15 +129,6 @@ tries to catch such exceptions, as doing so would allow the user to bypass the t profile. -Only Allow Explicitly Whitelisted Runtime API -............................................. - -Ensures that constant pool references are mapped against a verified subset of the Java runtime libraries. Said subset -excludes functionality that contract code should not have access to, such as native code. This whitelist has been -trimmed down to the bare minimum needed, a few classes in ``java.lang``, so that also the Java runtime libraries -themselves are subjected to the same amount of scrutiny that the rest of the code is. - - Disallow Dynamic Invocation ........................... @@ -154,9 +145,6 @@ network requests, general hardware interaction, threading, *etc.* These all cons allowing such code to be called arbitrarily from the JVM would require deterministic guarantees on the native machine code level. This falls out of scope for the DJVM. -Java runtime classes that call into native code and that are needed from within the sandbox environment, can be -whitelisted explicitly. - Disallow Finalizer Methods .......................... @@ -278,9 +266,8 @@ The loaded classes are further rewritten in two ways: Disable Synchronised Methods and Blocks ....................................... -Since Java's multi-threading API has been excluded from the whitelist, synchronised methods and code blocks have little -use in sandboxed code. Consequently, we log informational messages about occurrences of this in your sandboxed code and -automatically transform them into ordinary methods and code blocks instead. +The DJVM doesn't support multi-threading and so synchronised methods and code blocks have little +use in sandboxed code. Consequently, we automatically transform them into ordinary methods and code blocks instead. Future Work @@ -290,9 +277,6 @@ Further work is planned: * To enable controlled use of reflection APIs. - * Strip out the dependency on the extensive whitelist of underlying Java - runtime classes. - * Currently, dynamic invocation is disallowed. Allow specific lambda and string concatenation meta-factories used by Java code itself. @@ -351,7 +335,7 @@ This run will produce some output similar to this: The output should be pretty self-explanatory, but just to summarise: - * It prints out the return value from the ``SandboxedRunnable.run()`` method implemented in + * It prints out the return value from the ``Function.apply()`` method implemented in ``net.corda.sandbox.Hello``. * It also prints out the aggregated costs for allocations, invocations, jumps and throws. @@ -363,5 +347,3 @@ Other commands to be aware of are: * ``djvm inspect`` which allows you to inspect what byte code modifications will be applied to a class. * ``djvm show`` which displays the transformed byte code of a class, *i.e.*, the end result and not the difference. - - * ``djvm whitelist`` which displays the content of the whitelist in use. From c64301aa71c2cc3a085405b411878c135d480b64 Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Thu, 21 Feb 2019 18:28:35 +0000 Subject: [PATCH 07/75] CORDA-2664 - Update Docker docs (#4801) * CORDA-2664 - Update Docker docs (#4800) * CORDA-2664 - Use v4.0 on release branch --- docs/source/docker-image.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/docker-image.rst b/docs/source/docker-image.rst index 19c4e8b9c4..12d2955d11 100644 --- a/docs/source/docker-image.rst +++ b/docs/source/docker-image.rst @@ -20,7 +20,7 @@ In this example, the certificates are stored at ``/home/user/cordaBase/certifica -v /path/to/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-4.0:RELEASE + corda/corda-zulu-4.0-snapshot:latest As the node runs within a container, several mount points are required: @@ -55,7 +55,7 @@ In this example, we have previously generated a network-parameters file using th -v /home/user/sharedFolder/network-parameters:/opt/corda/network-parameters \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-4.0-snapshot:latest + corda/corda-zulu-4.0-snapshot:latest There is a new mount ``/home/user/sharedFolder/node-infos:/opt/corda/additional-node-infos`` which is used to hold the ``nodeInfo`` of all the nodes within the network. As the node within the container starts up, it will place it's own nodeInfo into this directory. This will allow other nodes also using this folder to see this new node. @@ -79,7 +79,7 @@ Joining TestNet -e LOCALITY="London" -e COUNTRY="GB" \ -v /home/user/docker/config:/etc/corda \ -v /home/user/docker/certificates:/opt/corda/certificates \ - corda/corda-4.0-snapshot:latest config-generator --testnet + corda/corda-zulu-4.0-snapshot:latest config-generator --testnet ``$MY_PUBLIC_ADDRESS`` will be the public address that this node will be advertised on. ``$ONE_TIME_DOWNLOAD_KEY`` is the one-time code provided for joining TestNet. @@ -104,7 +104,7 @@ It is now possible to start the node using the generated config and certificates -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-4.0-snapshot:latest + corda/corda-zulu-4.0-snapshot:latest Joining an existing Compatibility Zone @@ -128,7 +128,7 @@ It is possible to configure the name of the Trust Root file by setting the ``TRU -e MY_EMAIL_ADDRESS="cordauser@r3.com" \ -v /home/user/docker/config:/etc/corda \ -v /home/user/docker/certificates:/opt/corda/certificates \ - corda/corda-4.0-snapshot:latest config-generator --generic + corda/corda-zulu-4.0-snapshot:latest config-generator --generic Several environment variables must also be passed to the container to allow it to register: @@ -159,5 +159,5 @@ Once the container has finished performing the initial registration, the node ca -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-4.0-snapshot:latest + corda/corda-zulu-4.0-snapshot:latest From c2d6f8783135f311762f40388fdc0527745de9bb Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Fri, 22 Feb 2019 12:11:43 +0000 Subject: [PATCH 08/75] NOTICK - Docker: point to release images instead of snapshot (#4802) --- docs/source/docker-image.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/docker-image.rst b/docs/source/docker-image.rst index 12d2955d11..ec6dcd7c1c 100644 --- a/docs/source/docker-image.rst +++ b/docs/source/docker-image.rst @@ -20,7 +20,7 @@ In this example, the certificates are stored at ``/home/user/cordaBase/certifica -v /path/to/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-zulu-4.0-snapshot:latest + corda/corda-zulu-4.0:latest As the node runs within a container, several mount points are required: @@ -55,7 +55,7 @@ In this example, we have previously generated a network-parameters file using th -v /home/user/sharedFolder/network-parameters:/opt/corda/network-parameters \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-zulu-4.0-snapshot:latest + corda/corda-zulu-4.0:latest There is a new mount ``/home/user/sharedFolder/node-infos:/opt/corda/additional-node-infos`` which is used to hold the ``nodeInfo`` of all the nodes within the network. As the node within the container starts up, it will place it's own nodeInfo into this directory. This will allow other nodes also using this folder to see this new node. @@ -79,7 +79,7 @@ Joining TestNet -e LOCALITY="London" -e COUNTRY="GB" \ -v /home/user/docker/config:/etc/corda \ -v /home/user/docker/certificates:/opt/corda/certificates \ - corda/corda-zulu-4.0-snapshot:latest config-generator --testnet + corda/corda-zulu-4.0:latest config-generator --testnet ``$MY_PUBLIC_ADDRESS`` will be the public address that this node will be advertised on. ``$ONE_TIME_DOWNLOAD_KEY`` is the one-time code provided for joining TestNet. @@ -104,7 +104,7 @@ It is now possible to start the node using the generated config and certificates -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-zulu-4.0-snapshot:latest + corda/corda-zulu-4.0:latest Joining an existing Compatibility Zone @@ -128,7 +128,7 @@ It is possible to configure the name of the Trust Root file by setting the ``TRU -e MY_EMAIL_ADDRESS="cordauser@r3.com" \ -v /home/user/docker/config:/etc/corda \ -v /home/user/docker/certificates:/opt/corda/certificates \ - corda/corda-zulu-4.0-snapshot:latest config-generator --generic + corda/corda-zulu-4.0:latest config-generator --generic Several environment variables must also be passed to the container to allow it to register: @@ -159,5 +159,5 @@ Once the container has finished performing the initial registration, the node ca -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-zulu-4.0-snapshot:latest + corda/corda-zulu-4.0:latest From efe8a25138d707986018401c2a028174cadbc77c Mon Sep 17 00:00:00 2001 From: Shams Asari Date: Fri, 22 Feb 2019 16:08:47 +0000 Subject: [PATCH 09/75] CORDA-2610 - Internal APIs made much clearer in API doc page (#4803) (cherry picked from commit 08ce12f068f12e3ed14d4baeb366c80dfbe74da5) --- docs/source/corda-api.rst | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/docs/source/corda-api.rst b/docs/source/corda-api.rst index 211afbb484..4321741dd4 100644 --- a/docs/source/corda-api.rst +++ b/docs/source/corda-api.rst @@ -42,10 +42,10 @@ The following modules form part of Corda's public API and we commit to API/ABI b * **Core (net.corda.core)**: core Corda libraries such as crypto functions, types for Corda's building blocks: states, contracts, transactions, attachments, etc. and some interfaces for nodes and protocols * **Client RPC (net.corda.client.rpc)**: client RPC * **Client Jackson (net.corda.client.jackson)**: JSON support for client applications -* **Test Utils (net.corda.testing.core)**: generic test utilities -* **Test Node Driver (net.corda.testing.node, net.corda.testing.driver)**: test utilities to run nodes programmatically -* **Http Test Utils (net.corda.testing.http)**: a small set of utilities for making HttpCalls, aimed at demos and tests. * **DSL Test Utils (net.corda.testing.dsl)**: a simple DSL for building pseudo-transactions (not the same as the wire protocol) for testing purposes. +* **Test Node Driver (net.corda.testing.node, net.corda.testing.driver)**: test utilities to run nodes programmatically +* **Test Utils (net.corda.testing.core)**: generic test utilities +* **Http Test Utils (net.corda.testing.http)**: a small set of utilities for making HttpCalls, aimed at demos and tests. * **Dummy Contracts (net.corda.testing.contracts)**: dummy state and contracts for testing purposes * **Mock Services (net.corda.testing.services)**: mock service implementations for testing purposes @@ -57,7 +57,7 @@ Non-public API (experimental) The following modules are not part of the Corda's public API and no backwards compatibility guarantees are provided. They are further categorized in 2 classes: * the incubating modules, for which we will do our best to minimise disruption to developers using them until we are able to graduate them into the public API -* the unstable modules, which are available but we do not commit to their stability or continuation in any sense +* the internal modules, which are not to be used, and will change without notice Corda incubating modules ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -68,31 +68,14 @@ Corda incubating modules * **net.corda.client.mock**: client mock utilities * **Cordformation**: Gradle integration plugins -Corda unstable modules +Corda internal modules ~~~~~~~~~~~~~~~~~~~~~~ -* **net.corda.buildSrc**: necessary gradle plugins to build Corda -* **net.corda.node**: core code of the Corda node (eg: node driver, node services, messaging, persistence) -* **net.corda.node.api**: data structures shared between the node and the client module, e.g. types sent via RPC -* **net.corda.samples.network.visualiser**: a network visualiser that uses a simulation to visualise the interaction and messages between nodes on the Corda network -* **net.corda.samples.demos.attachment**: demonstrates sending a transaction with an attachment from one to node to another, and the receiving node accessing the attachment -* **net.corda.samples.demos.bankofcorda**: simulates the role of an asset issuing authority (eg. central bank for cash) -* **net.corda.samples.demos.irs**: demonstrates an Interest Rate Swap agreement between two banks -* **net.corda.samples.demos.notary**: a simple demonstration of a node getting multiple transactions notarised by a distributed (Raft or BFT SMaRt) notary -* **net.corda.samples.demos.simmvaluation**: A demo of SIMM valuation and agreement on a distributed ledger -* **net.corda.samples.demos.trader**: demonstrates four nodes, a notary, an issuer of cash (Bank of Corda), and two parties trading with each other, exchanging cash for a commercial paper -* **net.corda.node.smoke.test.utils**: test utilities for smoke testing -* **net.corda.node.test.common**: common test functionality -* **net.corda.tools.demobench**: a GUI tool that allows to run Corda nodes locally for demonstrations -* **net.corda.tools.explorer**: a GUI front-end for Corda -* **net.corda.tools.graphs**: utilities to infer project dependencies -* **net.corda.tools.loadtest**: Corda load tests -* **net.corda.webserver**: is a servlet container for CorDapps that export HTTP endpoints. This server is an RPC client of the node -* **net.corda.sandbox-creator**: sandbox utilities -* **net.corda.quasar.hook**: agent to hook into Quasar and provide types exclusion lists +Everything else is internal and will change without notice, even deleted, and should not be used. This also includes any package that has +``.internal`` in it. So for example, ``net.corda.core.internal`` and sub-packages should not be used. -.. warning:: Code inside any package in the ``net.corda`` namespace which contains ``.internal`` or in ``net.corda.node`` for internal use only. - Future releases will reject any CorDapps that use types from these packages. +Some of the public modules may depend on internal modules, so be careful to not rely on these transitive dependencies. In particular, the +testing modules depend on the node module and so you may end having the node in your test classpath. .. warning:: The web server module will be removed in future. You should call Corda nodes through RPC from your web server of choice e.g., Spring Boot, Vertx, Undertow. From 587e57579aa5ed1a4ea3f14a6b2a5bcc346391b5 Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Sat, 23 Feb 2019 23:51:21 +0000 Subject: [PATCH 10/75] CORDA-2669 - Reintroduce pendingFlowsCount (#4806) (#4807) * CORDA-2669 - pendingFlowsCount not in public API Reintroduce `pendingFlowsCount` to public API (as deprecated). Advise to use the `gracefulShutdown` command in the shell instead. * CORDA-2669 - Add pendingFlowsCount to api-current.txt --- .ci/api-current.txt | 2 ++ .../net/corda/core/messaging/CordaRPCOps.kt | 34 ++++++++++++++++++ .../net/corda/nodeapi/internal/RpcHelpers.kt | 36 ------------------- .../corda/node/internal/CordaRPCOpsImpl.kt | 1 - .../net/corda/tools/shell/InteractiveShell.kt | 6 +--- 5 files changed, 37 insertions(+), 42 deletions(-) diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 8505053a97..87186bf097 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -3147,6 +3147,8 @@ public interface net.corda.core.messaging.CordaRPCOps extends net.corda.core.mes public abstract net.corda.core.identity.Party wellKnownPartyFromX500Name(net.corda.core.identity.CordaX500Name) ## public final class net.corda.core.messaging.CordaRPCOpsKt extends java.lang.Object + @NotNull + public static final net.corda.core.messaging.DataFeed> pendingFlowsCount(net.corda.core.messaging.CordaRPCOps) ## @CordaSerializable public final class net.corda.core.messaging.DataFeed extends java.lang.Object diff --git a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt index 1b924610d7..ecc03e5763 100644 --- a/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt +++ b/core/src/main/kotlin/net/corda/core/messaging/CordaRPCOps.kt @@ -22,6 +22,8 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.Try import rx.Observable +import rx.schedulers.Schedulers +import rx.subjects.PublishSubject import java.io.IOException import java.io.InputStream import java.security.PublicKey @@ -420,6 +422,38 @@ interface CordaRPCOps : RPCOps { fun isWaitingForShutdown(): Boolean } +/** + * Returns a [DataFeed] of the number of pending flows. The [Observable] for the updates will complete the moment all pending flows will have terminated. + */ +@Deprecated("For automated upgrades, consider using the `gracefulShutdown` command in an SSH session instead.") +fun CordaRPCOps.pendingFlowsCount(): DataFeed> { + val updates = PublishSubject.create>() + val initialPendingFlowsCount = stateMachinesFeed().let { + var completedFlowsCount = 0 + var pendingFlowsCount = it.snapshot.size + it.updates.observeOn(Schedulers.io()).subscribe({ update -> + when (update) { + is StateMachineUpdate.Added -> { + pendingFlowsCount++ + updates.onNext(completedFlowsCount to pendingFlowsCount) + } + is StateMachineUpdate.Removed -> { + completedFlowsCount++ + updates.onNext(completedFlowsCount to pendingFlowsCount) + if (completedFlowsCount == pendingFlowsCount) { + updates.onCompleted() + } + } + } + }, updates::onError) + if (pendingFlowsCount == 0) { + updates.onCompleted() + } + pendingFlowsCount + } + return DataFeed(initialPendingFlowsCount, updates) +} + inline fun CordaRPCOps.vaultQueryBy(criteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(), paging: PageSpecification = PageSpecification(), sorting: Sort = Sort(emptySet())): Vault.Page { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/RpcHelpers.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/RpcHelpers.kt index e09112af4d..34567596da 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/RpcHelpers.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/RpcHelpers.kt @@ -1,45 +1,9 @@ package net.corda.nodeapi.internal import net.corda.core.messaging.CordaRPCOps -import net.corda.core.messaging.DataFeed -import net.corda.core.messaging.StateMachineUpdate import rx.Observable -import rx.schedulers.Schedulers -import rx.subjects.PublishSubject import java.util.concurrent.TimeUnit -/** - * Returns a [DataFeed] of the number of pending flows. The [Observable] for the updates will complete the moment all pending flows will have terminated. - */ -fun CordaRPCOps.pendingFlowsCount(): DataFeed> { - - val updates = PublishSubject.create>() - val initialPendingFlowsCount = stateMachinesFeed().let { - var completedFlowsCount = 0 - var pendingFlowsCount = it.snapshot.size - it.updates.observeOn(Schedulers.io()).subscribe({ update -> - when (update) { - is StateMachineUpdate.Added -> { - pendingFlowsCount++ - updates.onNext(completedFlowsCount to pendingFlowsCount) - } - is StateMachineUpdate.Removed -> { - completedFlowsCount++ - updates.onNext(completedFlowsCount to pendingFlowsCount) - if (completedFlowsCount == pendingFlowsCount) { - updates.onCompleted() - } - } - } - }, updates::onError) - if (pendingFlowsCount == 0) { - updates.onCompleted() - } - pendingFlowsCount - } - return DataFeed(initialPendingFlowsCount, updates) -} - /** * Returns an [Observable] that will complete when the node will have cancelled the draining shutdown hook. * diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index 80909e8fc4..ec15ef7b26 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -35,7 +35,6 @@ import net.corda.node.services.rpc.context import net.corda.node.services.statemachine.StateMachineManager import net.corda.nodeapi.exceptions.NonRpcFlowException import net.corda.nodeapi.exceptions.RejectedCommandException -import net.corda.nodeapi.internal.pendingFlowsCount import rx.Observable import rx.Subscription import java.io.InputStream diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt index 94868f2a68..bff6a27ece 100644 --- a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt +++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt @@ -19,11 +19,7 @@ import net.corda.core.flows.FlowLogic import net.corda.core.internal.* import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.openFuture -import net.corda.core.messaging.CordaRPCOps -import net.corda.core.messaging.DataFeed -import net.corda.core.messaging.FlowProgressHandle -import net.corda.core.messaging.StateMachineUpdate -import net.corda.nodeapi.internal.pendingFlowsCount +import net.corda.core.messaging.* import net.corda.tools.shell.utlities.ANSIProgressRenderer import net.corda.tools.shell.utlities.StdoutANSIProgressRenderer import org.crsh.command.InvocationContext From 365cde589a8841e82a529042be2c50113cae172b Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Mon, 25 Feb 2019 16:50:35 +0100 Subject: [PATCH 11/75] Docs: sync docs between master/c4 branches. --- docs/source/api-states.rst | 2 +- docs/source/app-upgrade-notes.rst | 4 +- docs/source/changelog.rst | 1569 +---------------------- docs/source/cipher-suites.rst | 6 +- docs/source/conf.py | 38 +- docs/source/cordapp-build-systems.rst | 45 +- docs/source/cordapp-overview.rst | 33 +- docs/source/docker-image.rst | 12 +- docs/source/docker.rst | 7 - docs/source/event-scheduling.rst | 2 +- docs/source/flow-state-machines.rst | 12 +- docs/source/getting-set-up.rst | 17 +- docs/source/index.rst | 3 +- docs/source/key-concepts-node.rst | 10 +- docs/source/node-administration.rst | 4 +- docs/source/oracles.rst | 20 +- docs/source/quickstart-index.rst | 130 +- docs/source/release-notes.rst | 36 +- docs/source/tutorial-custom-notary.rst | 4 +- docs/source/tutorial-observer-nodes.rst | 2 +- docs/source/tutorial-tear-offs.rst | 2 +- 21 files changed, 228 insertions(+), 1730 deletions(-) delete mode 100644 docs/source/docker.rst diff --git a/docs/source/api-states.rst b/docs/source/api-states.rst index c3cdb75416..5f62608e04 100644 --- a/docs/source/api-states.rst +++ b/docs/source/api-states.rst @@ -155,7 +155,7 @@ and methods. For example, here is the relatively complex definition for a state .. container:: codeset - .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt + .. literalinclude:: ../../finance/contracts/src/main/kotlin/net/corda/finance/contracts/asset/Cash.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index 879f566e6a..15bee7a3c1 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -401,8 +401,8 @@ Step 12. Possibly update vault state queries In Corda 4 queries made on a node's vault can filter by the relevancy of those states to the node. As this functionality does not exist in Corda 3, apps will continue to receive all states in any vault queries. However, it may make sense to migrate queries expecting just those states relevant -to the node in question to query for only relevant states. See :doc:`api-vault-query.rst` for more details on how to do this. Not doing this -may result in queries returning more states than expected if the node is using observer functionality (see ":doc:`tutorial-observer-nodes.rst`"). +to the node in question to query for only relevant states. See :doc:`api-vault-query` for more details on how to do this. Not doing this +may result in queries returning more states than expected if the node is using observer functionality (see ":doc:`tutorial-observer-nodes`"). Step 13. Explore other new features that may be useful ------------------------------------------------------ diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 81c6ed829b..6c3f946af0 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -4,32 +4,6 @@ Changelog Here's a summary of what's changed in each Corda release. For guidance on how to upgrade code from the previous release, see :doc:`app-upgrade-notes`. -Unreleased ----------- - -* Updating postgres dependency to 42.2.5 - -* Test ``CordaService`` s can be installed on mock nodes using ``UnstartedMockNode.installCordaService``. - -* The finance-contracts demo CorDapp has been slimmed down to contain only that which is relevant for contract verification. Everything else - has been moved to the finance-workflows CorDapp: - - * The cash selection logic. ``AbstractCashSelection`` is now in net.corda.finance.contracts.asset so any custom implementations must now be - defined in ``META-INF/services/net.corda.finance.workflows.asset.selection.AbstractCashSelection``. - - * The jackson annotations on ``Expression`` have been removed. You will need to use ``FinanceJSONSupport.registerFinanceJSONMappers`` if - you wish to preserve the JSON format for this class. - - * The various utility methods defined in ``Cash`` for creating cash transactions have been moved to ``net.corda.finance.workflows.asset.CashUtils``. - Similarly with ``CommercialPaperUtils`` and ``ObligationUtils``. - - * Various other utilities such as ``GetBalances` and the test calendar data. - - The only exception to this is ``Interpolator`` and related classes. These are now in the `IRS demo workflows CorDapp `_. - -* Vault states are now correctly migrated when moving from V3 to V4. In particular, this means the relevancy column is correctly filled, and the state party table is populated. - Note: This means Corda can be slow to start up for the first time after upgrading from V3 to V4. - .. _changelog_v4.0: Version 4.0 @@ -43,10 +17,10 @@ Version 4.0 retries have been disabled for single node notaries since in this case they offer no potential benefits, unlike for a notary cluster with several members who might have different availability. -* New configuration property `database.initialiseAppSchema` with values `UPDATE`, `VALIDATE` and `NONE`. - The property controls the behavior of the Hibernate DDL generation. `UPDATE` performs an update of CorDapp schemas, while - `VALID` only verifies their integrity. The property does not affect the node-specific DDL handling and - complements `database.initialiseSchema` to disable DDL handling altogether. +* New configuration property ``database.initialiseAppSchema`` with values ``UPDATE``, ``VALIDATE`` and ``NONE``. + The property controls the behavior of the Hibernate DDL generation. ``UPDATE`` performs an update of CorDapp schemas, while + ``VALIDATE`` only verifies their integrity. The property does not affect the node-specific DDL handling and + complements ``database.initialiseSchema`` to disable DDL handling altogether. * ``JacksonSupport.createInMemoryMapper`` was incorrectly marked as deprecated and is no longer so. @@ -72,11 +46,11 @@ Version 4.0 * Fixed a problem with IRS demo not being able to simulate future dates as expected (https://github.com/corda/corda/issues/3851). -* Fixed a problem that was preventing `Cash.generateSpend` to be used more than once per transaction (https://github.com/corda/corda/issues/4110). +* Fixed a problem that was preventing ``Cash.generateSpend`` to be used more than once per transaction (https://github.com/corda/corda/issues/4110). * Fixed a bug resulting in poor vault query performance and incorrect results when sorting. -* Improved exception thrown by `AttachmentsClassLoader` when an attachment cannot be used because its uploader is not trusted. +* Improved exception thrown by ``AttachmentsClassLoader`` when an attachment cannot be used because its uploader is not trusted. * Fixed deadlocks generated by starting flow from within CordaServices. @@ -98,7 +72,7 @@ Version 4.0 * BFT-Smart and Raft notary implementations have been moved to the ``net.corda.notary.experimental`` package to emphasise their experimental nature. Note that it is not possible to preserve the state for both types of notaries when upgrading from V3 or an earlier Corda version. -* New "validate-configuration" sub-command to `corda.jar`, allowing to validate the actual node configuration without starting the node. +* New "validate-configuration" sub-command to ``corda.jar``, allowing to validate the actual node configuration without starting the node. * CorDapps now have the ability to specify a minimum platform version in their MANIFEST.MF to prevent old nodes from loading them. @@ -292,7 +266,7 @@ Version 4.0 In case your Cordapps use this entity class to persist data in own custom tables as non Primary Key columns refer to :doc:`app-upgrade-notes` for upgrade instructions. -* Adding a public method to check if a public key satisfies Corda recommended algorithm specs, `Crypto.validatePublicKey(java.security.PublicKey)`. +* Adding a public method to check if a public key satisfies Corda recommended algorithm specs, ``Crypto.validatePublicKey(java.security.PublicKey)``. For instance, this method will check if an ECC key lies on a valid curve or if an RSA key is >= 2048bits. This might be required for extra key validation checks, e.g., for Doorman to check that a CSR key meets the minimum security requirements. @@ -316,14 +290,14 @@ Version 4.0 required to filter by object type. Thus, when one wants to filter-in all "reference input states" in a ``FilteredTransaction`` then he/she should check if it is of type ``ReferenceStateRef``. -* Removed type parameter `U` from `tryLockFungibleStatesForSpending` to allow the function to be used with `FungibleState` - as well as `FungibleAsset`. This _might_ cause a compile failure in some obscure cases due to the removal of the type +* Removed type parameter ``U`` from ``tryLockFungibleStatesForSpending`` to allow the function to be used with ``FungibleState`` + as well as ``FungibleAsset``. This _might_ cause a compile failure in some obscure cases due to the removal of the type parameter from the method. If your CorDapp does specify types explicitly when using this method then updating the types will allow your app to compile successfully. However, those using type inference (e.g. using Kotlin) should not experience any changes. Old CorDapp JARs will still work regardless. -* `issuer_ref` column in `FungibleStateSchema` was updated to be nullable to support the introduction of the - `FungibleState` interface. The `vault_fungible_states` table can hold both `FungibleAssets` and `FungibleStates`. +* ``issuer_ref`` column in ``FungibleStateSchema`` was updated to be nullable to support the introduction of the + ``FungibleState`` interface. The ``vault_fungible_states`` table can hold both ``FungibleAssets`` and ``FungibleStates``. * CorDapps built by ``corda-gradle-plugins`` are now signed and sealed JAR files. Signing can be configured or disabled, and it defaults to using the Corda development certificate. @@ -331,7 +305,9 @@ Version 4.0 * Finance CorDapps are now built as sealed and signed JAR files. Custom classes can no longer be placed in the packages defined in either finance Cordapp or access it's non-public members. -* Finance CorDapp was split into two separate apps: ``corda-finance-contracts`` and ``corda-finance-workflows``. There is no longer a single cordapp which provides both. +* Finance CorDapp was split into two separate apps: ``corda-finance-contracts`` and ``corda-finance-workflows``. There is + no longer a single cordapp which provides both. You need to have both JARs installed in the node simultaneously for the + app to work however. * All sample CorDapps were split into separate apps: workflows and contracts to reflect new convention. It is recommended to structure your CorDapps this way, see :doc:`app-upgrade-notes` on upgrading your CorDapp. @@ -345,1512 +321,25 @@ Version 4.0 * Vault Query Criteria have been enhanced to allow filtering by state relevancy. Queries can request all states, just relevant ones, or just non relevant ones. The default is to return all states, to maintain backwards compatibility. Note that this means apps running on nodes using Observer node functionality should update their queries to request only relevant states if they are only expecting to see states in which they participate. -Version 3.3 ------------ +* Postgres dependency was updated to version 42.2.5 -* Vault query fix: support query by parent classes of Contract State classes (see https://github.com/corda/corda/issues/3714) +* Test ``CordaService`` s can be installed on mock nodes using ``UnstartedMockNode.installCordaService``. -* Fixed an issue preventing Shell from returning control to the user when CTRL+C is pressed in the terminal. +* The finance-contracts demo CorDapp has been slimmed down to contain only that which is relevant for contract verification. Everything else + has been moved to the finance-workflows CorDapp: -* Fixed a problem that sometimes prevented nodes from starting in presence of custom state types in the database without a corresponding type from installed CorDapps. + * The cash selection logic. ``AbstractCashSelection`` is now in net.corda.finance.contracts.asset so any custom implementations must now be + defined in ``META-INF/services/net.corda.finance.workflows.asset.selection.AbstractCashSelection``. -* Introduced a grace period before the initial node registration fails if the node cannot connect to the Doorman. - It retries 10 times with a 1 minute interval in between each try. At the moment this is not configurable. + * The jackson annotations on ``Expression`` have been removed. You will need to use ``FinanceJSONSupport.registerFinanceJSONMappers`` if + you wish to preserve the JSON format for this class. -* Fixed an error thrown by NodeVaultService upon recording a transaction with a number of inputs greater than the default page size. + * The various utility methods defined in ``Cash`` for creating cash transactions have been moved to ``net.corda.finance.workflows.asset.CashUtils``. + Similarly with ``CommercialPaperUtils`` and ``ObligationUtils``. -* Changes to the JSON/YAML serialisation format from ``JacksonSupport``, which also applies to the node shell: + * Various other utilities such as ``GetBalances`` and the test calendar data. - * ``Instant`` and ``Date`` objects are serialised as ISO-8601 formatted strings rather than timestamps - * ``PublicKey`` objects are serialised and looked up according to their Base58 encoded string - * ``Party`` objects can be deserialised by looking up their public key, in addition to their name - * ``NodeInfo`` objects are serialised as an object and can be looked up using the same mechanism as ``Party`` - * ``NetworkHostAndPort`` serialised according to its ``toString()`` - * ``PartyAndCertificate`` is serialised as the name - * ``SerializedBytes`` is serialised by materialising the bytes into the object it represents, and then serialising that - object into YAML/JSON - * ``X509Certificate`` is serialised as an object with key fields such as ``issuer``, ``publicKey``, ``serialNumber``, etc. - The encoded bytes are also serialised into the ``encoded`` field. This can be used to deserialise an ``X509Certificate`` - back. - * ``CertPath`` objects are serialised as a list of ``X509Certificate`` objects. + The only exception to this is ``Interpolator`` and related classes. These are now in the `IRS demo workflows CorDapp `_. -* ``fullParties`` boolean parameter added to ``JacksonSupport.createDefaultMapper`` and ``createNonRpcMapper``. If ``true`` - then ``Party`` objects are serialised as JSON objects with the ``name`` and ``owningKey`` fields. For ``PartyAndCertificate`` - the ``certPath`` is serialised. - -* Several members of ``JacksonSupport`` have been deprecated to highlight that they are internal and not to be used - -* ``ServiceHub`` and ``CordaRPCOps`` can now safely be used from multiple threads without incurring in database transaction problems. - -* Fixed an issue preventing out of process nodes started by the ``Driver`` from logging to file. - -* The Vault Criteria API has been extended to take a more precise specification of which class contains a field. This primarily impacts Java users; Kotlin users need take no action. The old methods have been deprecated but still work - the new methods avoid bugs that can occur when JPA schemas inherit from each other. - -* Removed -xmx VM argument from Explorer's Capsule setup. This helps avoiding out of memory errors. - -* Node will now gracefully fail to start if one of the required ports is already in use. - -* Fixed incorrect exception handling in ``NodeVaultService._query()``. - -* Avoided a memory leak deriving from incorrect MappedSchema caching strategy. - -* Fix CORDA-1403 where a property of a class that implemented a generic interface could not be deserialised in - a factory without a serialiser as the subtype check for the class instance failed. Fix is to compare the raw - type. - -* Fix CORDA-1229. Setter-based serialization was broken with generic types when the property was stored - as the raw type, List for example. - -.. _changelog_v3.2: - -Version 3.2 ------------ - -* Doorman and NetworkMap URLs can now be configured individually rather than being assumed to be - the same server. Current ``compatibilityZoneURL`` configurations remain valid. See both :doc:`corda-configuration-file` - and :doc:`permissioning` for details. - -* Table name with a typo changed from ``NODE_ATTCHMENTS_CONTRACTS`` to ``NODE_ATTACHMENTS_CONTRACTS``. - -.. _changelog_v3.1: - -Version 3.1 ------------ - -* Update the fast-classpath-scanner dependent library version from 2.0.21 to 2.12.3 - - .. note:: Whilst this is not the latest version of this library, that being 2.18.1 at time of writing, versions - later than 2.12.3 (including 2.12.4) exhibit a different issue. - -* Updated the api scanner gradle plugin to work the same way as the version in master. These changes make the api scanner more - accurate and fix a couple of bugs, and change the format of the api-current.txt file slightly. Backporting these changes - to the v3 branch will make it easier for us to ensure that apis are stable for future versions. These changes are - released in gradle plugins version 3.0.10. For more information on the api scanner see - the `documentation `_. - -* Fixed security vulnerability when using the ``HashAttachmentConstraint``. Added strict check that the contract JARs - referenced in a transaction were deployed on the node. - -* Fixed node's behaviour on startup when there is no connectivity to network map. Node continues to work normally if it has - all the needed network data, waiting in the background for network map to become available. - -.. _changelog_v3: - -Version 3.0 ------------ - -* Due to a security risk, the `conflict` property has been removed from `NotaryError.Conflict` error object. It has been replaced - with `consumedStates` instead. The new property no longer specifies the original requesting party and transaction id for - a consumed state. Instead, only the hash of the transaction id is revealed. For more details why this change had to be - made please refer to the release notes. - -* Added ``NetworkMapCache.getNodesByLegalName`` for querying nodes belonging to a distributed service such as a notary cluster - where they all share a common identity. ``NetworkMapCache.getNodeByLegalName`` has been tightened to throw if more than - one node with the legal name is found. - -* Introduced Flow Draining mode, in which a node continues executing existing flows, but does not start new. This is to support graceful node shutdown/restarts. - In particular, when this mode is on, new flows through RPC will be rejected, scheduled flows will be ignored, and initial session messages will not be consumed. - This will ensure that the number of checkpoints will strictly diminish with time, allowing for a clean shutdown. - -* Removed blacklisted word checks in Corda X.500 name to allow "Server" or "Node" to be use as part of the legal name. - -* Separated our pre-existing Artemis broker into an RPC broker and a P2P broker. - -* Refactored ``NodeConfiguration`` to expose ``NodeRpcOptions`` (using top-level "rpcAddress" property still works with warning). - -* Modified ``CordaRPCClient`` constructor to take a ``SSLConfiguration?`` additional parameter, defaulted to ``null``. - -* Introduced ``CertificateChainCheckPolicy.UsernameMustMatchCommonName`` sub-type, allowing customers to optionally enforce - username == CN condition on RPC SSL certificates. - -* Modified ``DriverDSL`` and sub-types to allow specifying RPC settings for the Node. - -* Modified the ``DriverDSL`` to start Cordformation nodes allowing automatic generation of "rpcSettings.adminAddress" in case - "rcpSettings.useSsl" is ``false`` (the default). - -* Introduced ``UnsafeCertificatesFactory`` allowing programmatic generation of X509 certificates for test purposes. - -* JPA Mapping annotations for States extending ``CommonSchemaV1.LinearState`` and ``CommonSchemaV1.FungibleState`` on the - `participants` collection need to be moved to the actual class. This allows to properly specify the unique table name per - a collection. See: DummyDealStateSchemaV1.PersistentDummyDealState -* Database schema changes - an H2 database instance of Corda 1.0 and 2.0 cannot be reused for Corda 3.0, listed changes for Vault and Finance module: - - * ``NODE_TRANSACTIONS``: - column ``"TRANSACTION”`` renamed to ``TRANSACTION_VALUE``, serialization format of BLOB stored in the column has changed to AMQP - * ``VAULT_STATES``: - column ``CONTRACT_STATE`` removed - * ``VAULT_FUNGIBLE_STATES``: - column ``ISSUER_REFERENCE`` renamed to ``ISSUER_REF`` and the field size increased - * ``"VAULTSCHEMAV1$VAULTFUNGIBLESTATES_PARTICIPANTS"``: - table renamed to ``VAULT_FUNGIBLE_STATES_PARTS``, - column ``"VAULTSCHEMAV1$VAULTFUNGIBLESTATES_OUTPUT_INDEX"`` renamed to ``OUTPUT_INDEX``, - column ``"VAULTSCHEMAV1$VAULTFUNGIBLESTATES_TRANSACTION_ID"`` renamed to ``TRANSACTION_ID`` - * ``VAULT_LINEAR_STATES``: - type of column ``"UUID"`` changed from ``VARBINARY`` to ``VARCHAR(255)`` - select varbinary column as ``CAST("UUID" AS UUID)`` to get UUID in varchar format - * ``"VAULTSCHEMAV1$VAULTLINEARSTATES_PARTICIPANTS"``: - table renamed to ``VAULT_LINEAR_STATES_PARTS``, - column ``"VAULTSCHEMAV1$VAULTLINEARSTATES_OUTPUT_INDEX"`` renamed to ``OUTPUT_INDEX``, - column ``"VAULTSCHEMAV1$VAULTLINEARSTATES_TRANSACTION_ID"`` renamed to ``TRANSACTION_ID`` - * ``contract_cash_states``: - columns storing Base58 representation of the serialised public key (e.g. ``issuer_key``) were changed to store Base58 representation of SHA-256 of public key prefixed with `DL` - * ``contract_cp_states``: - table renamed to ``cp_states``, column changes as for ``contract_cash_states`` - -* X.509 certificates now have an extension that specifies the Corda role the certificate is used for, and the role - hierarchy is now enforced in the validation code. See ``net.corda.core.internal.CertRole`` for the current implementation - until final documentation is prepared. Certificates at ``NODE_CA``, ``WELL_KNOWN_SERVICE_IDENTITY`` and above must - only ever by issued by network services and therefore issuance constraints are not relevant to end users. - The ``TLS``, ``WELL_KNOWN_LEGAL_IDENTITY`` roles must be issued by the ``NODE_CA`` certificate issued by the - Doorman, and ``CONFIDENTIAL_IDENTITY`` certificates must be issued from a ``WELL_KNOWN_LEGAL_IDENTITY`` certificate. - For a detailed specification of the extension please see :doc:`permissioning`. - -* The network map service concept has been re-designed. More information can be found in :doc:`network-map`. - - * The previous design was never intended to be final but was rather a quick implementation in the earliest days of the - Corda project to unblock higher priority items. It suffers from numerous disadvantages including lack of scalability, - as one node is expected to hold open and manage connections to every node on the network; not reliable; hard to defend - against DoS attacks; etc. - - * There is no longer a special network map node for distributing the network map to the other nodes. Instead the network - map is now a collection of signed ``NodeInfo`` files distributed via HTTP. - - * The ``certificateSigningService`` config has been replaced by ``compatibilityZoneURL`` which is the base URL for the - doorman registration and for downloading the network map. There is also an end-point for the node to publish its node-info - object, which the node does each time it changes. ``networkMapService`` config has been removed. - - * To support local and test deployments, the node polls the ``additional-node-infos`` directory for these signed ``NodeInfo`` - objects which are stored in its local cache. On startup the node generates its own signed file with the filename format - "nodeInfo-\*". This can be copied to every node's ``additional-node-infos`` directory that is part of the network. - - * Cordform (which is the ``deployNodes`` gradle task) does this copying automatically for the demos. The ``NetworkMap`` - parameter is no longer needed. - - * For test deployments we've introduced a bootstrapping tool (see :doc:`network-bootstrapper`). - - * ``extraAdvertisedServiceIds``, ``notaryNodeAddress``, ``notaryClusterAddresses`` and ``bftSMaRt`` configs have been - removed. The configuration of notaries has been simplified into a single ``notary`` config object. See - :doc:`corda-configuration-file` for more details. - - * Introducing the concept of network parameters which are a set of constants which all nodes on a network must agree on - to correctly interoperate. These can be retrieved from ``ServiceHub.networkParameters``. - - * One of these parameters, ``maxTransactionSize``, limits the size of a transaction, including its attachments, so that - all nodes have sufficient memory to validate transactions. - - * The set of valid notaries has been moved to the network parameters. Notaries are no longer identified by the CN in - their X500 name. - - * Single node notaries no longer have a second separate notary identity. Their main identity *is* their notary identity. - Use ``NetworkMapCache.notaryIdentities`` to get the list of available notaries. - - * Added ``NetworkMapCache.getNodesByLegalName`` for querying nodes belonging to a distributed service such as a notary cluster - where they all share a common identity. ``NetworkMapCache.getNodeByLegalName`` has been tightened to throw if more than - one node with the legal name is found. - - * The common name in the node's X500 legal name is no longer reserved and can be used as part of the node's name. - - * Moved ``NodeInfoSchema`` to internal package as the node info's database schema is not part of the public API. This - was needed to allow changes to the schema. - -* Support for external user credentials data source and password encryption [CORDA-827]. - -* Exporting additional JMX metrics (artemis, hibernate statistics) and loading Jolokia agent at JVM startup when using - DriverDSL and/or cordformation node runner. - -* Removed confusing property database.initDatabase, enabling its guarded behaviour with the dev-mode. - In devMode Hibernate will try to create or update database schemas, otherwise it will expect relevant schemas to be present - in the database (pre configured via DDL scripts or equivalent), and validate these are correct. - -* ``AttachmentStorage`` now allows providing metadata on attachments upload - username and filename, currently as plain - strings. Those can be then used for querying, utilizing ``queryAttachments`` method of the same interface. - -* ``SSH Server`` - The node can now expose shell via SSH server with proper authorization and permissioning built in. - -* ``CordaRPCOps`` implementation now checks permissions for any function invocation, rather than just when starting flows. - -* ``wellKnownPartyFromAnonymous()`` now always resolve the key to a ``Party``, then the party to the well known party. - Previously if it was passed a ``Party`` it would use its name as-is without verifying the key matched that name. - -* ``OpaqueBytes.bytes`` now returns a clone of its underlying ``ByteArray``, and has been redeclared as ``final``. - This is a minor change to the public API, but is required to ensure that classes like ``SecureHash`` are immutable. - -* Experimental support for PostgreSQL: CashSelection done using window functions - -* ``FlowLogic`` now exposes a series of function called ``receiveAll(...)`` allowing to join ``receive(...)`` instructions. - -* Renamed "plugins" directory on nodes to "cordapps" - -* The ``Cordformation`` gradle plugin has been split into ``cordformation`` and ``cordapp``. The former builds and - deploys nodes for development and testing, the latter turns a project into a cordapp project that generates JARs in - the standard CorDapp format. - -* ``Cordapp`` now has a name field for identifying CorDapps and all CorDapp names are printed to console at startup. - -* Enums now respect the whitelist applied to the Serializer factory serializing / deserializing them. If the enum isn't - either annotated with the @CordaSerializable annotation or explicitly whitelisted then a NotSerializableException is - thrown. - -* Gradle task ``deployNodes`` can have an additional parameter ``configFile`` with the path to a properties file - to be appended to node.conf. - -* Cordformation node building DSL can have an additional parameter ``configFile`` with the path to a properties file - to be appended to node.conf. - -* ``FlowLogic`` now has a static method called ``sleep`` which can be used in certain circumstances to help with resolving - contention over states in flows. This should be used in place of any other sleep primitive since these are not compatible - with flows and their use will be prevented at some point in the future. Pay attention to the warnings and limitations - described in the documentation for this method. This helps resolve a bug in ``Cash`` coin selection. - A new static property ``currentTopLevel`` returns the top most ``FlowLogic`` instance, or null if not in a flow. - -* ``CordaService`` annotated classes should be upgraded to take a constructor parameter of type ``AppServiceHub`` which - allows services to start flows marked with the ``StartableByService`` annotation. For backwards compatability - service classes with only ``ServiceHub`` constructors will still work. - -* ``TimeWindow`` now has a ``length`` property that returns the length of the time-window as a ``java.time.Duration`` object, - or ``null`` if the time-window isn't closed. - -* A new ``SIGNERS_GROUP`` with ordinal 6 has been added to ``ComponentGroupEnum`` that corresponds to the ``Command`` - signers. - -* ``PartialMerkleTree`` is equipped with a ``leafIndex`` function that returns the index of a hash (leaf) in the - partial Merkle tree structure. - -* A new function ``checkCommandVisibility(publicKey: PublicKey)`` has been added to ``FilteredTransaction`` to check - if every command that a signer should receive (e.g. an Oracle) is indeed visible. - -* Changed the AMQP serializer to use the officially assigned R3 identifier rather than a placeholder. - -* The ``ReceiveTransactionFlow`` can now be told to record the transaction at the same time as receiving it. Using this - feature, better support for observer/regulator nodes has been added. See :doc:`tutorial-observer-nodes`. - -* Added an overload of ``TransactionWithSignatures.verifySignaturesExcept`` which takes in a collection of ``PublicKey`` s. - -* ``DriverDSLExposedInterface`` has been renamed to ``DriverDSL`` and the ``waitForAllNodesToFinish()`` method has instead - become a parameter on driver creation. - -* Values for the ``database.transactionIsolationLevel`` config now follow the ``java.sql.Connection`` int constants but - without the "TRANSACTION" prefix, i.e. "NONE", "READ_UNCOMMITTED", etc. - -* Peer-to-peer communications is now via AMQP 1.0 as default. - Although the legacy Artemis CORE bridging can still be used by setting the ``useAMQPBridges`` configuration property to false. - -* The Artemis topics used for peer-to-peer communication have been changed to be more consistent with future cryptographic - agility and to open up the future possibility of sharing brokers between nodes. This is a breaking wire level change - as it means that nodes after this change will not be able to communicate correctly with nodes running the previous version. - Also, any pending enqueued messages in the Artemis message store will not be delivered correctly to their original target. - However, assuming a clean reset of the artemis data and that the nodes are consistent versions, - data persisted via the AMQP serializer will be forward compatible. - -* The ability for CordaServices to register callbacks so they can be notified of shutdown and clean up resource such as - open ports. - -* Move to a message based control of peer to peer bridge formation to allow for future out of process bridging components. - This removes the legacy Artemis bridges completely, so the ``useAMQPBridges`` configuration property has been removed. - -* A ``CordaInternal`` attribute has been added to identify properties that are not intended to form part of the - public api and as such are not intended for public use. This is alongside the existing ``DoNotImplement`` attribute for classes which - provide Corda functionality to user applications, but should not be implemented by consumers, and any classes which - are defined in ``.internal`` packages, which are also not for public use. - -* Marked ``stateMachine`` on ``FlowLogic`` as ``CordaInternal`` to make clear that is it not part of the public api and is - only for internal use - -* Provided experimental support for specifying your own webserver to be used instead of the default development - webserver in ``Cordform`` using the ``webserverJar`` argument - -* Created new ``StartedMockNode`` and ``UnstartedMockNode`` classes which are wrappers around our MockNode implementation - that expose relevant methods for testing without exposing internals, create these using a ``MockNetwork``. - -* The test utils in ``Expect.kt``, ``SerializationTestHelpers.kt``, ``TestConstants.kt`` and ``TestUtils.kt`` have moved - from the ``net.corda.testing`` package to the ``net.corda.testing.core`` package, and ``FlowStackSnapshot.kt`` has moved to the - ``net.corda.testing.services`` package. Moving existing classes out of the ``net.corda.testing.*`` package - will help make it clearer which parts of the api are stable. Scripts have been provided to smooth the upgrade - process for existing projects in the ``tools\scripts`` directory of the Corda repo. - -* ``TransactionSignature`` includes a new ``partialMerkleTree`` property, required for future support of signing over - multiple transactions at once. - -* Updating Jolokia dependency to latest version (includes security fixes) - -.. _changelog_v1: - -Release 1.0 ------------ - -* Unification of VaultQuery And VaultService APIs - Developers now only need to work with a single Vault Service API for all needs. - -* Java 8 lambdas now work property with Kryo during check-pointing. - -* Java 8 serializable lambdas now work property with Kryo during check-pointing. - -* String constants have been marked as ``const`` type in Kotlin, eliminating cases where functions of the form - ``get()`` were created for the Java API. These can now be referenced by their name directly. - -* ``FlowLogic`` communication has been extensively rewritten to use functions on ``FlowSession`` as the base for communication - between nodes. - - * Calls to ``send()``, ``receive()`` and ``sendAndReceive()`` on FlowLogic should be replaced with calls - to the function of the same name on ``FlowSession``. Note that the replacement functions do not take in a destination - parameter, as this is defined in the session. - * Initiated flows now take in a ``FlowSession`` instead of ``Party`` in their constructor. If you need to access the - counterparty identity, it is in the ``counterparty`` property of the flow session. - - -* Added X509EdDSAEngine to intercept and rewrite EdDSA public keys wrapped in X509Key instances. This corrects an issue - with verifying certificate paths loaded from a Java Keystore where they contain EdDSA keys. - -* Confidential identities are now complete: - - * The identity negotiation flow is now called ``SwapIdentitiesFlow``, renamed from ``TransactionKeyFlow``. - * generateSpend() now creates a new confidential identity for the change address rather than using the identity of the - input state owner. - * Please see the documentation :doc:`key-concepts-identity` and :doc:`api-identity` for more details. - -* Remove the legacy web front end from the SIMM demo. - -* ``NodeInfo`` and ``NetworkMapCache`` changes: - - * Removed ``NodeInfo::legalIdentity`` in preparation for handling of multiple identities. We left list of ``NodeInfo::legalIdentitiesAndCerts``, - the first identity still plays a special role of main node identity. - * We no longer support advertising services in network map. Removed ``NodeInfo::advertisedServices``, ``serviceIdentities`` - and ``notaryIdentity``. - * Removed service methods from ``NetworkMapCache``: ``partyNodes``, ``networkMapNodes``, ``notaryNodes``, ``regulatorNodes``, - ``getNodesWithService``, ``getPeersWithService``, ``getRecommended``, ``getNodesByAdvertisedServiceIdentityKey``, ``getAnyNotary``, - ``notaryNode``, ``getAnyServiceOfType``. To get all known ``NodeInfo``'s call ``allNodes``. - * In preparation for ``NetworkMapService`` redesign and distributing notaries through ``NetworkParameters`` we added - ``NetworkMapCache::notaryIdentities`` list to enable to lookup for notary parties known to the network. Related ``CordaRPCOps::notaryIdentities`` - was introduced. Other special nodes parties like Oracles or Regulators need to be specified directly in CorDapp or flow. - * Moved ``ServiceType`` and ``ServiceInfo`` to ``net.corda.nodeapi`` package as services are only required on node startup. - -* Adding enum support to the class carpenter - -* ``ContractState::contract`` has been moved ``TransactionState::contract`` and it's type has changed to ``String`` in order to - support dynamic classloading of contract and contract constraints. - -* CorDapps that contain contracts are now automatically loaded into the attachment storage - for CorDapp developers this - now means that contracts should be stored in separate JARs to flows, services and utilities to avoid large JARs being - auto imported to the attachment store. - -* About half of the code in test-utils has been moved to a new module ``node-driver``, - and the test scope modules are now located in a ``testing`` directory. - -* ``CordaPluginRegistry`` has been renamed to ``SerializationWhitelist`` and moved to the ``net.corda.core.serialization`` - package. The API for whitelisting types that can't be annotated was slightly simplified. This class used to contain - many things, but as we switched to annotations and classpath scanning over time it hollowed out until this was - the only functionality left. You also need to rename your services resource file to the new class name. - An associated property on ``MockNode`` was renamed from ``testPluginRegistries`` to ``testSerializationWhitelists``. - -* Contract Upgrades: deprecated RPC authorization / deauthorization API calls in favour of equivalent flows in ContractUpgradeFlow. - Implemented contract upgrade persistence using JDBC backed persistent map. - -* Vault query common attributes (state status and contract state types) are now handled correctly when using composite - criteria specifications. State status is overridable. Contract states types are aggregatable. - -* Cash selection algorithm is now pluggable (with H2 being the default implementation) - -* Removed usage of Requery ORM library (replaced with JPA/Hibernate) - -* Vault Query performance improvement (replaced expensive per query SQL statement to obtain concrete state types - with single query on start-up followed by dynamic updates using vault state observable)) - -* Vault Query fix: filter by multiple issuer names in ``FungibleAssetQueryCriteria`` - -* Following deprecated methods have been removed: - - * In ``DataFeed`` - - * ``first`` and ``current``, replaced by ``snapshot`` - * ``second`` and ``future``, replaced by ``updates`` - - * In ``CordaRPCOps`` - - * ``stateMachinesAndUpdates``, replaced by ``stateMachinesFeed`` - * ``verifiedTransactions``, replaced by ``verifiedTransactionsFeed`` - * ``stateMachineRecordedTransactionMapping``, replaced by ``stateMachineRecordedTransactionMappingFeed`` - * ``networkMapUpdates``, replaced by ``networkMapFeed`` - -* Due to security concerns and the need to remove the concept of state relevancy (which isn't needed in Corda), - ``ResolveTransactionsFlow`` has been made internal. Instead merge the receipt of the ``SignedTransaction`` and the subsequent - sub-flow call to ``ResolveTransactionsFlow`` with a single call to ``ReceiveTransactionFlow``. The flow running on the counterparty - must use ``SendTransactionFlow`` at the correct place. There is also ``ReceiveStateAndRefFlow`` and ``SendStateAndRefFlow`` for - dealing with ``StateAndRef``'s. - -* Vault query soft locking enhancements and deprecations - - * removed original ``VaultService`` ``softLockedStates`` query mechanism. - * introduced improved ``SoftLockingCondition`` filterable attribute in ``VaultQueryCriteria`` to enable specification of - different soft locking retrieval behaviours (exclusive of soft locked states, soft locked states only, specified by set - of lock ids) - -* Trader demo now issues cash and commercial paper directly from the bank node, rather than the seller node self-issuing - commercial paper but labelling it as if issued by the bank. - -* Merged handling of well known and confidential identities in the identity service. Registration now takes in an identity - (either type) plus supporting certificate path, and de-anonymisation simply returns the issuing identity where known. - If you specifically need well known identities, use the network map, which is the authoritative source of current well - known identities. - -* Currency-related API in ``net.corda.core.contracts.ContractsDSL`` has moved to ```net.corda.finance.CurrencyUtils``. - -* Remove `IssuerFlow` as it allowed nodes to request arbitrary amounts of cash to be issued from any remote node. Use - `CashIssueFlow` instead. - -* Some utility/extension functions (``sumOrThrow``, ``sumOrNull``, ``sumOrZero`` on ``Amount`` and ``Commodity``) - have moved to be static methods on the classes themselves. This improves the API for Java users who no longer - have to see or known about file-level FooKt style classes generated by the Kotlin compile, but means that IntelliJ - no longer auto-suggests these extension functions in completion unless you add import lines for them yourself - (this is Kotlin IDE bug KT-15286). - -* ``:finance`` module now acting as a CorDapp with regard to flow registration, schemas and serializable types. - -* ``WebServerPluginRegistry`` now has a ``customizeJSONSerialization`` which can be overridden to extend the REST JSON - serializers. In particular the IRS demos must now register the ``BusinessCalendar`` serializers. - -* Moved ``:finance`` gradle project files into a ``net.corda.finance`` package namespace. - This may require adjusting imports of Cash flow references and also of ``StartFlow`` permission in ``gradle.build`` files. - -* Removed the concept of relevancy from ``LinearState``. The ``ContractState``'s relevancy to the vault can be determined - by the flow context, the vault will process any transaction from a flow which is not derived from transaction resolution - verification. - -* Removed the tolerance attribute from ``TimeWindowChecker`` and thus, there is no extra tolerance on the notary side anymore. - -* The ``FungibleAsset`` interface has been made simpler. The ``Commands`` grouping interface - that included the ``Move``, ``Issue`` and ``Exit`` interfaces have all been removed, while the ``move`` function has - been renamed to ``withNewOwnerAndAmount`` to be consistent with the ``withNewOwner`` function of the ``OwnableState``. - -* The ``IssueCommand`` interface has been removed from ``Structures``, because, due to the introduction of nonces per - transaction component, the issue command does not need a nonce anymore and it does not require any other attributes. - -* As a consequence of the above and the simpler ``FungibleAsset`` format, fungible assets like ``Cash`` now use - ``class Issue : TypeOnlyCommandData()``, because it's only its presence (``Issue``) that matters. - -* A new `PrivacySalt` transaction component is introduced, which is now an attribute in ``TraversableTransaction`` and - inherently in ``WireTransaction``. - -* A new ``nonces: List`` feature has been added to ``FilteredLeaves``. - -* Due to the ``nonces`` and ``PrivacySalt`` introduction, new functions have been added to ``MerkleTransaction``: - ``fun serializedHash(x: T, privacySalt: PrivacySalt?, index: Int): SecureHash`` - ``fun serializedHash(x: T, nonce: SecureHash): SecureHash`` - ``fun computeNonce(privacySalt: PrivacySalt, index: Int)``. - -* A new ``SignatureMetadata`` data class is introduced with two attributes, ``platformVersion: Int`` and - ``schemeNumberID: Int`` (the signature scheme used). - -* As part of the metadata support in signatures, a new ``data class SignableData(val txId: SecureHash, val signatureMetadata: SignatureMetadata)`` - is introduced, which represents the object actually signed. - -* The unused ``MetaData`` and ``SignatureType`` in ``crypto`` package have been removed. - -* The ``class TransactionSignature(bytes: ByteArray, val by: PublicKey, val signatureMetadata:`` - ``SignatureMetadata): DigitalSignature(bytes)`` class is now utilised Vs the old ``DigitalSignature.WithKey`` for - Corda transaction signatures. Practically, it takes the ``signatureMetadata`` as an extra input, in order to support - signing both the transaction and the extra metadata. - -* To reflect changes in the signing process, the ``Crypto`` object is now equipped with the: - ``fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature`` and - ``fun doVerify(txId: SecureHash, transactionSignature: TransactionSignature): Boolean`` functions. - -* ``SerializationCustomization.addToWhitelist()`` now accepts multiple classes via varargs. - -* Two functions to easily sign a ``FilteredTransaction`` have been added to ``ServiceHub``: - ``createSignature(filteredTransaction: FilteredTransaction, publicKey: PublicKey)`` and - ``createSignature(filteredTransaction: FilteredTransaction)`` to sign with the legal identity key. - -* A new helper method ``buildFilteredTransaction(filtering: Predicate)`` is added to ``SignedTransaction`` to - directly build a ``FilteredTransaction`` using provided filtering functions, without first accessing the - ``tx: WireTransaction``. - -* Test type ``NodeHandle`` now has method ``stop(): CordaFuture`` that terminates the referenced node. - -* Fixed some issues in IRS demo: - * Fixed leg and floating leg notional amounts were not displayed for created deals neither in single nor in list view. - * Parties were not displayed for created deals in single view. - * Non-default notional amounts caused the creation of new deals to fail. - -.. warning:: Renamed configuration property key `basedir` to `baseDirectory`. This will require updating existing configuration files. - -* Removed deprecated parts of the API. - -* Removed ``PluginServiceHub``. Replace with ``ServiceHub`` for ``@CordaService`` constructors. - -* ``X509CertificateHolder`` has been removed from the public API, replaced by ``java.security.X509Certificate``. - -* Moved ``CityDatabase`` out of ``core`` and into ``finance`` - -* All of the ``serializedHash`` and ``computeNonce`` functions have been removed from ``MerkleTransaction``. - The ``serializedHash(x: T)`` and ``computeNonce`` were moved to ``CryptoUtils``. - -* Two overloaded methods ``componentHash(opaqueBytes: OpaqueBytes, privacySalt: PrivacySalt,`` - ``componentGroupIndex: Int, internalIndex: Int): SecureHash`` and ``componentHash(nonce: SecureHash, opaqueBytes: OpaqueBytes): SecureHash`` have - been added to ``CryptoUtils``. Similarly to ``computeNonce``, they internally use SHA256d for nonce and leaf hash - computations. - -* The ``verify(node: PartialTree, usedHashes: MutableList): SecureHash`` in ``PartialMerkleTree`` has been - renamed to ``rootAndUsedHashes`` and is now public, as it is required in the verify function of ``FilteredTransaction``. - -* ``TraversableTransaction`` is now an abstract class extending ``CoreTransaction``. ``WireTransaction`` and - ``FilteredTransaction`` now extend ``TraversableTransaction``. - -* Two classes, ``ComponentGroup(open val groupIndex: Int, open val components: List)`` and - ``FilteredComponentGroup(override val groupIndex: Int, override val components:`` - ``List, val nonces: List, val partialMerkleTree:`` - ``PartialMerkleTree): ComponentGroup(groupIndex, components)`` have been added, which are properties - of the ``WireTransaction`` and ``FilteredTransaction``, respectively. - -* ``checkAllComponentsVisible(componentGroupEnum: ComponentGroupEnum)`` is added to ``FilteredTransaction``, a new - function to check if all components are visible in a specific component-group. - -* To allow for backwards compatibility, ``WireTransaction`` and ``FilteredTransaction`` have new fields and - constructors: ``WireTransaction(componentGroups: List, privacySalt: PrivacySalt = PrivacySalt())``, - ``FilteredTransaction private constructor(id: SecureHash,filteredComponentGroups:`` - ``List, groupHashes: List``. ``FilteredTransaction`` is still built via - ``buildFilteredTransaction(wtx: WireTransaction, filtering: Predicate)``. - -* ``FilteredLeaves`` class have been removed and as a result we can directly call the components from - ``FilteredTransaction``, such as ``ftx.inputs`` Vs the old ``ftx.filteredLeaves.inputs``. - -* A new ``ComponentGroupEnum`` is added with the following enum items: ``INPUTS_GROUP``, ``OUTPUTS_GROUP``, - ``COMMANDS_GROUP``, ``ATTACHMENTS_GROUP``, ``NOTARY_GROUP``, ``TIMEWINDOW_GROUP``. - -* ``ContractUpgradeFlow.Initiator`` has been renamed to ``ContractUpgradeFlow.Initiate`` - -* ``@RPCSinceVersion``, ``RPCException`` and ``PermissionException`` have moved to ``net.corda.client.rpc``. - -* Current implementation of SSL in ``CordaRPCClient`` has been removed until we have a better solution which doesn't rely - on the node's keystore. - -.. _changelog_m14: - -Milestone 14 ------------- - -* Changes in ``NodeInfo``: - - * ``PhysicalLocation`` was renamed to ``WorldMapLocation`` to emphasise that it doesn't need to map to a truly physical - location of the node server. - * Slots for multiple IP addresses and ``legalIdentitiesAndCert`` entries were introduced. Addresses are no longer of type - ``SingleMessageRecipient``, but of ``NetworkHostAndPort``. - -* ``ServiceHub.storageService`` has been removed. ``attachments`` and ``validatedTransactions`` are now direct members of - ``ServiceHub``. - -* Mock identity constants used in tests, such as ``ALICE``, ``BOB``, ``DUMMY_NOTARY``, have moved to ``net.corda.testing`` - in the ``test-utils`` module. - -* ``DummyContract``, ``DummyContractV2``, ``DummyLinearContract`` and ``DummyState`` have moved to ``net.corda.testing.contracts`` - in the ``test-utils`` modules. - -* In Java, ``QueryCriteriaUtilsKt`` has moved to ``QueryCriteriaUtils``. Also ``and`` and ``or`` are now instance methods - of ``QueryCriteria``. - -* ``random63BitValue()`` has moved to ``CryptoUtils`` - -* Added additional common Sort attributes (see ``Sort.CommandStateAttribute``) for use in Vault Query criteria - to include STATE_REF, STATE_REF_TXN_ID, STATE_REF_INDEX - -* Moved the core flows previously found in ``net.corda.flows`` into ``net.corda.core.flows``. This is so that all packages - in the ``core`` module begin with ``net.corda.core``. - -* ``FinalityFlow`` can now be subclassed, and the ``broadcastTransaction`` and ``lookupParties`` function can be - overridden in order to handle cases where no single transaction participant is aware of all parties, and therefore - the transaction must be relayed between participants rather than sent from a single node. - -* ``TransactionForContract`` has been removed and all usages of this class have been replaced with usage of - ``LedgerTransaction``. In particular ``Contract.verify`` and the ``Clauses`` API have been changed and now take a - ``LedgerTransaction`` as passed in parameter. The principal consequence of this is that the types of the input and output - collections on the transaction object have changed, so it may be necessary to ``map`` down to the ``ContractState`` - sub-properties in existing code. - -* Added various query methods to ``LedgerTransaction`` to simplify querying of states and commands. In the same vain - ``Command`` is now parameterised on the ``CommandData`` field. - -* Kotlin utilities that we deemed useful enough to keep public have been moved out of ``net.corda.core.Utils`` and into - ``net.corda.core.utilities.KotlinUtils``. The other utilities have been marked as internal. - -* Changes to ``Cordformation``/ cordapp building: - - * ``Cordformation`` modifies the JAR task to make cordapps build as semi fat JARs containing all dependencies - except other cordapps and Corda core dependencies. - * ``Cordformation`` adds a ``corda`` and ``cordaRuntime`` configuration to projects which cordapp developers should - use to exclude core Corda JARs from being built into Cordapp fat JARs. - -* ``database`` field in ``AbstractNode`` class has changed the type from ``org.jetbrains.exposed.sql.Database`` to - ‘net.corda.node.utilities.CordaPersistence’ - no change is needed for the typical use - (i.e. services.database.transaction { code block } ) however a change is required when Database was explicitly declared - -* ``DigitalSignature.LegallyIdentifiable``, previously used to identify a signer (e.g. in Oracles), has been removed. - One can use the public key to derive the corresponding identity. - -* Vault Query improvements and fixes: - - * FIX inconsistent behaviour: Vault Query defaults to UNCONSUMED in all QueryCriteria types - - * FIX serialization error: Vault Query over RPC when using custom attributes using VaultCustomQueryCriteria. - - * Aggregate function support: extended VaultCustomQueryCriteria and associated DSL to enable specification of - aggregate functions (sum, max, min, avg, count) with, optional, group by clauses and sorting (on calculated aggregate). - - * Pagination simplification. Pagination continues to be optional, with following changes: - - - If no PageSpecification provided then a maximum of MAX_PAGE_SIZE (200) results will be returned, otherwise we fail-fast - with a ``VaultQueryException`` to alert the API user to the need to specify a PageSpecification. Internally, we no - longer need to calculate a results count (thus eliminating an expensive SQL query) unless a PageSpecification is - supplied (note: that a value of -1 is returned for total_results in this scenario). Internally, we now use the - AggregateFunction capability to perform the count. - - Paging now starts from 1 (was previously 0). - - * Additional Sort criteria: by StateRef (or constituents: txId, index) - -* Confidential identities API improvements - - * Registering anonymous identities now takes in AnonymousPartyAndPath - * AnonymousParty.toString() now uses toStringShort() to match other toString() functions - * Add verifyAnonymousIdentity() function to verify without storing an identity - * Replace pathForAnonymous() with anonymousFromKey() which matches actual use-cases better - * Add unit test for fetching the anonymous identity from a key - * Update verifyAnonymousIdentity() function signature to match registerAnonymousIdentity() - * Rename AnonymisedIdentity to AnonymousPartyAndPath - * Remove certificate from AnonymousPartyAndPath as it's not actually used. - * Rename registerAnonymousIdentity() to verifyAndRegisterAnonymousIdentity() - -* Added JPA ``AbstractPartyConverter`` to ensure identity schema attributes are persisted securely according to type - (well known party, resolvable anonymous party, completely anonymous party). - -.. _changelog_m13: - -Milestone 13 ------------- - -Special thank you to `Frederic Dalibard `_, for his contribution which adds -support for more currencies to the DemoBench and Explorer tools. - -* A new Vault Query service: - - * Implemented using JPA and Hibernate, this new service provides the ability to specify advanced queries using - criteria specification sets for both vault attributes and custom contract specific attributes. In addition, new - queries provide sorting and pagination capabilities. - The new API provides two function variants which are exposed for usage within Flows and by RPC clients: - - - ``queryBy()`` for point-in-time snapshot queries - (replaces several existing VaultService functions and a number of Kotlin-only extension functions) - - ``trackBy()`` for snapshot and streaming updates - (replaces the VaultService ``track()`` function and the RPC ``vaultAndUpdates()`` function) - - Existing VaultService API methods will be maintained as deprecated until the following milestone release. - - * The NodeSchema service has been enhanced to automatically generate mapped objects for any ContractState objects - that extend FungibleAsset or LinearState, such that common attributes of those parent states are persisted to - two new vault tables: vault_fungible_states and vault_linear_states (and thus queryable using the new Vault Query - service API). - Similarly, two new common JPA superclass schemas (``CommonSchemaV1.FungibleState`` and - ``CommonSchemaV1.LinearState``) mirror the associated FungibleAsset and LinearState interface states to enable - CorDapp developers to create new custom schemas by extension (rather than duplication of common attribute mappings) - - * A new configurable field ``requiredSchemas`` has been added to the CordaPluginRegistry to enable CorDapps to - register custom contract state schemas they wish to query using the new Vault Query service API (using the - ``VaultCustomQueryCriteria``). - - * See :doc:`api-vault-query` for full details and code samples of using the new Vault Query service. - -* Identity and cryptography related changes: - - * Enable certificate validation in most scenarios (will be enforced in all cases in an upcoming milestone). - - * Added DER encoded format for CompositeKey so they can be used in X.509 certificates. - - * Corrected several tests which made assumptions about counterparty keys, which are invalid when confidential - identities are used. - - * A new RPC has been added to support fuzzy matching of X.500 names, for instance, to translate from user input to - an unambiguous identity by searching the network map. - - * A function for deterministic key derivation ``Crypto.deriveKeyPair(privateKey: PrivateKey, seed: ByteArray)`` - has been implemented to support deterministic ``KeyPair`` derivation using an existing private key and a seed - as inputs. This operation is based on the HKDF scheme and it's a variant of the hardened parent-private -> - child-private key derivation function of the BIP32 protocol, but it doesn't utilize extension chain codes. - Currently, this function supports the following schemes: ECDSA secp256r1 (NIST P-256), ECDSA secp256k1 and - EdDSA ed25519. - -* A new ``ClassWhitelist`` implementation, ``AllButBlacklisted`` is used internally to blacklist classes/interfaces, - which are not expected to be serialised during checkpoints, such as ``Thread``, ``Connection`` and ``HashSet``. - This implementation supports inheritance and if a superclass or superinterface of a class is blacklisted, so is - the class itself. An ``IllegalStateException`` informs the user if a class is blacklisted and such an exception is - returned before checking for ``@CordaSerializable``; thus, blacklisting precedes annotation checking. - -* ``TimeWindow`` has a new 5th factory method ``TimeWindow.fromStartAndDuration(fromTime: Instant, duration: Duration)`` - which takes a start-time and a period-of-validity (after this start-time) as inputs. - -* The node driver has moved to net.corda.testing.driver in the test-utils module. - -* Web API related collections ``CordaPluginRegistry.webApis`` and ``CordaPluginRegistry.staticServeDirs`` moved to - ``net.corda.webserver.services.WebServerPluginRegistry`` in ``webserver`` module. - Classes serving Web API should now extend ``WebServerPluginRegistry`` instead of ``CordaPluginRegistry`` - and they should be registered in ``resources/META-INF/services/net.corda.webserver.services.WebServerPluginRegistry``. - -* Added a flag to the driver that allows the running of started nodes in-process, allowing easier debugging. - To enable use `driver(startNodesInProcess = true) { .. }`, or `startNode(startInSameProcess = true, ..)` - to specify for individual nodes. - -* Dependencies changes: - * Upgraded Dokka to v0.9.14. - * Upgraded Gradle Plugins to 0.12.4. - * Upgraded Apache ActiveMQ Artemis to v2.1.0. - * Upgraded Netty to v4.1.9.Final. - * Upgraded BouncyCastle to v1.57. - * Upgraded Requery to v1.3.1. - -.. _changelog_m12: - -Milestone 12 (First Public Beta) --------------------------------- - -* Quite a few changes have been made to the flow API which should make things simpler when writing CorDapps: - - * ``CordaPluginRegistry.requiredFlows`` is no longer needed. Instead annotate any flows you wish to start via RPC with - ``@StartableByRPC`` and any scheduled flows with ``@SchedulableFlow``. - - * ``CordaPluginRegistry.servicePlugins`` is also no longer used, along with ``PluginServiceHub.registerFlowInitiator``. - Instead annotate your initiated flows with ``@InitiatedBy``. This annotation takes a single parameter which is the - initiating flow. This initiating flow further has to be annotated with ``@InitiatingFlow``. For any services you - may have, such as oracles, annotate them with ``@CordaService``. These annotations will be picked up automatically - when the node starts up. - - * Due to these changes, when unit testing flows make sure to use ``AbstractNode.registerInitiatedFlow`` so that the flows - are wired up. Likewise for services use ``AbstractNode.installCordaService``. - - * Related to ``InitiatingFlow``, the ``shareParentSessions`` boolean parameter of ``FlowLogic.subFlow`` has been - removed. This was an unfortunate parameter that unnecessarily exposed the inner workings of flow sessions. Now, if - your sub-flow can be started outside the context of the parent flow then annotate it with ``@InitiatingFlow``. If - it's meant to be used as a continuation of the existing parent flow, such as ``CollectSignaturesFlow``, then it - doesn't need any annotation. - - * The ``InitiatingFlow`` annotation also has an integer ``version`` property which assigns the initiating flow a version - number, defaulting to 1 if it's not specified. This enables versioning of flows with nodes only accepting communication - if the version number matches. At some point we will support the ability for a node to have multiple versions of the - same flow registered, enabling backwards compatibility of flows. - - * ``ContractUpgradeFlow.Instigator`` has been renamed to just ``ContractUpgradeFlow``. - - * ``NotaryChangeFlow.Instigator`` has been renamed to just ``NotaryChangeFlow``. - - * ``FlowLogic.getCounterpartyMarker`` is no longer used and been deprecated for removal. If you were using this to - manage multiple independent message streams with the same party in the same flow then use sub-flows instead. - -* There are major changes to the ``Party`` class as part of confidential identities: - - * ``Party`` has moved to the ``net.corda.core.identity`` package; there is a deprecated class in its place for - backwards compatibility, but it will be removed in a future release and developers should move to the new class as soon - as possible. - * There is a new ``AbstractParty`` superclass to ``Party``, which contains just the public key. This now replaces - use of ``Party`` and ``PublicKey`` in state objects, and allows use of full or anonymised parties depending on - use-case. - * A new ``PartyAndCertificate`` class has been added which aggregates a Party along with an X.509 certificate and - certificate path back to a network trust root. This is used where a Party and its proof of identity are required, - for example in identity registration. - * Names of parties are now stored as a ``X500Name`` rather than a ``String``, to correctly enforce basic structure of the - name. As a result all node legal names must now be structured as X.500 distinguished names. - -* The identity management service takes an optional network trust root which it will validate certificate paths to, if - provided. A later release will make this a required parameter. - -* There are major changes to transaction signing in flows: - - * You should use the new ``CollectSignaturesFlow`` and corresponding ``SignTransactionFlow`` which handle most - of the details of this for you. They may get more complex in future as signing becomes a more featureful - operation. ``ServiceHub.legalIdentityKey`` no longer returns a ``KeyPair``, it instead returns just the - ``PublicKey`` portion of this pair. The ``ServiceHub.notaryIdentityKey`` has changed similarly. The goal of this - change is to keep private keys encapsulated and away from most flow code/Java code, so that the private key - material can be stored in HSMs and other key management devices. - * The ``KeyManagementService`` no longer provides any mechanism to request the node's ``PrivateKey`` objects directly. - Instead signature creation occurs in the ``KeyManagementService.sign``, with the ``PublicKey`` used to indicate - which of the node's keypairs to use. This lookup also works for ``CompositeKey`` scenarios - and the service will search for a leaf key hosted on the node. - * The ``KeyManagementService.freshKey`` method now returns only the ``PublicKey`` portion of the newly generated ``KeyPair`` - with the ``PrivateKey`` kept internally to the service. - * Flows which used to acquire a node's ``KeyPair``, typically via ``ServiceHub.legalIdentityKey``, - should instead use the helper methods on ``ServiceHub``. In particular to freeze a ``TransactionBuilder`` and - generate an initial partially signed ``SignedTransaction`` the flow should use ``ServiceHub.toSignedTransaction``. - Flows generating additional party signatures should use ``ServiceHub.createSignature``. Each of these methods is - provided with two signatures. One version that signs with the default node key, the other which allows key selection - by passing in the ``PublicKey`` partner of the desired signing key. - * The original ``KeyPair`` signing methods have been left on the ``TransactionBuilder`` and ``SignedTransaction``, but - should only be used as part of unit testing. - -* ``Timestamp`` used for validation/notarization time-range has been renamed to ``TimeWindow``. - There are now 4 factory methods ``TimeWindow.fromOnly(fromTime: Instant)``, - ``TimeWindow.untilOnly(untilTime: Instant)``, ``TimeWindow.between(fromTime: Instant, untilTime: Instant)`` and - ``TimeWindow.withTolerance(time: Instant, tolerance: Duration)``. - Previous constructors ``TimeWindow(fromTime: Instant, untilTime: Instant)`` and - ``TimeWindow(time: Instant, tolerance: Duration)`` have been removed. - -* The Bouncy Castle library ``X509CertificateHolder`` class is now used in place of ``X509Certificate`` in order to - have a consistent class used internally. Conversions to/from ``X509Certificate`` are done as required, but should - be avoided where possible. - -* The certificate hierarchy has been changed in order to allow corda node to sign keys with proper certificate chain. - * The corda node will now be issued a restricted client CA for identity/transaction key signing. - * TLS certificate are now stored in `sslkeystore.jks` and identity keys are stored in `nodekeystore.jks` - -.. warning:: The old keystore will need to be removed when upgrading to this version. - -Milestone 11.1 --------------- - -* Fix serialisation error when starting a flow. -* Automatically whitelist subclasses of `InputStream` when serialising. -* Fix exception in DemoBench on Windows when loading CorDapps into the Node Explorer. -* Detect when localhost resolution is broken on MacOSX, and provide instructions on how to fix it. - -Milestone 11.0 --------------- - -* API changes: - * Added extension function ``Database.transaction`` to replace ``databaseTransaction``, which is now deprecated. - - * Starting a flow no longer enables progress tracking by default. To enable it, you must now invoke your flow using - one of the new ``CordaRPCOps.startTrackedFlow`` functions. ``FlowHandle`` is now an interface, and its ``progress: Observable`` - field has been moved to the ``FlowProgressHandle`` child interface. Hence developers no longer need to invoke ``notUsed`` - on their flows' unwanted progress-tracking observables. - - * Moved ``generateSpend`` and ``generateExit`` functions into ``OnLedgerAsset`` from the vault and - ``AbstractConserveAmount`` clauses respectively. - - * Added ``CompositeSignature`` and ``CompositeSignatureData`` as part of enabling ``java.security`` classes to work - with composite keys and signatures. - - * ``CompositeKey`` now implements ``java.security.PublicKey`` interface, so that keys can be used on standard classes - such as ``Certificate``. - - * There is no longer a need to transform single keys into composite - ``composite`` extension was removed, it is - impossible to create ``CompositeKey`` with only one leaf. - - * Constructor of ``CompositeKey`` class is now private. Use ``CompositeKey.Builder`` to create a composite key. - Keys emitted by the builder are normalised so that it's impossible to create a composite key with only one node. - (Long chains of single nodes are shortened.) - - * Use extension function ``PublicKeys.keys`` to access all keys belonging to an instance of ``PublicKey``. For a - ``CompositeKey``, this is equivalent to ``CompositeKey.leafKeys``. - - * Introduced ``containsAny``, ``isFulfilledBy``, ``keys`` extension functions on ``PublicKey`` - ``CompositeKey`` - type checking is done there. - -* Corda now requires JDK 8u131 or above in order to run. Our Kotlin now also compiles to JDK8 bytecode, and so you'll need - to update your CorDapp projects to do the same. E.g. by adding this to ``build.gradle``: - -.. parsed-literal:: - - tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - languageVersion = "1.1" - apiVersion = "1.1" - jvmTarget = "1.8" - } - } - -.. - - or by adjusting ``Settings/Build,Execution,Deployment/Compiler/KotlinCompiler`` in IntelliJ:: - - - Language Version: 1.1 - - API Version: 1.1 - - Target JVM Version: 1.8 - -* DemoBench is now installed as ``Corda DemoBench`` instead of ``DemoBench``. - -* Rewrote standard test identities to have full X.500 distinguished names. As part of this work we standardised on a - smaller set of test identities, to reduce risk of subtle differences (i.e. similar common names varying by whitespace) - in naming making it hard to diagnose issues. - -Milestone 10.0 --------------- - -Special thank you to `Qian Hong `_, `Marek Skocovsky `_, -`Karel Hajek `_, and `Jonny Chiu `_ for their contributions -to Corda in M10. - -.. warning:: Due to incompatibility between older version of IntelliJ and gradle 3.4, you will need to upgrade Intellij -to 2017.1 (with kotlin-plugin v1.1.1) in order to run Corda demos in IntelliJ. You can download the latest IntelliJ - from `JetBrains `_. - -.. warning:: The Kapt-generated models are no longer included in our codebase. If you experience ``unresolved references`` -errors when building in IntelliJ, please rebuild the schema model by running ``gradlew kaptKotlin`` in Windows or - ``./gradlew kaptKotlin`` in other systems. Alternatively, perform a full gradle build or install. - -.. note:: Kapt is used to generate schema model and entity code (from annotations in the codebase) using the Kotlin Annotation -processor. - -* Corda DemoBench: - * DemoBench is a new tool to make it easy to configure and launch local Corda nodes. A very useful tool to demonstrate - to your colleagues the fundamentals of Corda in real-time. It has the following features: - - * Clicking "Add node" creates a new tab that lets you edit the most important configuration properties of the node - before launch, such as its legal name and which CorDapps will be loaded. - * Each tab contains a terminal emulator, attached to the pseudoterminal of the node. This lets you see console output. - * You can launch an Corda Explorer instance for each node via the DemoBench UI. Credentials are handed to the Corda - Explorer so it starts out logged in already. - * Some basic statistics are shown about each node, informed via the RPC connection. - * Another button launches a database viewer in the system browser. - * The configurations of all running nodes can be saved into a single ``.profile`` file that can be reloaded later. - - * Download `Corda DemoBench `_. - -* Vault: - * Soft Locking is a new feature implemented in the vault which prevent a node constructing transactions that attempt - to use the same input(s) simultaneously. - * Such transactions would result in naturally wasted effort when the notary rejects them as double spend attempts. - * Soft locks are automatically applied to coin selection (eg. cash spending) to ensure that no two transactions attempt - to spend the same fungible states. - -* Corda Shell : - * The shell lets developers and node administrators easily command the node by running flows, RPCs and SQL queries. - * It provides a variety of commands to monitor the node. - * The Corda Shell is based on the popular `CRaSH project `_ and new commands can be easily - added to the node by simply dropping Groovy or Java files into the node's ``shell-commands`` directory. - * We have many enhancements planned over time including SSH access, more commands and better tab completion. - -* API changes: - * The new Jackson module provides JSON/YAML serialisers for common Corda datatypes. - If you have previously been using the JSON support in the standalone web server, - please be aware that Amounts are now serialised as strings instead of { quantity, token } pairs as before. - The old format is still accepted, but the new JSON will be produced using strings like "1000.00 USD" when writing. - You can use any format supported by ``Amount.parseCurrency`` as input. - - * We have restructured client package in this milestone. - * ``CordaClientRPC`` is now in the new ``:client:rpc`` module. - * The old ``:client`` module has been split up into ``:client:jfx`` and ``:client:mock``. - * We also have a new ``:node-api`` module (package ``net.corda.nodeapi``) which contains the shared code between - ``node`` and ``client``. - - * The basic Amount API has been upgraded to have support for advanced financial use cases and to better integrate with - currency reference data. - -* Configuration: - * Replace ``artemisPort`` with ``p2pPort`` in Gradle configuration. - * Replace ``artemisAddress`` with ``p2pAddress`` in node configuration. - * Added ``rpcAddress`` in node configuration for non-ssl RPC connection. - -* Object Serialization: - * Pool Kryo instances for efficiency. - -* RPC client changes: - * RPC clients can now connect to the node without the need for SSL. This requires a separate port on the Artemis broker, - SSL must not be used for RPC connection. - * CordaRPCClient now needs to connect to ``rpcAddress`` rather than ``p2pAddress``. - -* Dependencies changes: - * Upgraded Kotlin to v1.1.1. - * Upgraded Gradle to v3.4.1. - * Upgraded requery to v1.2.1. - * Upgraded H2 to v1.4.194. - * Replaced kotlinx-support-jdk8 with kotlin-stdlib-jre8. - -* Improvements: - * Added ``--version`` command line flag to print the version of the node. - * Flows written in Java can now execute a sub-flow inside ``UntrustworthyData.unwrap``. - * Added optional out-of-process transaction verification. Any number of external verifier processes may be attached - to the node which can handle loadbalanced verification requests. - -* Bug fixes: - * ``--logging-level`` command line flag was previously broken, now correctly sets the logging level. - * Fixed bug whereby Cash Exit was not taking into account the issuer reference. - - -Milestone 9.1 -------------- - -* Correct web server ports for IRS demo. -* Correct which corda-webserver JAR is published to Maven. - -Milestone 9 ------------ - -* With thanks to `Thomas Schroeter `_ for the Byzantine fault tolerant (BFT) - notary prototype. -* Web server is a separate JAR. This is a breaking change. The new webserver JAR (``corda-webserver.jar``) - must be invoked separately to node startup, using the command``java -jar corda-webserver.jar`` in the same - directory as the ``node.conf``. Further changes are anticipated in upcoming milestone releases. - -* API: - - * Pseudonymous ``AnonymousParty`` class added as a superclass of ``Party``. - * Split ``CashFlow`` into individual ``CashIssueFlow``, ``CashPaymentFlow`` and ``CashExitFlow`` flows, so that fine - grained permissions can be applied. Added ``CashFlowCommand`` for use-cases where cash flow triggers need to be - captured in an object that can be passed around. - * ``CordaPluginRegistry`` method ``registerRPCKryoTypes`` is renamed ``customizeSerialization`` and the argument - types now hide the presence of Kryo. - * New extension functions for encoding/decoding to base58, base64, etc. See - ``core/src/main/kotlin/net/corda/core/crypto/EncodingUtils.kt`` - * Add ``openAttachment`` function to Corda RPC operations, for downloading an attachment from a node's data storage. - * Add ``getCashBalances`` function to Corda RPC operations, for getting cash balances from a node's vault. - -* Configuration: - * ``extraAdvertisedServiceIds`` config is now a list of strings, rather than a comma separated string. For example - ``[ "corda.interest_rates" ]`` instead of ``"corda.interest_rates"``. - -* Flows: - * Split ``CashFlow`` into separate ``CashIssueFlow``, ``CashPaymentFlow`` and ``CashExitFlow`` so that permissions can - be assigned individually. - * Split single example user into separate "bankUser" and "bigCorpUser" so that permissions for the users make sense - rather than being a combination of both roles. - * ``ProgressTracker`` emits exception thrown by the flow, allowing the ANSI renderer to correctly stop and print the error - -* Object Serialization: - - * Consolidated Kryo implementations across RPC and P2P messaging with whitelisting of classes via plugins or with - ``@CordaSerializable`` for added node security. - -* Privacy: - * Non-validating notary service now takes in a ``FilteredTransaction`` so that no potentially sensitive transaction - details are unnecessarily revealed to the notary - -* General: - * Add vault service persistence using Requery - * Certificate signing utility output is now more verbose - -Milestone 8 ------------ - -* Node memory usage and performance improvements, demo nodes now only require 200 MB heap space to run. - -* The Corda node no longer runs an internal web server, it's now run in a separate process. Driver and Cordformation have - been updated to reflect this change. Existing CorDapps should be updated with additional calls to the new ``startWebserver()`` - interface in their Driver logic (if they use the driver e.g. in integration tests). See the IRS demo for an example. - -* Data model: ``Party`` equality is now based on the owning key, rather than the owning key and name. This is important for - party anonymisation to work, as each key must identify exactly one party. - -* Contracts: created new composite clauses called ``AllOf``, ``AnyOf`` and ``FirstOf`` to replace ``AllComposition``, ``AnyComposition`` - and ``FirstComposition``, as this is significantly clearer in intent. ``AnyOf`` also enforces that at least one subclause - must match, whereas ``AnyComposition`` would accept no matches. - -* Explorer: the user can now configure certificate path and keystore/truststore password on the login screen. - -* Documentation: - - * Key Concepts section revamped with new structure and content. - * Added more details to :doc:`getting-set-up` page. - -* Flow framework: improved exception handling with the introduction of ``FlowException``. If this or a subtype is thrown - inside a flow it will propagate to all counterparty flows and subsequently be thrown by them as well. Existing flows such as - ``NotaryFlow.Client/Service`` and others have been modified to throw a ``FlowException`` (in this particular case a - ``NotaryException``) instead of sending back error responses. - -* Notary flow: provide complete details of underlying error when contract validation fails. - -Milestone 7 ------------ - -* With thanks to `Thomas Schroeter `_ ``NotaryFlow`` is now idempotent. - -* Explorer: - - * The GUI for the explorer now shows other nodes on the network map and the transactions between them. - * Map resolution increased and allows zooming and panning. - * `Video demonstration `_ of the Node Explorer. - -* The CorDapp template now has a Java example that parallels the Kotlin one for developers more comfortable with Java. - ORM support added to the Kotlin example. - -* Demos: - - * Added the Bank of Corda demo - a demo showing a node (Bank of Corda) acting as an issuer of Cash, and a client - driver providing both Web and RPC access to request issuance of cash. - * Demos now use RPC to communicate with the node from the webserver. This brings the demos more in line with how - interaction with nodes is expected to be. The demos now treat their webservers like clients. This will also allow - for the splitting of the webserver from the node for milestone 8. - * Added a SIMM valuation demo integration test to catch regressions. - -* Security: - - * MQ broker of the node now requires authentication which means that third parties cannot connect to and - listen to queues on the Node. RPC and P2P between nodes is now authenticated as a result of this change. - This also means that nodes or RPC users cannot pretend to be other nodes or RPC users. - * The node now does host verification of any node that connects to it and prevents man in the middle attacks. - -* Improvements: - - * Vault updates now contain full ``StateAndRef`` which allows subscribers to check whether the update contains - relevant states. - * Cash balances are calculated using aggregate values to prevent iterating through all states in the vault, which - improves performance. - * Multi-party services, such as notaries, are now load balanced and represented as a single ``Party`` object. - * The Notary Change flow now supports encumbrances. - -Milestone 6 ------------ - -* Added the `Corda technical white paper <_static/corda-technical-whitepaper.pdf>`_. Note that its current version - is 0.5 to reflect the fact that the Corda design is still evolving. Although we expect only relatively small tweaks - at this point, when Corda reaches 1.0 so will the white paper. - -* Major documentation restructuring and new content: - - * More details on Corda node internals. - * New CorDapp tutorial. - * New tutorial on building transactions. - * New tutorials on how to run and use a notary service. - -* An experimental version of the deterministic JVM sandbox has been added. It is not integrated with the node and will - undergo some significant changes in the coming releases before it is integrated, as the code is finished, as bugs are - found and fixed, and as the platform subset we choose to expose is finalised. Treat this as an outline of the basic - approach rather than something usable for production. - -* Developer experience: - - * Samples have been merged back into the main repository. All samples can now be run via command line or IntelliJ. - - * Added a Client RPC python example. - - * Node console output now displays concise startup information, such as startup time or web address. All logging to - the console is suppressed apart from errors and flow progress tracker steps. It can be re-enabled by passing - ``--log-to-console`` command line parameter. Note that the log file remains unchanged and will still contain all - log entries. - - * The ``runnodes`` scripts generated by the Gradle plugins now open each node in separate terminal windows or (on macOS) tabs. - - * A much more complete template app. - - * JARs now available on Maven Central. - -* Data model: A party is now identified by a composite key (formerly known as a "public key tree") instead of a single public key. - Read more in :ref:`composite-keys`. This allows expressing distributed service identities, e.g. a distributed notary. - In the future this will also allow parties to use multiple signing keys for their legal identity. - -* Decentralised consensus: A prototype RAFT based notary composed of multiple nodes has been added. This implementation - is optimised for high performance over robustness against malicious cluster members, which may be appropriate for - some financial situations. - -* Node explorer app: - - * New theme aligned with the Corda branding. - * The New Transaction screen moved to the Cash View (as it is used solely for cash transactions) - * Removed state machine/flow information from Transaction table. A new view for this will be created in a future release. - * Added a new Network View that displays details of all nodes on the network. - * Users can now configure the reporting currency in settings. - * Various layout and performance enhancements. - -* Client RPC: - - * Added a generic ``startFlow`` method that enables starting of any flow, given sufficient permissions. - * Added the ability for plugins to register additional classes or custom serialisers with Kryo for use in RPC. - * ``rpc-users.properties`` file has been removed with RPC user settings moved to the config file. - -* Configuration changes: It is now possible to specify a custom legal name for any of the node's advertised services. - -* Added a load testing framework which allows stress testing of a node cluster, as well as specifying different ways of - disrupting the normal operation of nodes. See :doc:`loadtesting`. - -* Improvements to the experimental contract DSL, by Sofus Mortensen of Nordea Bank (please give Nordea a shoutout too). - -API changes: - -* The top level package has been renamed from ``com.r3corda`` to ``net.corda``. -* Protocols have been renamed to "flows". -* ``OpaqueBytes`` now uses ``bytes`` as the field name rather than ``bits``. - -Milestone 5 ------------ - -* A simple RPC access control mechanism. Users, passwords and permissions can be defined in a configuration file. - This mechanism will be extended in future to support standard authentication systems like LDAP. - -* New features in the explorer app and RPC API for working with cash: - - * Cash can now be sent, issued and exited via RPC. - * Notes can now be associated with transactions. - * Hashes are visually represented using identicons. - * Lots of functional work on the explorer UI. You can try it out by running ``gradle tools:explorer:runDemoNodes`` to run - a local network of nodes that swap cash with each other, and then run ``gradle tools:explorer:run`` to start - the app. - -* A new demo showing shared valuation of derivatives portfolios using the ISDA SIMM has been added. Note that this app - relies on a proprietary implementation of the ISDA SIMM business logic from OpenGamma. A stub library is provided - to ensure it compiles but if you want to use the app for real please contact us. - -* Developer experience (we plan to do lots more here in milestone 6): - - * Demos and samples have been split out of the main repository, and the initial developer experience continues to be - refined. All necessary JARs can now be installed to Maven Local by simply running ``gradle install``. - * It's now easier to define a set of nodes to run locally using the new "CordFormation" gradle plugin, which - defines a simple DSL for creating networks of nodes. - * The template CorDapp has been upgraded with more documentation and showing more features. - -* Privacy: transactions are now structured as Merkle trees, and can have sections "torn off" - presented for - verification and signing without revealing the rest of the transaction. - -* Lots of bug fixes, tweaks and polish starting the run up to the open source release. - -API changes: - -* Plugin service classes now take a ``PluginServiceHub`` rather than a ``ServiceHubInternal``. -* ``UniqueIdentifier`` equality has changed to only take into account the underlying UUID. -* The contracts module has been renamed to finance, to better reflect what it is for. - -Milestone 4 ------------ - -New features in this release: - -* Persistence: - - * States can now be written into a relational database and queried using JDBC. The schemas are defined by the - smart contracts and schema versioning is supported. It is reasonable to write an app that stores data in a mix - of global ledger transactions and local database tables which are joined on demand, using join key slots that - are present in many state definitions. Read more about :doc:`api-persistence`. - * The embedded H2 SQL database is now exposed by default to any tool that can speak JDBC. The database URL is - printed during node startup and can be used to explore the database, which contains both node internal data - and tables generated from ledger states. - * Protocol checkpoints are now stored in the database as well. Message processing is now atomic with protocol - checkpointing and run under the same RDBMS transaction. - * MQ message deduplication is now handled at the app layer and performed under the RDMS transaction, so - ensuring messages are only replayed if the RDMS transaction rolled back. - * "The wallet" has been renamed to "the vault". - -* Client RPC: - - * New RPCs added to subscribe to snapshots and update streams state of the vault, currently executing protocols - and other important node information. - * New tutorial added that shows how to use the RPC API to draw live transaction graphs on screen. - -* Protocol framework: - - * Large simplifications to the API. Session management is now handled automatically. Messages are now routed - based on identities rather than node IP addresses. - -* Decentralised consensus: - - * A standalone one-node notary backed by a JDBC store has been added. - * A prototype RAFT based notary composed of multiple nodes is available on a branch. - -* Data model: - - * Compound keys have been added as preparation for merging a distributed RAFT based notary. Compound keys - are trees of public keys in which interior nodes can have validity thresholds attached, thus allowing - boolean formulas of keys to be created. This is similar to Bitcoin's multi-sig support and the data model - is the same as the InterLedger Crypto-Conditions spec, which should aid interoperate in future. Read more about - key trees in the ":doc:`api-core-types`" article. - * A new tutorial has been added showing how to use transaction attachments in more detail. - -* Testnet - - * Permissioning infrastructure phase one is built out. The node now has a notion of development mode vs normal - mode. In development mode it works like M3 and the SSL certificates used by nodes running on your local - machine all self-sign using a developer key included in the source tree. When development mode is not active, - the node won't start until it has a signed certificate. Such a certificate can be obtained by simply running - an included command line utility which generates a CSR and submits it to a permissioning service, then waits - for the signed certificate to be returned. Note that currently there is no public Corda testnet, so we are - not currently running a permissioning service. - -* Standalone app development: - - * The Corda libraries that app developers need to link against can now be installed into your local Maven - repository, where they can then be used like any other JAR. See :doc:`running-a-node`. - -* User interfaces: - - * Infrastructure work on the node explorer is now complete: it is fully switched to using the MQ based RPC system. - * A library of additional reactive collections has been added. This API builds on top of Rx and the observable - collections API in Java 8 to give "live" data structures in which the state of the node and ledger can be - viewed as an ordinary Java ``List``, ``Map`` and ``Set``, but which also emit callbacks when these views - change, and which can have additional views derived in a functional manner (filtered, mapped, sorted, etc). - Finally, these views can then be bound directly into JavaFX UIs. This makes for a concise and functional - way of building application UIs that render data from the node, and the API is available for third party - app developers to use as well. We believe this will be highly productive and enjoyable for developers who - have the option of building JavaFX apps (vs web apps). - * The visual network simulator tool that was demoed back in April as part of the first Corda live demo has - been merged into the main repository. - -* Documentation - - * New secure coding guidelines. Corda tries to eliminate as many security mistakes as practical via the type - system and other mechanically checkable processes, but there are still things that one must be aware of. - * New attachments tutorial. - * New Client RPC tutorial. - * More tutorials on how to build a standalone CorDapp. - -* Testing - - * More integration testing support - * New micro-DSLs for expressing expected sequences of operations with more or less relaxed ordering constraints. - * QuickCheck generators to create streams of randomised transactions and other basic types. QuickCheck is a way - of writing unit tests that perform randomised fuzz testing of code, originally developed by the Haskell - community and now also available in Java. - -API changes: - -* The transaction types (Signed, Wire, LedgerTransaction) have moved to ``net.corda.core.transactions``. You can - update your code by just deleting the broken import lines and letting your IDE re-import them from the right - location. -* ``AbstractStateReplacementProtocol.verifyProposal`` has changed its prototype in a minor way. -* The ``UntrustworthyData.validate`` method has been renamed to ``unwrap`` - the old name is now deprecated. -* The wallet, wallet service, etc. are now vault, vault service, etc. These better reflect the intent that they - are a generic secure data store, rather than something which holds cash. -* The protocol send/receive APIs have changed to no longer require a session id. Please check the current version - of the protocol framework tutorial for more details. - -Milestone 3 ------------ - -* More work on preparing for the testnet: - - * Corda is now a standalone app server that loads "CorDapps" into itself as plugins. Whilst the existing IRS - and trader demos still exist for now, these will soon be removed and there will only be a single Corda node - program. Note that the node is a single, standalone jar file that is easier to execute than the demos. - * Project Vega (shared SIMM modelling for derivative portfolios) has already been converted to be a CorDapp. - * Significant work done on making the node persist its wallet data to a SQL backend, with more on the way. - * Upgrades and refactorings of the core transaction types in preparation for the incoming sandboxing work. - -* The Clauses API that seeks to make writing smart contracts easier has gone through another design iteration, - with the result that clauses are now cleaner and more composable. -* Improvements to the protocol API for finalising transactions (notarising, transmitting and storing). -* Lots of work done on an MQ based client API. -* Improvements to the developer site: - - * The developer site has been re-read from start to finish and refreshed for M3 so there should be no obsolete - texts or references anywhere. - * The Corda non-technical white paper is now a part of the developer site and git repository. The LaTeX source is - also provided so if you spot any issues with it, you can send us patches. - * There is a new section on how to write CorDapps. - -* Further R&D work by Sofus Mortensen in the experimental module on a new 'universal' contract language. -* SSL for the REST API and webapp server can now be configured. - - -Milestone 2 ------------ - -* Big improvements to the interest rate swap app: - - * A new web app demonstrating the IRS contract has been added. This can be used as an example for how to interact with - the Corda API from the web. - * Simplifications to the way the demo is used from the command line. - * :doc:`Detailed documentation on how the contract works and can be used ` has been written. - * Better integration testing of the app. - -* Smart contracts have been redesigned around reusable components, referred to as "clauses". The cash, commercial paper - and obligation contracts now share a common issue clause. -* New code in the experimental module (note that this module is a place for work-in-progress code which has not yet gone - through code review and which may, in general, not even function correctly): - - * Thanks to the prolific Sofus Mortensen @ Nordea Bank, an experimental generic contract DSL that is based on the famous - 2001 "Composing contracts" paper has been added. We thank Sofus for this great and promising research, which is so - relevant in the wake of the DAO hack. - * The contract code from the recent trade finance demos is now in experimental. This code comes thanks to a - collaboration of the members; all credit to: - - * Mustafa Ozturk @ Natixis - * David Nee @ US Bank - * Johannes Albertsen @ Dankse Bank - * Rui Hu @ Nordea - * Daniele Barreca @ Unicredit - * Sukrit Handa @ Scotiabank - * Giuseppe Cardone @ Banco Intesa - * Robert Santiago @ BBVA - -* The usability of the command line demo programs has been improved. -* All example code and existing contracts have been ported to use the new Java/Kotlin unit testing domain-specific - languages (DSLs) which make it easy to construct chains of transactions and verify them together. This cleans up - and unifies the previous ad-hoc set of similar DSLs. A tutorial on how to use it has been added to the documentation. - We believe this largely completes our testing story for now around smart contracts. Feedback from bank developers - during the Trade Finance project has indicated that the next thing to tackle is docs and usability improvements in - the protocols API. -* Significant work done towards defining the "CorDapp" concept in code, with dynamic loading of API services and more to - come. -* Inter-node communication now uses SSL/TLS and AMQP/1.0, albeit without all nodes self-signing at the moment. A real - PKI for the p2p network will come later. -* Logging is now saved to files with log rotation provided by Log4J. - -API changes: - -* Some utility methods and extension functions that are specific to certain contract types have moved packages: just - delete the import lines that no longer work and let IntelliJ replace them with the correct package paths. -* The ``arg`` method in the test DSL is now called ``command`` to be consistent with the rest of the data model. -* The messaging APIs have changed somewhat to now use a new ``TopicSession`` object. These APIs will continue to change - in the upcoming releases. -* Clauses now have default values provided for ``ifMatched``, ``ifNotMatched`` and ``requiredCommands``. - -New documentation: - -* :doc:`contract-catalogue` -* :doc:`contract-irs` -* :doc:`tutorial-test-dsl` - -Milestone 1 ------------ - -Highlights of this release: - -* Event scheduling. States in the ledger can now request protocols to be invoked at particular times, for states - considered relevant by the wallet. -* Upgrades to the notary/consensus service support: - - * There is now a way to change the notary controlling a state. - * You can pick between validating and non-validating notaries, these let you select your privacy/robustness trade-off. - -* A new obligation contract that supports bilateral and multilateral netting of obligations, default tracking and - more. -* Improvements to the financial type system, with core classes and contracts made more generic. -* Switch to a better digital signature algorithm: ed25519 instead of the previous JDK default of secp256r1. -* A new integration test suite. -* A new Java unit testing DSL for contracts, similar in spirit to the one already developed for Kotlin users (which - depended on Kotlin specific features). -* An experimental module, where developers who want to work with the latest Corda code can check in contracts/cordapp - code before it's been fully reviewed. Code in this module has compiler warnings suppressed but we will still make - sure it compiles across refactorings. -* Persistence improvements: transaction data is now stored to disk and automatic protocol resume is now implemented. -* Many smaller bug fixes, cleanups and improvements. - -We have new documentation on: - -* :doc:`event-scheduling` -* :doc:`api-core-types` -* :doc:`key-concepts-consensus` - -Summary of API changes (not exhaustive): - -* Notary/consensus service: - - * ``NotaryService`` is now extensible. - * Every ``ContractState`` now has to specify a *participants* field, which is a list of parties that are able to - consume this state in a valid transaction. This is used for e.g. making sure all relevant parties obtain the updated - state when changing a notary. - * Introduced ``TransactionState``, which wraps ``ContractState``, and is used when defining a transaction output. - The notary field is moved from ``ContractState`` into ``TransactionState``. - * Every transaction now has a *type* field, which specifies custom build & validation rules for that transaction type. - Currently two types are supported: General (runs the default build and validation logic) and NotaryChange ( - contract code is not run during validation, checks that the notary field is the only difference between the - inputs and outputs). - ``TransactionBuilder()`` is now abstract, you should use ``TransactionType.General.Builder()`` for building transactions. - -* The cash contract has moved from ``net.corda.contracts`` to ``net.corda.contracts.cash`` -* ``Amount`` class is now generic, to support non-currency types such as physical assets. Where you previously had just - ``Amount``, you should now use ``Amount``. -* Refactored the Cash contract to have a new FungibleAsset superclass, to model all countable assets that can be merged - and split (currency, barrels of oil, etc.) -* Messaging: - - * ``addMessageHandler`` now has a different signature as part of error handling changes. - * If you want to return nothing to a protocol, use ``Ack`` instead of ``Unit`` from now on. - -* In the IRS contract, dateOffset is now an integer instead of an enum. -* In contracts, you now use ``tx.getInputs`` and ``tx.getOutputs`` instead of ``getInStates`` and ``getOutStates``. This is - just a renaming. -* A new ``NonEmptySet`` type has been added for cases where you wish to express that you have a collection of unique - objects which cannot be empty. -* Please use the global ``newSecureRandom()`` function rather than instantiating your own SecureRandom's from now on, as - the custom function forces the use of non-blocking random drivers on Linux. - -Milestone 0 ------------ - -This is the first release, which includes: - -* Some initial smart contracts: cash, commercial paper, interest rate swaps -* An interest rate oracle -* The first version of the protocol/orchestration framework -* Some initial support for pluggable consensus mechanisms -* Tutorials and documentation explaining how it works -* Much more ... +* Vault states are migrated when moving from V3 to V4: the relevancy column is correctly filled, and the state party table is populated. + Note: This means Corda can be slow to start up for the first time after upgrading from V3 to V4. \ No newline at end of file diff --git a/docs/source/cipher-suites.rst b/docs/source/cipher-suites.rst index 8f2d313c45..25e4c99664 100644 --- a/docs/source/cipher-suites.rst +++ b/docs/source/cipher-suites.rst @@ -15,9 +15,9 @@ carefully selected based on various factors, such as provided security-level and with various HSM vendors, algorithm standardisation, variety of cryptographic primitives, business demand, option for post-quantum resistance, side channel security, efficiency and rigorous testing. -Before we present the pool of supported schemes it is useful to be familiar with :doc:`key-concepts-identity`, -:doc:`permissioning` and :doc:`api-identity`. An important design decision in Corda is its shared hierarchy -between the TLS and Node Identity certificates. +Before we present the pool of supported schemes it is useful to be familiar with :doc:`permissioning` +and :doc:`api-identity`. An important design decision in Corda is its shared hierarchy between the +TLS and Node Identity certificates. Certificate hierarchy --------------------- diff --git a/docs/source/conf.py b/docs/source/conf.py index 8aea05c0ef..0516f279ed 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,20 +1,19 @@ # -*- coding: utf-8 -*- -# -# R3 prototyping documentation build configuration file, created by -# sphinx-quickstart on Mon Nov 23 21:19:35 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. import sphinx_rtd_theme import sys, os +############################################################################ +# +# TEXT SUBSTITUTIONS + +rst_epilog = """ +.. |java_version| replace:: 8u171 +.. |kotlin_version| replace:: 1.2.71 +""" + +############################################################################ + sys.path.append(os.path.abspath('../ext/')) # If extensions (or modules to document with autodoc) are in another directory, @@ -60,20 +59,11 @@ author = u'R3 DLG' # built documents. # # The short X.Y version. -version = 'V4.0' +version = 'Master' # The full version, including alpha/beta/rc tags. -release = 'V4.0' +release = 'Master' # The version for use in the dropdown html. -html_context = {'version': 'V4.0'} - -# Properties. -kotlin_version = '1.2.71' - -rst_epilog = """ -.. |kotlin_version| replace:: {kotlin_version} -""".format( - kotlin_version = kotlin_version, -) +html_context = {'version': 'Master'} # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/cordapp-build-systems.rst b/docs/source/cordapp-build-systems.rst index 5444790601..07da3debc7 100644 --- a/docs/source/cordapp-build-systems.rst +++ b/docs/source/cordapp-build-systems.rst @@ -38,6 +38,8 @@ the following folder and files from the `Kotlin CorDapp Template ` and re-starting the node. - uploading the attachment JAR to the node via RPC, either programmatically (see :ref:`Connecting to a node via RPC `) + or via the :doc:`shell` by issuing the following command: -Which method to use depends on the reason for installing the CorDapp and is detailed below. +``>>> run uploadAttachment jar: path/to/the/file.jar`` -.. note:: this behaviour is to protect the node from executing contract code that was not vetted. It is a temporary precaution until the - Deterministic JVM is integrated into Corda whereby execution takes place in a sandboxed environment which protects the node from malicious code. - - -Installing Contract Attachments for Previously Unknown CorDapps -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When you are processing a transaction that contains states that need to be verified by contracts in CorDapps not currently installed on the node, the -Contract attachment will be received from a peer over the p2p network. CorDapps received this way are considered **untrusted** and will throw an `UntrustedAttachmentsException` +Contract attachments that are received from a peer over the p2p network are considered **untrusted** and will throw a `UntrustedAttachmentsException` exception when processed by a listening flow that cannot resolve that attachment from its local attachment storage. The flow will be aborted and sent to the nodes flow hospital for recovery and retry. The untrusted attachment JAR will be stored in the nodes local attachment store for review by a node operator. It can be downloaded for viewing using the following CRaSH shell command: ``>>> run openAttachment id: `) -and subsequently retry the failed flow. Currently this requires a node-restart which will automatically retry the failed flows. - -.. note:: from Corda 4.1 you will also be able to upload the attachment to the store, as described below. - -Installing Contract Attachments for Older Versions of CorDapps -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you need to install older versions of a CorDapp in order to verify chains of states created with older versions of a contract, you can upload the -older CorDapp to the attachment store. Placing the older CorDapp in the ``cordapps`` directory will not work in this case, -as you can only have one CorDapp loaded per contract. The latest version of the CorDapp should be the one installed in the ``cordapps`` folder. - -As above, the untrusted attachment JAR will be stored in the nodes local attachment store for review by a node operator. It can be downloaded for -viewing using the following CRaSH shell command: - -``>>> run openAttachment id: >> run uploadAttachment jar: path/to/the/trusted-file.jar`` +``>>> run uploadAttachment jar: path/to/the/trusted-file.jar`` + +and subsequently retry the failed flow (currently this requires a node re-start). + +.. note:: this behaviour is to protect the node from executing contract code that was not vetted. It is a temporary precaution until the + Deterministic JVM is integrated into Corda whereby execution takes place in a sandboxed environment which protects the node from malicious code. + + -and subsequently retry the failed flow. Currently this requires a node-restart which will automatically retry the failed flows. diff --git a/docs/source/cordapp-overview.rst b/docs/source/cordapp-overview.rst index 7c59ed6701..4829eebd57 100644 --- a/docs/source/cordapp-overview.rst +++ b/docs/source/cordapp-overview.rst @@ -45,4 +45,35 @@ the following components: * An ``ExitBondFlow``, allowing existing ``BondState`` states to be exited from the ledger After installing this CorDapp, the node owner will be able to use the flows defined by the CorDapp to agree ledger -updates related to issuance, sale, purchase and exit of bonds. \ No newline at end of file +updates related to issuance, sale, purchase and exit of bonds. + +Writing and building apps that run on both Corda (open source) and Corda Enterprise +----------------------------------------------------------------------------------- +Corda and Corda Enterprise are compatible and interoperable, which means you can write a CorDapp that can run on both. +To make this work in practice you should follow these steps: + +1. Ensure your CorDapp is designed per :doc:`Structuring a CorDapp ` and annotated according to :ref:`CorDapp separation `. + In particular, it is critical to separate the consensus-critical parts of your application (contracts, states and their dependencies) from + the rest of the business logic (flows, APIs, etc). + The former - the **CorDapp kernel** - is the Jar that will be attached to transactions creating/consuming your states and is the Jar + that any node on the network verifying the transaction must execute. + +.. note:: It is also important to understand how to manage any dependencies a CorDapp may have on 3rd party libraries and other CorDapps. + Please read :ref:`Setting your dependencies ` to understand the options and recommendations with regards to correctly Jar'ing CorDapp dependencies. + +2. Compile this **CorDapp kernel** Jar once, and then depend on it from your workflows Jar (or Jars - see below). Importantly, if + you want your app to work on both Corda and Corda Enterprise, you must compile this Jar against Corda, not Corda Enterprise. + This is because, in future, we may add additional functionality to Corda Enterprise that is not in Corda and you may inadvertently create a + CorDapp kernel that does not work on Corda open source. Compiling against Corda open source as a matter of course prevents this risk, as well + as preventing the risk that you inadvertently create two different versions of the Jar, which will have different hashes and hence break compatibility + and interoperability. + +.. note:: As of Corda 4 it is recommended to use :ref:`CorDapp Jar signing ` to leverage the new signature constraints functionality. + +3. Your workflow Jar(s) should depend on the **CorDapp kernel** (contract, states and dependencies). Importantly, you can create different workflow + Jars for Corda and Corda Enterprise, because the workflows Jar is not consensus critical. For example, you may wish to add additional features + to your CorDapp for when it is run on Corda Enterprise (perhaps it uses advanced features of one of the supported enterprise databases or includes + advanced database migration scripts, or some other Enterprise-only feature). + +In summary, structure your app as kernel (contracts, states, dependencies) and workflow (the rest) and be sure to compile the kernel +against Corda open source. You can compile your workflow (Jars) against the distribution of Corda that they target. diff --git a/docs/source/docker-image.rst b/docs/source/docker-image.rst index ec6dcd7c1c..3fd725eec7 100644 --- a/docs/source/docker-image.rst +++ b/docs/source/docker-image.rst @@ -20,7 +20,7 @@ In this example, the certificates are stored at ``/home/user/cordaBase/certifica -v /path/to/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-zulu-4.0:latest + corda/corda-zulu-5.0-snapshot:latest As the node runs within a container, several mount points are required: @@ -55,7 +55,7 @@ In this example, we have previously generated a network-parameters file using th -v /home/user/sharedFolder/network-parameters:/opt/corda/network-parameters \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-zulu-4.0:latest + corda/corda-zulu-5.0-snapshot:latest There is a new mount ``/home/user/sharedFolder/node-infos:/opt/corda/additional-node-infos`` which is used to hold the ``nodeInfo`` of all the nodes within the network. As the node within the container starts up, it will place it's own nodeInfo into this directory. This will allow other nodes also using this folder to see this new node. @@ -79,7 +79,7 @@ Joining TestNet -e LOCALITY="London" -e COUNTRY="GB" \ -v /home/user/docker/config:/etc/corda \ -v /home/user/docker/certificates:/opt/corda/certificates \ - corda/corda-zulu-4.0:latest config-generator --testnet + corda/corda-zulu-5.0-snapshot:latest config-generator --testnet ``$MY_PUBLIC_ADDRESS`` will be the public address that this node will be advertised on. ``$ONE_TIME_DOWNLOAD_KEY`` is the one-time code provided for joining TestNet. @@ -104,7 +104,7 @@ It is now possible to start the node using the generated config and certificates -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-zulu-4.0:latest + corda/corda-zulu-5.0-snapshot:latest Joining an existing Compatibility Zone @@ -128,7 +128,7 @@ It is possible to configure the name of the Trust Root file by setting the ``TRU -e MY_EMAIL_ADDRESS="cordauser@r3.com" \ -v /home/user/docker/config:/etc/corda \ -v /home/user/docker/certificates:/opt/corda/certificates \ - corda/corda-zulu-4.0:latest config-generator --generic + corda/corda-zulu-5.0-snapshot:latest config-generator --generic Several environment variables must also be passed to the container to allow it to register: @@ -159,5 +159,5 @@ Once the container has finished performing the initial registration, the node ca -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -p 10200:10200 \ -p 10201:10201 \ - corda/corda-zulu-4.0:latest + corda/corda-zulu-5.0-snapshot:latest diff --git a/docs/source/docker.rst b/docs/source/docker.rst deleted file mode 100644 index 3315b5cb57..0000000000 --- a/docs/source/docker.rst +++ /dev/null @@ -1,7 +0,0 @@ -Docker -===== - -.. toctree:: - :maxdepth: 1 - - docker-image diff --git a/docs/source/event-scheduling.rst b/docs/source/event-scheduling.rst index 34633c61dc..cc356ffb71 100644 --- a/docs/source/event-scheduling.rst +++ b/docs/source/event-scheduling.rst @@ -67,7 +67,7 @@ Let's take an example of the interest rate swap fixings for our scheduled events .. container:: codeset - .. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/contract/IRS.kt + .. literalinclude:: ../../samples/irs-demo/cordapp/contracts-irs/src/main/kotlin/net/corda/irs/contract/IRS.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 diff --git a/docs/source/flow-state-machines.rst b/docs/source/flow-state-machines.rst index 8c8c217153..bd2fd638d7 100644 --- a/docs/source/flow-state-machines.rst +++ b/docs/source/flow-state-machines.rst @@ -93,7 +93,7 @@ Our flow has two parties (B and S for buyer and seller) and will proceed as foll transaction in B's local vault, and then sending it on to S who also checks it and commits the transaction to S's local vault. -You can find the implementation of this flow in the file ``finance/src/main/kotlin/net/corda/finance/TwoPartyTradeFlow.kt``. +You can find the implementation of this flow in the file ``finance/workflows/src/main/kotlin/net/corda/finance/TwoPartyTradeFlow.kt``. Assuming no malicious termination, they both end the flow being in possession of a valid, signed transaction that represents an atomic asset swap. @@ -201,7 +201,7 @@ Let's implement the ``Seller.call`` method that will be run when the flow is inv .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 4 :end-before: DOCEND 4 @@ -237,7 +237,7 @@ OK, let's do the same for the buyer side: .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -367,7 +367,7 @@ override ``checkTransaction()`` to add our own custom validation logic: .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 5 :end-before: DOCEND 5 @@ -454,7 +454,7 @@ A flow might declare some steps with code inside the flow class like this: .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 @@ -478,7 +478,7 @@ is a good idea, as that will help the users see what is coming up. You can pre-c .. container:: codeset - .. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt + .. literalinclude:: ../../finance/workflows/src/main/kotlin/net/corda/finance/flows/TwoPartyTradeFlow.kt :language: kotlin :start-after: DOCSTART 3 :end-before: DOCEND 3 diff --git a/docs/source/getting-set-up.rst b/docs/source/getting-set-up.rst index e6c2c27d52..7e727046fb 100644 --- a/docs/source/getting-set-up.rst +++ b/docs/source/getting-set-up.rst @@ -3,20 +3,17 @@ Getting set up for CorDapp development Software requirements --------------------- + Corda uses industry-standard tools: -* **Oracle JDK 8 JVM** - minimum supported version **8u171** +* **Java 8 JVM** - we require at least version **|java_version|**, but do not currently support Java 9 or higher. + We have tested with Oracle JDK, Amazon Corretto, and Red Hat's OpenJDK builds. Please note that OpenJDK builds + usually exclude JavaFX, which our GUI tools require. * **IntelliJ IDEA** - supported versions **2017.x** and **2018.x** (with Kotlin plugin version |kotlin_version|) -* **Git** - -We also use Gradle and Kotlin, but you do not need to install them. A standalone Gradle wrapper is provided, and it -will download the correct version of Kotlin. +* **Gradle** - we use 4.10 and the ``gradlew`` script in the project / samples directories will download it for you. Please note: -* Corda runs in a JVM. JVM implementations other than Oracle JDK 8 are not actively supported. However, if you do - choose to use OpenJDK, you will also need to install OpenJFX - * Applications on Corda (CorDapps) can be written in any language targeting the JVM. However, Corda itself and most of the samples are written in Kotlin. Kotlin is an `official Android language `_, and you can read more about why @@ -26,7 +23,7 @@ Please note: `getting started guide `_, and a series of `Kotlin Koans `_ -* IntelliJ IDEA is recommended due to the strength of its Kotlin integration +* IntelliJ IDEA is recommended due to the strength of its Kotlin integration. Following these software recommendations will minimize the number of errors you encounter, and make it easier for others to provide support. However, if you do use other tools, we'd be interested to hear about any issues that arise. @@ -131,7 +128,7 @@ Jetbrains offers a pre-built snap package that allows for easy, one-step install .. _fedora-label: Fedora -------------- +------ .. warning:: If you are using a Mac, Windows or Debian/Ubuntu machine, please follow the :ref:`mac-label`, :ref:`windows-label` or :ref:`deb-ubuntu-label` instructions instead. diff --git a/docs/source/index.rst b/docs/source/index.rst index bbbf636ae6..ac35a8d50d 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -33,6 +33,7 @@ We look forward to seeing what you can do with Corda! release-notes app-upgrade-notes + node-upgrade-notes .. toctree:: :caption: Development @@ -69,7 +70,7 @@ We look forward to seeing what you can do with Corda! :if_tag: htmlmode corda-network/index.md - corda-network/uat.md + corda-network/UAT.md .. conditional-toctree:: :caption: Contents diff --git a/docs/source/key-concepts-node.rst b/docs/source/key-concepts-node.rst index 7e479060cb..72663ddfdc 100644 --- a/docs/source/key-concepts-node.rst +++ b/docs/source/key-concepts-node.rst @@ -81,11 +81,13 @@ The node also has several CorDapps installed by default to handle common tasks s * Upgrading contracts * Broadcasting agreed ledger updates for recording by counterparties +.. _draining-mode: + Draining mode -^^^^^^^^^^^^^ +------------- In order to operate a clean shutdown of a node, it is important than no flows are in-flight, meaning no checkpoints should -be persisted. The node is able to be put in a Flows Draining Mode, during which: +be persisted. The node is able to be put in draining mode, during which: * Commands requiring to start new flows through RPC will be rejected. * Scheduled flows due will be ignored. @@ -93,4 +95,6 @@ be persisted. The node is able to be put in a Flows Draining Mode, during which: * All other activities will proceed as usual, ensuring that the number of in-flight flows will strictly diminish. As their number - which can be monitored through RPC - reaches zero, it is safe to shut the node down. -This property is durable, meaning that restarting the node will not reset it to its default value and that a RPC command is required. \ No newline at end of file +This property is durable, meaning that restarting the node will not reset it to its default value and that a RPC command is required. + +The node can be safely shut down via a drain using the shell. \ No newline at end of file diff --git a/docs/source/node-administration.rst b/docs/source/node-administration.rst index 9a978d1477..3f58bc7dea 100644 --- a/docs/source/node-administration.rst +++ b/docs/source/node-administration.rst @@ -128,7 +128,7 @@ due to expensive run-time costs. They can be turned on and off explicitly regard When starting Corda nodes using Cordformation runner (see :doc:`running-a-node`), you should see a startup message similar to the following: **Jolokia: Agent started with URL http://127.0.0.1:7005/jolokia/** -When starting Corda nodes using the `DriverDSL`, you should see a startup message in the logs similar to the following: +When starting Corda nodes using the 'driver DSL', you should see a startup message in the logs similar to the following: **Starting out-of-process Node USA Bank Corp, debug port is not enabled, jolokia monitoring port is 7005 {}** @@ -203,6 +203,8 @@ For launching on Windows without PowerShell, it is not possible to perform comma .. warning:: If this approach is taken, the passwords will appear in the windows command prompt history. +.. _ref-backup-recommendations: + Backup recommendations ---------------------- diff --git a/docs/source/oracles.rst b/docs/source/oracles.rst index e9c7198c84..117d08cce0 100644 --- a/docs/source/oracles.rst +++ b/docs/source/oracles.rst @@ -101,12 +101,12 @@ class that binds it to the network layer. Here is an extract from the ``NodeInterestRates.Oracle`` class and supporting types: -.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt +.. literalinclude:: ../../finance/contracts/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 -.. literalinclude:: ../../finance/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt +.. literalinclude:: ../../finance/contracts/src/main/kotlin/net/corda/finance/contracts/FinanceTypes.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 @@ -166,7 +166,7 @@ parameter and ``CommandData`` classes. Let's see how the ``sign`` method for ``NodeInterestRates.Oracle`` is written: -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/api/NodeInterestRates.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -192,7 +192,7 @@ Binding to the network The first step is to create the oracle as a service by annotating its class with ``@CordaService``. Let's see how that's done: -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/api/NodeInterestRates.kt :language: kotlin :start-after: DOCSTART 3 :end-before: DOCEND 3 @@ -201,7 +201,7 @@ done: The Corda node scans for any class with this annotation and initialises them. The only requirement is that the class provide a constructor with a single parameter of type ``ServiceHub``. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/api/NodeInterestRates.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 @@ -219,7 +219,7 @@ We mentioned the client sub-flow briefly above. They are the mechanism that cli use to interact with your oracle. Typically there will be one for querying and one for signing. Let's take a look at those for ``NodeInterestRates.Oracle``. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/RatesFixFlow.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -238,7 +238,7 @@ The oracle is invoked through sub-flows to query for values, add them to the tra the transaction signed by the oracle. Following on from the above examples, this is all encapsulated in a sub-flow called ``RatesFixFlow``. Here's the ``call`` method of that flow. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/RatesFixFlow.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/RatesFixFlow.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 @@ -255,7 +255,7 @@ As you can see, this: Here's an example of it in action from ``FixingFlow.Fixer``. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/FixingFlow.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/FixingFlow.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 @@ -276,10 +276,10 @@ containing your oracle service. You can then write tests on your mock network to verify the nodes interact with your Oracle correctly. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/OracleNodeTearOffTests.kt :language: kotlin :start-after: DOCSTART 2 :end-before: DOCEND 2 :dedent: 4 -See `here `_ for more examples. +See `here `_ for more examples. diff --git a/docs/source/quickstart-index.rst b/docs/source/quickstart-index.rst index c20b4ed10b..3b3abb4382 100644 --- a/docs/source/quickstart-index.rst +++ b/docs/source/quickstart-index.rst @@ -16,86 +16,86 @@ I want to: Learn about Corda for the first time ------------------------------------ -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+=========================================================================================================+ -| :doc:`key-concepts` | The key concepts and features of the Corda Platform | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`getting-set-up` | Set up your machine for running and developing CorDapps | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`tutorial-cordapp` | A guide to running a simple CorDapp | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| Useful links | Description | ++============================================+============================================================================================+ +| :doc:`key-concepts` | The key concepts and features of the Corda Platform | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`getting-set-up` | Set up your machine for running and developing CorDapps | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`tutorial-cordapp` | A guide to running a simple CorDapp | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ .. _quickstart-develop: Develop a CorDapp ----------------- -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+=========================================================================================================+ -| :doc:`hello-world-introduction` | A coding walk-through of a basic CorDapp | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`cordapp-overview` | An introduction to CordApps | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`writing-a-cordapp` | How to structure a CorDapp project | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`cordapp-build-systems` | How to build a CorDapp | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`corda-api` | A guide to the CorDapp API | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| Useful links | Description | ++============================================+============================================================================================+ +| :doc:`hello-world-introduction` | A coding walk-through of a basic CorDapp | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`cordapp-overview` | An introduction to CordApps | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`writing-a-cordapp` | How to structure a CorDapp project | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`cordapp-build-systems` | How to build a CorDapp | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`corda-api` | A guide to the CorDapp API | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ .. _quickstart-run: Run and test a CorDapp on local Corda network --------------------------------------------- -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+=========================================================================================================+ -| :doc:`generating-a-node` | Guidance on creating Corda nodes for development and testing locally and on Docker | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-structure` | The Corda node folder structure and how to name your node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`running-a-node` | Guidance on running Corda nodes locally and on Docker | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`setting-up-a-corda-network` | Considerations for setting up a Corda network | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| Useful links | Description | ++================================================+========================================================================================+ +| :doc:`generating-a-node` | Guidance on creating Corda nodes for development and testing locally and on Docker | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`node-structure` | The Corda node folder structure and how to name your node | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`running-a-node` | Guidance on running Corda nodes locally and on Docker | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`setting-up-a-dynamic-compatibility-zone` | Considerations for setting up a Corda network | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | ++------------------------------------------------+----------------------------------------------------------------------------------------+ +| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | ++------------------------------------------------+----------------------------------------------------------------------------------------+ .. _quickstart-add: Add a node to an existing test Corda network -------------------------------------------- -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+=========================================================================================================+ -| :doc:`node-structure` | The Corda node folder structure and how to name your node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`deploying-a-node` | A step-by-step guide on deploying a Corda node to your own server | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`azure-vm` | A step-by-step guide on creating a Corda Network on Azure | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`aws-vm` | A step-by-step guide on creating a Corda Network on AWS | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ -| :doc:`blob-inspector` | A troubleshooting tool allowing you to read the contents of a binary blob file | -+--------------------------------------------+---------------------------------------------------------------------------------------------------------+ ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| Useful links | Description | ++============================================+============================================================================================+ +| :doc:`node-structure` | The Corda node folder structure and how to name your node | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`deploying-a-node` | A step-by-step guide on deploying a Corda node to your own server | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`azure-vm` | A step-by-step guide on creating a Corda Network on Azure | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`aws-vm` | A step-by-step guide on creating a Corda Network on AWS | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ +| :doc:`blob-inspector` | A troubleshooting tool allowing you to read the contents of a binary blob file | ++--------------------------------------------+--------------------------------------------------------------------------------------------+ .. _quickstart-production: @@ -103,5 +103,9 @@ Add a node to an existing production network -------------------------------------------- +---------------------------------------------------------------------------------------------------------+ -| Contact R3 Solutions Engineering at support@r3.com | +| Corda Network is a global production network of Corda nodes, operated by the independent | +| Corda Network Foundation. You can learn more here: https://corda.network/participation/index.html | ++---------------------------------------------------------------------------------------------------------+ +| Corda Testnet is a test network, operated for the community by R3. You can learn | +| more here: https://testnet.corda.network | +---------------------------------------------------------------------------------------------------------+ diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 28849fe297..5b5192ee2c 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -3,23 +3,27 @@ Release notes for Corda 4 .. _release_notes_v4_0: -Here we are, 9 months and 1500 plus commits later... and it's a bouncing baby software release! +Welcome to the Corda 4 release notes. Please read these carefully to understand what's new in this +release and how the changes can help you. Just as prior releases have brought with them commitments +to wire and API stability, Corda 4 comes with those same guarantees. States and apps valid in +Corda 3 are transparently usable in Corda 4. -We are really proud to release Corda 4 to the open source community today. It's been a long time in -the making, but we think you'll agree worth the wait. +For app developers, we strongly recommend reading ":doc:`app-upgrade-notes`". This covers the upgrade +procedure, along with how you can adjust your app to opt-in to new features making your app more secure and +easier to upgrade in future. -Just as prior releases have brought with them commitments to wire and API stability, Corda 4 -comes with those same guarantees. States and apps valid in Corda 3 are transparently usable in Corda 4. +For node operators, we recommend reading ":doc:`node-upgrade-notes`". The upgrade procedure is simple but +it can't hurt to read the instructions anyway. -.. important:: We strongly recommend reading ":doc:`app-upgrade-notes`". This covers the upgrade procedure, - along with how you can adjust your app to opt-in to new features making your app more secure and - easier to upgrade. - -Additionally, be aware that the data model upgrades are changes to the Corda consensus rules. To use +Additionally, be aware that the data model improvements are changes to the Corda consensus rules. To use apps that benefit from them, *all* nodes in a compatibility zone must be upgraded and the zone must be enforcing that upgrade. This may take time in large zones like the testnet. Please take this into account for your own schedule planning. +.. warning:: There is a bug in Corda 3.3 that causes problems when receiving a ``FungibleState`` created + by Corda 4. There will shortly be a followup Corda 3.4 release that corrects this error. Interop between + Corda 3 and Corda 4 will require that Corda 3 users are on the latest patchlevel release. + .. contents:: Changes for developers in Corda 4 @@ -29,9 +33,9 @@ Reference states ++++++++++++++++ With Corda 4 we are introducing the concept of "reference input states". These allow smart contracts -to read data from the ledger without simultaneously updating it. They're useful not only for any kind of -reference data such as rates, healthcare codes, geographical information etc, but for anywhere -you might have used a SELECT JOIN in a SQL based app. +to reference data from the ledger in a transaction without simultaneously updating it. They're useful +not only for any kind of reference data such as rates, healthcare codes, geographical information etc, +but for anywhere you might have used a SELECT JOIN in a SQL based app. A reference input state is a ``ContractState`` which can be referred to in a transaction by the contracts of input and output states but, significantly, whose contract is not executed as part of the transaction @@ -211,9 +215,9 @@ version requirement if they start using new features and APIs. Dependency upgrades +++++++++++++++++++ -We've raised the minimum JDK to 8u171, needed to get fixes for certain ZIP compression bugs. +We've raised the minimum JDK to |java_version|, needed to get fixes for certain ZIP compression bugs. -We've upgraded to Kotlin 1.2.71 so your apps can now benefit from the new features in this language release. +We've upgraded to Kotlin |kotlin_version| so your apps can now benefit from the new features in this language release. We've upgraded to Gradle 4.10.1. @@ -297,4 +301,4 @@ Miscellaneous changes To learn more about smaller changes, please read the :doc:`changelog`. -Finally, we have added some new jokes. Thankyou and good night! +Finally, we have added some new jokes. Thank you and good night! diff --git a/docs/source/tutorial-custom-notary.rst b/docs/source/tutorial-custom-notary.rst index cf7b78a0ff..4c96751a3e 100644 --- a/docs/source/tutorial-custom-notary.rst +++ b/docs/source/tutorial-custom-notary.rst @@ -11,7 +11,7 @@ This will ensure that it is recognised as a notary service. The custom notary service class should provide a constructor with two parameters of types ``ServiceHubInternal`` and ``PublicKey``. Note that ``ServiceHubInternal`` does not provide any API stability guarantees. -.. literalinclude:: ../../samples/notary-demo/src/main/kotlin/net/corda/notarydemo/MyCustomNotaryService.kt +.. literalinclude:: ../../samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/MyCustomNotaryService.kt :language: kotlin :start-after: START 1 :end-before: END 1 @@ -20,7 +20,7 @@ The next step is to write a notary service flow. You are free to copy and modify as ``ValidatingNotaryFlow``, ``NonValidatingNotaryFlow``, or implement your own from scratch (following the ``NotaryFlow.Service`` template). Below is an example of a custom flow for a *validating* notary service: -.. literalinclude:: ../../samples/notary-demo/src/main/kotlin/net/corda/notarydemo/MyCustomNotaryService.kt +.. literalinclude:: ../../samples/notary-demo/workflows/src/main/kotlin/net/corda/notarydemo/MyCustomNotaryService.kt :language: kotlin :start-after: START 2 :end-before: END 2 diff --git a/docs/source/tutorial-observer-nodes.rst b/docs/source/tutorial-observer-nodes.rst index 9d3cdcfe75..3e70892897 100644 --- a/docs/source/tutorial-observer-nodes.rst +++ b/docs/source/tutorial-observer-nodes.rst @@ -17,7 +17,7 @@ Just define a new flow that wraps the SendTransactionFlow/ReceiveTransactionFlow .. container:: codeset - .. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/flows/AutoOfferFlow.kt + .. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/AutoOfferFlow.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 diff --git a/docs/source/tutorial-tear-offs.rst b/docs/source/tutorial-tear-offs.rst index 926c552293..b850ccb5f9 100644 --- a/docs/source/tutorial-tear-offs.rst +++ b/docs/source/tutorial-tear-offs.rst @@ -49,7 +49,7 @@ transaction components is exactly the same. Note that unlike ``WireTransaction`` The following code snippet is taken from ``NodeInterestRates.kt`` and implements a signing part of an Oracle. -.. literalinclude:: ../../samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +.. literalinclude:: ../../samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/api/NodeInterestRates.kt :language: kotlin :start-after: DOCSTART 1 :end-before: DOCEND 1 From 5548d184b6e847a55de941ec6120d88d137b21f8 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Tue, 26 Feb 2019 16:17:17 +0100 Subject: [PATCH 12/75] Dummy commit to force rebuild of the docs --- docs/source/changelog.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 6c3f946af0..94636c5b99 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -342,4 +342,6 @@ Version 4.0 The only exception to this is ``Interpolator`` and related classes. These are now in the `IRS demo workflows CorDapp `_. * Vault states are migrated when moving from V3 to V4: the relevancy column is correctly filled, and the state party table is populated. - Note: This means Corda can be slow to start up for the first time after upgrading from V3 to V4. \ No newline at end of file + Note: This means Corda can be slow to start up for the first time after upgrading from V3 to V4. + +End of changelog. From 0f87d777d903c958f545aa7b5b0c0183c0fc9d42 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Tue, 26 Feb 2019 16:48:58 +0100 Subject: [PATCH 13/75] Docs: add node-upgrade-notes --- docs/source/node-upgrade-notes.rst | 63 ++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 docs/source/node-upgrade-notes.rst diff --git a/docs/source/node-upgrade-notes.rst b/docs/source/node-upgrade-notes.rst new file mode 100644 index 0000000000..24148562d3 --- /dev/null +++ b/docs/source/node-upgrade-notes.rst @@ -0,0 +1,63 @@ +Upgrading your node to Corda 4 +============================== + +Corda releases strive to be backwards compatible, so upgrading a node is fairly straightforward and should not require changes to +applications. It consists of the following steps: + +1. Drain the node. +2. Make a backup of your node directories and/or database. +3. Replace the ``corda.jar`` file with the new version. +4. Start up the node. This step may incur a delay whilst any needed database migrations are applied. +5. Undrain it to re-enable processing of new inbound flows. + +The protocol is designed to tolerate node outages, so during the upgrade process peers on the network will wait for your node to come back. + +Step 1. Drain the node +---------------------- + +Before a node or application on it can be upgraded, the node must be put in :ref:`draining-mode`. This brings the currently running +:doc:`key-concepts-flows` to a smooth halt such that existing work is finished and new work is queuing up rather than being processed. + +Draining flows is a key task for node administrators to perform. It exists to simplify applications by ensuring apps don't have to be +able to migrate workflows from any arbitrary point to other arbitrary points, a task that would rapidly become infeasible as workflow +and protocol complexity increases. + +To drain the node, run the ``gracefulShutdown`` command. This will wait for the node to drain and then shut down the node when the drain +is complete. + +.. warning:: The length of time a node takes to drain depends on both how your applications are designed, and whether any apps are currently + talking to network peers that are offline or slow to respond. It is thus hard to give guidance on how long a drain should take, but in + an environment with well written apps and in which your counterparties are online, drains may need only a few seconds. + +Step 2. Make a backup of your node directories and/or database +-------------------------------------------------------------- + +It's always a good idea to make a backup of your data before upgrading any server. This will make it easy to roll back if there's a problem. +You can simply make a copy of the node's data directory to enable this. If you use an external non-H2 database please consult your database +user guide to learn how to make backups. + +We provide some `backup recommendations `_ if you'd like more detail. + +Step 3. Replace ``corda.jar`` with the new version +-------------------------------------------------- + +Download the latest version of Corda from `our Artifactory site `_. +Make sure it's available on your path, and that you've read the :doc:`release-notes`, in particular to discover what version of Java this +node requires. + +.. important:: Corda 4 requires Java |java_version| or any higher Java 8 patchlevel. Java 9+ is not currently supported. + +Step 4. Start up the node +------------------------- + +Start the node in the usual manner you have selected. The node will perform any automatic data migrations required, which may take some +time. If the migration process is interrupted it can be continued simply by starting the node again, without harm. + +Step 5. Undrain the node +------------------------ + +You may now do any checks that you wish to perform, read the logs, and so on. When you are ready, use this command at the shell: + +``run setFlowsDrainingModeEnabled enabled: false`` + +Your upgrade is complete. \ No newline at end of file From 6e441930120801d3547479637609345c0b520a09 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Fri, 22 Mar 2019 14:12:04 +0000 Subject: [PATCH 14/75] Fixes link. --- docs/source/network-bootstrapper.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/network-bootstrapper.rst b/docs/source/network-bootstrapper.rst index 63fb3f7c95..5286809367 100644 --- a/docs/source/network-bootstrapper.rst +++ b/docs/source/network-bootstrapper.rst @@ -24,7 +24,7 @@ You can find out more about network maps and network parameters from :doc:`netwo Bootstrapping a test network ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The Corda Network Bootstrapper can be downloaded from `here `_. +The Corda Network Bootstrapper can be downloaded from `here `_. Create a directory containing a node config file, ending in "_node.conf", for each node you want to create. Then run the following command: @@ -429,4 +429,4 @@ The Network Bootstrapper can be started with the following command line options: Sub-commands ------------ -``install-shell-extensions``: Install ``bootstrapper`` alias and auto completion for bash and zsh. See :doc:`cli-application-shell-extensions` for more info. \ No newline at end of file +``install-shell-extensions``: Install ``bootstrapper`` alias and auto completion for bash and zsh. See :doc:`cli-application-shell-extensions` for more info. From 8f81b770e035acdc0e824cb334b1b850f871ef33 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Wed, 3 Apr 2019 16:03:29 +0100 Subject: [PATCH 15/75] RELEASE - 4.1-RC01 --- constants.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index dbb49a6433..a6da6db33b 100644 --- a/constants.properties +++ b/constants.properties @@ -2,7 +2,7 @@ # because some versions here need to be matched by app authors in # their own projects. So don't get fancy with syntax! -cordaVersion=5.0-SNAPSHOT +cordaVersion=4.1-RC01 gradlePluginsVersion=4.0.42 kotlinVersion=1.2.71 java8MinUpdateVersion=171 From 358d984829644e180ee3a18a65e687229e9c1772 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Tue, 9 Apr 2019 10:10:18 +0100 Subject: [PATCH 16/75] CORDA-2825 - Fix Progress Tracker bug (#4986) (#4989) * CORDA-2825 Fix Progress Tracker bug CORDA-2825 Add discriminator CORDA-2825 Fix hashcode CORDA-2825 remove todo CORDA-2825 more tests * CORDA-2825 Address code review comments (cherry picked from commit 685f94bf6659ae4a1b7589e43f518fd9420ef5ce) --- .../corda/core/utilities/ProgressTracker.kt | 29 ++++++++- .../core/utilities/ProgressTrackerTest.kt | 62 ++++++++++++++++--- 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt index ffe765d274..96a43e80e7 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/ProgressTracker.kt @@ -2,6 +2,7 @@ package net.corda.core.utilities import net.corda.core.DeleteForDJVM import net.corda.core.internal.STRUCTURAL_STEP_PREFIX +import net.corda.core.internal.warnOnce import net.corda.core.serialization.CordaSerializable import rx.Observable import rx.Subscription @@ -34,6 +35,10 @@ import java.util.* @DeleteForDJVM class ProgressTracker(vararg inputSteps: Step) { + private companion object { + private val log = contextLogger() + } + @CordaSerializable @DeleteForDJVM sealed class Change(val progressTracker: ProgressTracker) { @@ -55,6 +60,11 @@ class ProgressTracker(vararg inputSteps: Step) { */ @CordaSerializable open class Step(open val label: String) { + private fun definitionLocation(): String = Exception().stackTrace.first { it.className != ProgressTracker.Step::class.java.name }.let { "${it.className}:${it.lineNumber}" } + + // Required when Steps with the same name are defined in multiple places. + private val discriminator: String = definitionLocation() + open val changes: Observable get() = Observable.empty() open fun childProgressTracker(): ProgressTracker? = null /** @@ -63,6 +73,17 @@ class ProgressTracker(vararg inputSteps: Step) { * Even if empty the basic details (i.e. label) of the step will be recorded for audit purposes. */ open val extraAuditData: Map get() = emptyMap() + + override fun equals(other: Any?) = when (other) { + is Step -> this.label == other.label && this.discriminator == other.discriminator + else -> false + } + + override fun hashCode(): Int { + var result = label.hashCode() + result = 31 * result + discriminator.hashCode() + return result + } } // Sentinel objects. Overrides equals() to survive process restarts and serialization. @@ -89,7 +110,12 @@ class ProgressTracker(vararg inputSteps: Step) { /** * The steps in this tracker, same as the steps passed to the constructor but with UNSTARTED and DONE inserted. */ - val steps = arrayOf(UNSTARTED, STARTING, *inputSteps, DONE) + val steps = arrayOf(UNSTARTED, STARTING, *inputSteps, DONE).also { stepsArray -> + val labels = stepsArray.map { it.label } + if (labels.toSet().size < labels.size) { + log.warnOnce("Found ProgressTracker Step(s) with the same label: ${labels.groupBy { it }.filter { it.value.size > 1 }.map { it.key }}") + } + } private var _allStepsCache: List> = _allSteps() @@ -137,7 +163,6 @@ class ProgressTracker(vararg inputSteps: Step) { } } - init { steps.forEach { configureChildTrackerForStep(it) diff --git a/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt b/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt index 13769fcb0f..fa2569a3cb 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/ProgressTrackerTest.kt @@ -1,13 +1,25 @@ package net.corda.core.utilities +import net.corda.core.serialization.internal.checkpointDeserialize +import net.corda.core.serialization.internal.checkpointSerialize +import net.corda.core.utilities.ProgressTrackerTest.NonSingletonSteps.first +import net.corda.core.utilities.ProgressTrackerTest.NonSingletonSteps.first2 +import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat import org.junit.Before +import org.junit.Rule import org.junit.Test import java.util.* import kotlin.test.assertEquals import kotlin.test.assertFails +import kotlin.test.assertNotEquals class ProgressTrackerTest { + + @Rule + @JvmField + val testCheckpointSerialization = CheckpointSerializationEnvironmentRule() + object SimpleSteps { object ONE : ProgressTracker.Step("one") object TWO : ProgressTracker.Step("two") @@ -92,7 +104,7 @@ class ProgressTrackerTest { assertEquals(pt2.currentStep, ProgressTracker.UNSTARTED) assertEquals(ProgressTracker.STARTING, pt2.nextStep()) assertEquals(ChildSteps.AYY, pt2.nextStep()) - assertEquals((stepNotification.last as ProgressTracker.Change.Position).newStep, ChildSteps.AYY) + assertEquals((stepNotification.last as ProgressTracker.Change.Position).newStep, ChildSteps.AYY) assertEquals(ChildSteps.BEE, pt2.nextStep()) } @@ -112,7 +124,7 @@ class ProgressTrackerTest { stepsTreeNotification += it } - fun assertCurrentStepsTree(index:Int, step: ProgressTracker.Step) { + fun assertCurrentStepsTree(index: Int, step: ProgressTracker.Step) { assertEquals(index, pt.stepsTreeIndex) assertEquals(step, allSteps[pt.stepsTreeIndex].second) } @@ -169,7 +181,7 @@ class ProgressTrackerTest { assertThat(stepsIndexNotifications).containsExactlyElementsOf(listOf(0, 1, 4, 7)) assertThat(stepsTreeNotification).hasSize(3) // The initial tree state, plus one per update } - + @Test fun `structure changes are pushed down when progress trackers are added`() { pt.setChildProgressTracker(SimpleSteps.TWO, pt2) @@ -186,7 +198,7 @@ class ProgressTrackerTest { stepsTreeNotification += it } - fun assertCurrentStepsTree(index:Int, step: ProgressTracker.Step) { + fun assertCurrentStepsTree(index: Int, step: ProgressTracker.Step) { assertEquals(index, pt.stepsTreeIndex) assertEquals(step.label, stepsTreeNotification.last()[pt.stepsTreeIndex].second) } @@ -223,7 +235,7 @@ class ProgressTrackerTest { stepsTreeNotification += it } - fun assertCurrentStepsTree(index:Int, step: ProgressTracker.Step) { + fun assertCurrentStepsTree(index: Int, step: ProgressTracker.Step) { assertEquals(index, pt.stepsTreeIndex) assertEquals(step.label, stepsTreeNotification.last()[pt.stepsTreeIndex].second) } @@ -273,7 +285,7 @@ class ProgressTrackerTest { pt.nextStep() pt.nextStep() pt.nextStep() - pt.changes.subscribe { steps.add(it.toString())} + pt.changes.subscribe { steps.add(it.toString()) } pt.nextStep() pt.nextStep() pt.nextStep() @@ -290,7 +302,7 @@ class ProgressTrackerTest { pt.setChildProgressTracker(SimpleSteps.TWO, pt3) val thirdStepLabels = pt.allStepsLabels - pt.stepsTreeChanges.subscribe { stepTreeNotifications.add(it)} + pt.stepsTreeChanges.subscribe { stepTreeNotifications.add(it) } // Should have one notification for original tree, then one for each time it changed. assertEquals(3, stepTreeNotifications.size) @@ -320,4 +332,40 @@ class ProgressTrackerTest { fun `cannot assign step not belonging to this progress tracker`() { assertFails { pt.currentStep = BabySteps.UNOS } } + + object NonSingletonSteps { + val first = ProgressTracker.Step("first") + val second = ProgressTracker.Step("second") + val first2 = ProgressTracker.Step("first") + fun tracker() = ProgressTracker(first, second, first2) + } + + @Test + fun `Serializing and deserializing a tracker maintains equality`() { + val step = NonSingletonSteps.first + val recreatedStep = step + .checkpointSerialize(testCheckpointSerialization.checkpointSerializationContext) + .checkpointDeserialize(testCheckpointSerialization.checkpointSerializationContext) + assertEquals(step, recreatedStep) + } + + @Test + fun `can assign a recreated equal step`() { + val tracker = NonSingletonSteps.tracker() + val recreatedStep = first + .checkpointSerialize(testCheckpointSerialization.checkpointSerializationContext) + .checkpointDeserialize(testCheckpointSerialization.checkpointSerializationContext) + tracker.currentStep = recreatedStep + } + + @Test + fun `Steps with the same label defined in different places are not equal`() { + val one = ProgressTracker.Step("one") + assertNotEquals(one, SimpleSteps.ONE) + } + + @Test + fun `Steps with the same label defined in the same place are also not equal`() { + assertNotEquals(first, first2) + } } From 13f6bc0c4dd8891b3ebf2b8ae3fc7c4824d231aa Mon Sep 17 00:00:00 2001 From: Dominic Fox <40790090+r3domfox@users.noreply.github.com> Date: Fri, 12 Apr 2019 11:54:47 +0100 Subject: [PATCH 17/75] CORDA-2848 - relax fingerprinter strictness (#5001 (#5007)) --- .../model/TypeModellingFingerPrinter.kt | 11 +++++++-- .../amqp/TypeModellingFingerPrinterTests.kt | 24 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt index c0db392d88..b0314bbc2a 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/model/TypeModellingFingerPrinter.kt @@ -139,8 +139,7 @@ private class FingerPrintingState( is LocalTypeInformation.Abstract -> fingerprintAbstract(type) is LocalTypeInformation.Singleton -> fingerprintName(type) is LocalTypeInformation.Composable -> fingerprintComposable(type) - is LocalTypeInformation.NonComposable -> throw NotSerializableException( - "Attempted to fingerprint non-composable type ${type.typeIdentifier.prettyPrint(false)}") + is LocalTypeInformation.NonComposable -> fingerprintNonComposable(type) } } @@ -176,6 +175,14 @@ private class FingerPrintingState( fingerprintTypeParameters(type.typeParameters) } + private fun fingerprintNonComposable(type: LocalTypeInformation.NonComposable) = + fingerprintWithCustomSerializerOrElse(type) { + fingerprintName(type) + fingerprintProperties(type.properties) + fingerprintInterfaces(type.interfaces) + fingerprintTypeParameters(type.typeParameters) + } + private fun fingerprintComposable(type: LocalTypeInformation.Composable) = fingerprintWithCustomSerializerOrElse(type) { fingerprintName(type) diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/TypeModellingFingerPrinterTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/TypeModellingFingerPrinterTests.kt index 79f931571a..0369363bb3 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/TypeModellingFingerPrinterTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/TypeModellingFingerPrinterTests.kt @@ -1,9 +1,14 @@ package net.corda.serialization.internal.amqp +import net.corda.serialization.internal.AllWhitelist +import net.corda.serialization.internal.NotSerializable +import net.corda.serialization.internal.model.ConfigurableLocalTypeModel import net.corda.serialization.internal.model.LocalTypeInformation import net.corda.serialization.internal.model.TypeModellingFingerPrinter +import org.assertj.core.api.Assertions.assertThat import org.junit.Test import kotlin.test.assertNotEquals +import kotlin.test.assertTrue class TypeModellingFingerPrinterTests { @@ -19,4 +24,23 @@ class TypeModellingFingerPrinterTests { assertNotEquals(fingerprinter.fingerprint(objectType), fingerprinter.fingerprint(anyType)) } + + // Not serializable, because there is no readable property corresponding to the constructor parameter + class NonSerializable(a: String) + + class HasTypeParameter + data class SuppliesTypeParameter(val value: HasTypeParameter) + + // See https://r3-cev.atlassian.net/browse/CORDA-2848 + @Test + fun `can fingerprint type with non-serializable type parameter`() { + val typeModel = ConfigurableLocalTypeModel(WhitelistBasedTypeModelConfiguration(AllWhitelist, customRegistry)) + val typeInfo = typeModel.inspect(SuppliesTypeParameter::class.java) + + assertThat(typeInfo).isInstanceOf(LocalTypeInformation.Composable::class.java) + val propertyTypeInfo = typeInfo.propertiesOrEmptyMap["value"]?.type as LocalTypeInformation.Composable + assertThat(propertyTypeInfo.typeParameters[0]).isInstanceOf(LocalTypeInformation.NonComposable::class.java) + + fingerprinter.fingerprint(typeInfo) + } } \ No newline at end of file From 43e3755c6f9109aa97f30062a368ace9b7c37afd Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Mon, 15 Apr 2019 10:23:30 +0100 Subject: [PATCH 18/75] CORDA-2621 - change message when rpc/p2p login fails (#4994) (#5020) (cherry picked from commit d15699289c675871610f40c8247d14eba2afff21) --- .../net/corda/node/internal/artemis/BrokerJaasLoginModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/artemis/BrokerJaasLoginModule.kt b/node/src/main/kotlin/net/corda/node/internal/artemis/BrokerJaasLoginModule.kt index ecc069b97a..8aa59026c8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/artemis/BrokerJaasLoginModule.kt +++ b/node/src/main/kotlin/net/corda/node/internal/artemis/BrokerJaasLoginModule.kt @@ -108,7 +108,7 @@ class BrokerJaasLoginModule : BaseBrokerJaasLoginModule() { if (e is IllegalArgumentException && e.stackTrace.any { it.className == "org.apache.activemq.artemis.protocol.amqp.sasl.PlainSASL" }) { log.trace("SASL Login failed.") } else { - log.error("Login failed: ${e.message}", e) + log.warn("Login failed: ${e.message}") } if (e is LoginException) { throw e From 0ba7b65ee8768a57b0c277ff94d296a08fc76e59 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Mon, 15 Apr 2019 10:24:40 +0100 Subject: [PATCH 19/75] CORDA-2456 - Documentation around explicit upgrades (#4783) (#5018) * Documentation around explicit upgrades Small changes Fix merge * Address review comments (cherry picked from commit 09ba5c2652b6ab5f57f9f639d2128d372de9659e) --- docs/source/upgrading-cordapps.rst | 58 ++++++++++++++++-------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/docs/source/upgrading-cordapps.rst b/docs/source/upgrading-cordapps.rst index 6859dffb4c..6b28ec1fd9 100644 --- a/docs/source/upgrading-cordapps.rst +++ b/docs/source/upgrading-cordapps.rst @@ -14,6 +14,8 @@ Release new CorDapp versions CorDapp versioning ------------------ +.. UPDATE - This is no longer accurate! Needs to talk about the different types of artifacts ( kernel, workflows) each versioned independently + The Corda platform does not mandate a version number on a per-CorDapp basis. Different elements of a CorDapp are allowed to evolve separately. Sometimes, however, a change to one element will require changes to other elements. For example, changing a shared data structure may require flow changes that are not backwards-compatible. @@ -279,25 +281,25 @@ Performing explicit contract and state upgrades ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In an explicit upgrade, contracts and states can be changed in arbitrary ways, if and only if all of the state's -participants agree to the proposed upgrade. The following combinations of upgrades are possible: +participants agree to the proposed upgrade. To ensure the continuity of the chain the upgraded contract needs to declare the contract and +constraint of the states it's allowed to replace. -* A contract is upgraded while the state definition remains the same -* A state is upgraded while the contract stays the same -* The state and the contract are updated simultaneously +.. warning:: In Corda 4 we've introduced the Signature Constraint (see :doc:`api-contract-constraints`). States created or migrated to + the Signature Constraint can't be explicitly upgraded using the Contract upgrade transaction. This feature might be added in a future version. + Given the nature of the Signature constraint there should be little need to create a brand new contract to fix issues in the old contract. 1. Preserve the existing state and contract definitions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Currently, all nodes must **permanently** keep **all** old state and contract definitions on their node's classpath +Currently, all nodes must **permanently** keep **all** old state and contract definitions on their node's classpath if the explicit upgrade +process was used on them. -.. note:: Once the contract-code-as-an-attachment feature has been implemented, nodes will only be required to keep the - old state and contract definitions on their node's classpath for the duration of the upgrade +.. note:: This requirement will go away in a future version of Corda. In Corda 4, the contract-code-as-attachment feature was implemented + only for "normal" transactions. ``Contract Upgrade`` and ``Notary Change`` transactions will still be executed within the node classpath. -Changing a state or contract's package constitutes a definition change. If you want to move a state or contract -definition to a new package, you must also preserve the definition in the old package 2. Write the new state and contract definitions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Update the contract and/or state definitions. There are no restrictions on how states are updated. However, +Update the contract and state definitions. There are no restrictions on how states are updated. However, upgraded contracts must implement the ``UpgradedContract`` interface. This interface is defined as: .. sourcecode:: kotlin @@ -307,11 +309,20 @@ upgraded contracts must implement the ``UpgradedContract`` interface. This inter fun upgrade(state: OldState): NewState } -The ``upgrade`` method describes how the old state type is upgraded to the new state type. When the state isn't being -upgraded, the same state type can be used for both the old and new state type parameters. +The ``upgrade`` method describes how the old state type is upgraded to the new state type. By default this new contract will only be able to upgrade legacy states which are constrained by the zone whitelist (see :doc:`api-contract-constraints`). -If hash or other constraint types are used, the new contract should implement ``UpgradedContractWithLegacyConstraint`` + + +.. note:: The requirement for a ``legacyContractConstraint`` arises from the fact that when a transaction chain is verified and a ``Contract Upgrade`` is + encountered on the back chain, the verifier wants to know that a legitimate state was transformed into the new contract. The ``legacyContractConstraint`` is + the mechanism by which this is enforced. Using it, the new contract is able to narrow down what constraint the states it is upgrading should have. + If a malicious party would create a fake ``com.megacorp.MegaToken`` state, he would not be able to use the usual ``MegaToken`` code as his + fake token will not validate because the constraints will not match. The ``com.megacorp.SuperMegaToken`` would know that it is a fake state and thus refuse to upgrade it. + It is safe to omit the ``legacyContractConstraint`` for the zone whitelist constraint, because the chain of trust is ensured by the Zone operator + who would have whitelisted both contracts and checked them. + +If the hash constraint is used, the new contract should implement ``UpgradedContractWithLegacyConstraint`` instead, and specify the constraint explicitly: .. sourcecode:: kotlin @@ -342,8 +353,8 @@ Have each node operator stop their node. If you are also changing flow definitio :ref:`node drain ` first to avoid the definition of states or contracts changing whilst a flow is in progress. -6. Re-run the network bootstrapper -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +6. Re-run the network bootstrapper (only if you want to whitelist the new contract) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you're using the network bootstrapper instead of a network map server and have defined any new contracts, you need to re-run the network bootstrapper to whitelist the new contracts. See :doc:`network-bootstrapper`. @@ -378,29 +389,22 @@ contract upgrade. One the flow ends successfully, all the participants of the old state object should have the upgraded state object which references the new contract code. +10. Migrate the new upgraded state to the Signature Constraint from the zone constraint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Follow the guide in :doc:`api-contract-constraints`. + Points to note ~~~~~~~~~~~~~~ Capabilities of the contract upgrade flows ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* Despite its name, the ``ContractUpgradeFlow`` also handles the update of state object definitions +* Despite its name, the ``ContractUpgradeFlow`` handles the update of both state object definitions and contract logic * The state can completely change as part of an upgrade! For example, it is possible to transmute a ``Cat`` state into a ``Dog`` state, provided that all participants in the ``Cat`` state agree to the change -* Equally, the state doesn't have to change at all * If a node has not yet run the contract upgrade authorisation flow, they will not be able to upgrade the contract and/or state objects * State schema changes are handled separately -Writing new states and contracts -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -* If a property is removed from a state, any references to it must be removed from the contract code. Otherwise, you - will not be able to compile your contract code. It is generally not advisable to remove properties from states. Mark - them as deprecated instead -* When adding properties to a state, consider how the new properties will affect transaction validation involving this - state. If the contract is not updated to add constraints over the new properties, they will be able to take on any - value -* Updated state objects can use the old contract code as long as there is no requirement to update it - Logistics ^^^^^^^^^ * All nodes need to run the contract upgrade authorisation flow to upgrade the contract and/or state objects From ee884a92de257935d59d44aacefd170cd27b0445 Mon Sep 17 00:00:00 2001 From: JamesHR3 <45565019+JamesHR3@users.noreply.github.com> Date: Mon, 15 Apr 2019 10:25:18 +0100 Subject: [PATCH 20/75] CORDA-2522 - Improve error reporting around failed flows (#5016) * [CORDA-2522] Improve error reporting around failed flows (#5000) * Improve error reporting around failed flows * Fix an index to start from 1 when printed * Address first set of review comments (cherry picked from commit 24699cd7f499010f07d518381f1ea31b881311b3) * [CORDA-2522] Follow up changes to error reporting around failed flows (#5006) * Follow up changes to error reporting around failed flows * Have FinalityDoctor report stack trace * Revert changes to the DumpHistoryOnErrorInterceptor (cherry picked from commit 2da597a5b7744e62888d0c1594814454c2d6ef70) --- .../contracts/TransactionVerificationException.kt | 2 +- .../serialization/internal/AttachmentsClassLoader.kt | 6 +++++- .../services/statemachine/FlowStateMachineImpl.kt | 2 +- .../node/services/statemachine/StaffedFlowHospital.kt | 11 ++++++----- .../interceptors/DumpHistoryOnErrorInterceptor.kt | 4 +++- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt index ef56349e7b..c00696018d 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerificationException.kt @@ -292,7 +292,7 @@ abstract class TransactionVerificationException(val txId: SecureHash, message: S class UntrustedAttachmentsException(val txId: SecureHash, val ids: List) : CordaException("Attempting to load untrusted transaction attachments: $ids. " + "At this time these are not loadable because the DJVM sandbox has not yet been integrated. " + - "You will need to install that app version yourself, to whitelist it for use. " + + "You will need to manually install the CorDapp to whitelist it for use. " + "Please follow the operational steps outlined in https://docs.corda.net/cordapp-build-systems.html#cordapp-contract-attachments to learn more and continue.") /* diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt index 93a61c0f2f..28f472ec71 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt @@ -125,8 +125,12 @@ class AttachmentsClassLoader(attachments: List, } .map(Attachment::id) - if (untrusted.isNotEmpty()) + if (untrusted.isNotEmpty()) { + log.warn("Cannot verify transaction $sampleTxId as the following attachment IDs are untrusted: $untrusted." + + "You will need to manually install the CorDapp to whitelist it for use. " + + "Please follow the operational steps outlined in https://docs.corda.net/cordapp-build-systems.html#cordapp-contract-attachments to learn more and continue.") throw TransactionVerificationException.UntrustedAttachmentsException(sampleTxId, untrusted) + } // Enforce the no-overlap and package ownership rules. checkAttachments(attachments) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index a7dc06a44e..9353f8fb7f 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -243,7 +243,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, if(t.isUnrecoverable()) { errorAndTerminate("Caught unrecoverable error from flow. Forcibly terminating the JVM, this might leave resources open, and most likely will.", t) } - logger.info("Flow raised an error... sending it to flow hospital", t) + logger.info("Flow raised an error: ${t.message}. Sending it to flow hospital to be triaged.") Try.Failure(t) } val softLocksId = if (hasSoftLockedStates) logic.runId.uuid else null diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt index 48673989d8..95f3685426 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StaffedFlowHospital.kt @@ -107,17 +107,17 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, private val val (outcome, event, backOffForChronicCondition) = when (report.diagnosis) { Diagnosis.DISCHARGE -> { val backOff = calculateBackOffForChronicCondition(report, medicalHistory, currentState) - log.info("Flow ${flowFiber.id} error discharged from hospital (delay ${backOff.seconds}s) by ${report.by}") + log.info("Flow error discharged from hospital (delay ${backOff.seconds}s) by ${report.by} (error was ${report.error.message})") Triple(Outcome.DISCHARGE, Event.RetryFlowFromSafePoint, backOff) } Diagnosis.OVERNIGHT_OBSERVATION -> { - log.info("Flow ${flowFiber.id} error kept for overnight observation by ${report.by}") + log.info("Flow error kept for overnight observation by ${report.by} (error was ${report.error.message})") // We don't schedule a next event for the flow - it will automatically retry from its checkpoint on node restart Triple(Outcome.OVERNIGHT_OBSERVATION, null, 0.seconds) } Diagnosis.NOT_MY_SPECIALTY -> { // None of the staff care for these errors so we let them propagate - log.info("Flow ${flowFiber.id} error allowed to propagate") + log.info("Flow error allowed to propagate", report.error) Triple(Outcome.UNTREATABLE, Event.StartErrorPropagation, 0.seconds) } } @@ -160,7 +160,8 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, private val return errors .asSequence() .mapIndexed { index, error -> - log.info("Flow ${flowFiber.id} has error [$index]", error) + // Rely on the logging context to print details of the flow ID. + log.info("Error ${index + 1} of ${errors.size}:", error) val diagnoses: Map> = staff.groupBy { it.consult(flowFiber, currentState, error, medicalHistory) } // We're only interested in the highest priority diagnosis for the error val (diagnosis, by) = diagnoses.entries.minBy { it.key }!! @@ -306,7 +307,7 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, private val override fun consult(flowFiber: FlowFiber, currentState: StateMachineState, newError: Throwable, history: FlowMedicalHistory): Diagnosis { return if (currentState.flowLogic is FinalityHandler || isFromReceiveFinalityFlow(newError)) { log.warn("Flow ${flowFiber.id} failed to be finalised. Manual intervention may be required before retrying " + - "the flow by re-starting the node. State machine state: $currentState") + "the flow by re-starting the node. State machine state: $currentState", newError) Diagnosis.OVERNIGHT_OBSERVATION } else { Diagnosis.NOT_MY_SPECIALTY diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/interceptors/DumpHistoryOnErrorInterceptor.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/interceptors/DumpHistoryOnErrorInterceptor.kt index c5ef2bbca8..ecef21157b 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/interceptors/DumpHistoryOnErrorInterceptor.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/interceptors/DumpHistoryOnErrorInterceptor.kt @@ -3,6 +3,7 @@ package net.corda.node.services.statemachine.interceptors import co.paralleluniverse.fibers.Suspendable import net.corda.core.flows.StateMachineRunId import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.debug import net.corda.node.services.statemachine.ActionExecutor import net.corda.node.services.statemachine.ErrorState import net.corda.node.services.statemachine.Event @@ -39,7 +40,8 @@ class DumpHistoryOnErrorInterceptor(val delegate: TransitionExecutor) : Transiti (record ?: ArrayList()).apply { add(transitionRecord) } } - // Just if we decide to propagate, and not if just on the way to the hospital. + // Just if we decide to propagate, and not if just on the way to the hospital. Only log at debug level here - the flow transition + // information is often unhelpful in the logs, and the actual cause of the problem will be logged elsewhere. if (nextState.checkpoint.errorState is ErrorState.Errored && nextState.checkpoint.errorState.propagating) { log.warn("Flow ${fiber.id} errored, dumping all transitions:\n${record!!.joinToString("\n")}") for (error in nextState.checkpoint.errorState.errors) { From 21adcffd315ce10679c8b5b74a6815e86aa487bc Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Mon, 15 Apr 2019 10:25:56 +0100 Subject: [PATCH 21/75] CORDA-2743 - utilities and test to show rpc operations that support disconnects (#5009) (#5015) (cherry picked from commit 6771386b4befcb9142a30ba7c037ff1602477e65) --- .../rpc/internal/ReconnectingCordaRPCOps.kt | 402 ++++++++++++++++++ docs/source/clientrpc.rst | 71 ++-- .../node/services/rpc/RandomFailingProxy.kt | 101 +++++ .../node/services/rpc/RpcReconnectTests.kt | 285 +++++++++++++ .../testing/driver/internal/DriverInternal.kt | 2 +- 5 files changed, 835 insertions(+), 26 deletions(-) create mode 100644 client/rpc/src/main/kotlin/net/corda/client/rpc/internal/ReconnectingCordaRPCOps.kt create mode 100644 node/src/integration-test/kotlin/net/corda/node/services/rpc/RandomFailingProxy.kt create mode 100644 node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcReconnectTests.kt diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/ReconnectingCordaRPCOps.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/ReconnectingCordaRPCOps.kt new file mode 100644 index 0000000000..563b9130d4 --- /dev/null +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/ReconnectingCordaRPCOps.kt @@ -0,0 +1,402 @@ +package net.corda.client.rpc.internal + +import net.corda.client.rpc.* +import net.corda.core.flows.StateMachineRunId +import net.corda.core.internal.div +import net.corda.core.internal.times +import net.corda.core.internal.uncheckedCast +import net.corda.core.messaging.ClientRpcSslOptions +import net.corda.core.messaging.CordaRPCOps +import net.corda.core.messaging.DataFeed +import net.corda.core.messaging.FlowHandle +import net.corda.core.utilities.* +import net.corda.nodeapi.exceptions.RejectedCommandException +import org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException +import org.apache.activemq.artemis.api.core.ActiveMQSecurityException +import org.apache.activemq.artemis.api.core.ActiveMQUnBlockedException +import rx.Observable +import java.lang.reflect.InvocationHandler +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.lang.reflect.Proxy +import java.time.Duration +import java.util.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.TimeUnit + +/** + * Wrapper over [CordaRPCOps] that handles exceptions when the node or the connection to the node fail. + * + * All operations are retried on failure, except flow start operations that die before receiving a valid [FlowHandle], in which case a [CouldNotStartFlowException] is thrown. + * + * When calling methods that return a [DataFeed] like [CordaRPCOps.vaultTrackBy], the returned [DataFeed.updates] object will no longer + * be a usable [rx.Observable] but an instance of [ReconnectingObservable]. + * The caller has to explicitly cast to [ReconnectingObservable] and call [ReconnectingObservable.subscribe]. If used as an [rx.Observable] it will just fail. + * The returned [DataFeed.snapshot] is the snapshot as it was when the feed was first retrieved. + * + * Note: There is no guarantee that observations will not be lost. + * + * *This class is not a stable API. Any project that wants to use it, must copy and paste it.* + */ +class ReconnectingCordaRPCOps private constructor( + private val reconnectingRPCConnection: ReconnectingRPCConnection, + private val observersPool: ExecutorService, + private val userPool: Boolean +) : AutoCloseable, CordaRPCOps by proxy(reconnectingRPCConnection, observersPool) { + + // Constructors that mirror CordaRPCClient. + constructor( + nodeHostAndPort: NetworkHostAndPort, + username: String, + password: String, + sslConfiguration: ClientRpcSslOptions? = null, + classLoader: ClassLoader? = null, + observersPool: ExecutorService? = null + ) : this( + ReconnectingRPCConnection(listOf(nodeHostAndPort), username, password, sslConfiguration, classLoader), + observersPool ?: Executors.newCachedThreadPool(), + observersPool != null) + + constructor( + nodeHostAndPorts: List, + username: String, + password: String, + sslConfiguration: ClientRpcSslOptions? = null, + classLoader: ClassLoader? = null, + observersPool: ExecutorService? = null + ) : this( + ReconnectingRPCConnection(nodeHostAndPorts, username, password, sslConfiguration, classLoader), + observersPool ?: Executors.newCachedThreadPool(), + observersPool != null) + + private companion object { + private val log = contextLogger() + private fun proxy(reconnectingRPCConnection: ReconnectingRPCConnection, observersPool: ExecutorService): CordaRPCOps { + return Proxy.newProxyInstance( + this::class.java.classLoader, + arrayOf(CordaRPCOps::class.java), + ErrorInterceptingHandler(reconnectingRPCConnection, observersPool)) as CordaRPCOps + } + } + + private val retryFlowsPool = Executors.newScheduledThreadPool(1) + + /** + * This function runs a flow and retries until it completes successfully. + * + * [runFlow] is a function that starts a flow. + * [hasFlowStarted] is a function that checks if the flow has actually completed by checking some side-effect, for example the vault. + * [onFlowConfirmed] Callback when the flow is confirmed. + * [timeout] Indicative timeout to wait until the flow would create the side-effect. Should be increased if the flow is slow. Note that + * this timeout is calculated after the rpc client has reconnected to the node. + * + * Note that this method does not guarantee 100% that the flow will not be started twice. + */ + fun runFlowWithLogicalRetry(runFlow: (CordaRPCOps) -> StateMachineRunId, hasFlowStarted: (CordaRPCOps) -> Boolean, onFlowConfirmed: () -> Unit = {}, timeout: Duration = 4.seconds) { + try { + runFlow(this) + onFlowConfirmed() + } catch (e: CouldNotStartFlowException) { + log.error("Couldn't start flow: ${e.message}") + retryFlowsPool.schedule( + { + if (!hasFlowStarted(this)) { + runFlowWithLogicalRetry(runFlow, hasFlowStarted, onFlowConfirmed, timeout) + } else { + onFlowConfirmed() + } + }, + timeout.seconds, TimeUnit.SECONDS + ) + } + } + + /** + * This function is similar to [runFlowWithLogicalRetry] but is blocking and it returns the result of the flow. + * + * [runFlow] - starts a flow and returns the [FlowHandle]. + * [hasFlowCompleted] - Runs a vault query and is able to recreate the result of the flow. + */ + fun runFlowAndReturnResultWithLogicalRetry(runFlow: (CordaRPCOps) -> FlowHandle, hasFlowCompleted: (CordaRPCOps) -> T?, timeout: Duration = 4.seconds): T { + return try { + runFlow(this).returnValue.get() + } catch (e: CouldNotStartFlowException) { + log.error("Couldn't start flow: ${e.message}") + Thread.sleep(timeout.toMillis()) + hasFlowCompleted(this) ?: runFlowAndReturnResultWithLogicalRetry(runFlow, hasFlowCompleted, timeout) + } + } + + /** + * Helper class useful for reconnecting to a Node. + */ + internal data class ReconnectingRPCConnection( + val nodeHostAndPorts: List, + val username: String, + val password: String, + val sslConfiguration: ClientRpcSslOptions? = null, + val classLoader: ClassLoader? + ) : RPCConnection { + private var currentRPCConnection: CordaRPCConnection? = null + + init { + connect() + } + + enum class CurrentState { + UNCONNECTED, CONNECTED, CONNECTING, CLOSED, DIED + } + + private var currentState = CurrentState.UNCONNECTED + + private val current: CordaRPCConnection + @Synchronized get() = when (currentState) { + CurrentState.CONNECTED -> currentRPCConnection!! + CurrentState.UNCONNECTED, CurrentState.CLOSED -> { + connect() + currentRPCConnection!! + } + CurrentState.CONNECTING, CurrentState.DIED -> throw IllegalArgumentException("Illegal state") + } + + /** + * Called on external error. + * Will block until the connection is established again. + */ + @Synchronized + fun error(e: Throwable) { + currentState = CurrentState.DIED + //TODO - handle error cases + log.error("Reconnecting to ${this.nodeHostAndPorts} due to error: ${e.message}") + connect() + } + + private fun connect() { + currentState = CurrentState.CONNECTING + currentRPCConnection = establishConnectionWithRetry() + currentState = CurrentState.CONNECTED + } + + private tailrec fun establishConnectionWithRetry(retryInterval: Duration = 1.seconds, nrRetries: Int = 0): CordaRPCConnection { + log.info("Connecting to: $nodeHostAndPorts") + try { + return CordaRPCClient( + nodeHostAndPorts, CordaRPCClientConfiguration(connectionMaxRetryInterval = retryInterval), sslConfiguration, classLoader + ).start(username, password).also { + // Check connection is truly operational before returning it. + require(it.proxy.nodeInfo().legalIdentitiesAndCerts.isNotEmpty()) { + "Could not establish connection to ${nodeHostAndPorts}." + } + log.debug { "Connection successfully established with: ${nodeHostAndPorts}" } + } + } catch (ex: Exception) { + when (ex) { + is ActiveMQSecurityException -> { + // Happens when incorrect credentials provided. + // It can happen at startup as well when the credentials are correct. + if (nrRetries > 1) throw ex + } + is RPCException -> { + // Deliberately not logging full stack trace as it will be full of internal stacktraces. + log.debug { "Exception upon establishing connection: ${ex.message}" } + } + is ActiveMQConnectionTimedOutException -> { + // Deliberately not logging full stack trace as it will be full of internal stacktraces. + log.debug { "Exception upon establishing connection: ${ex.message}" } + } + is ActiveMQUnBlockedException -> { + // Deliberately not logging full stack trace as it will be full of internal stacktraces. + log.debug { "Exception upon establishing connection: ${ex.message}" } + } + else -> { + log.debug("Unknown exception upon establishing connection.", ex) + } + } + } + + // Could not connect this time round - pause before giving another try. + Thread.sleep(retryInterval.toMillis()) + return establishConnectionWithRetry((retryInterval * 3) / 2, nrRetries + 1) + } + + override val proxy: CordaRPCOps + get() = current.proxy + + override val serverProtocolVersion + get() = current.serverProtocolVersion + + @Synchronized + override fun notifyServerAndClose() { + currentState = CurrentState.CLOSED + currentRPCConnection?.notifyServerAndClose() + } + + @Synchronized + override fun forceClose() { + currentState = CurrentState.CLOSED + currentRPCConnection?.forceClose() + } + + @Synchronized + override fun close() { + currentState = CurrentState.CLOSED + currentRPCConnection?.close() + } + } + + internal class ReconnectingObservableImpl internal constructor( + val reconnectingRPCConnection: ReconnectingRPCConnection, + val observersPool: ExecutorService, + val initial: DataFeed<*, T>, + val createDataFeed: () -> DataFeed<*, T> + ) : Observable(null), ReconnectingObservable { + + private var initialStartWith: Iterable? = null + private fun _subscribeWithReconnect(observerHandle: ObserverHandle, onNext: (T) -> Unit, onStop: () -> Unit, onDisconnect: () -> Unit, onReconnect: () -> Unit, startWithValues: Iterable? = null) { + var subscriptionError: Throwable? + try { + val subscription = initial.updates.let { if (startWithValues != null) it.startWith(startWithValues) else it } + .subscribe(onNext, observerHandle::fail, observerHandle::stop) + subscriptionError = observerHandle.await() + subscription.unsubscribe() + } catch (e: Exception) { + log.error("Failed to register subscriber .", e) + subscriptionError = e + } + + // In case there was no exception the observer has finished gracefully. + if (subscriptionError == null) { + onStop() + return + } + + onDisconnect() + // Only continue if the subscription failed. + reconnectingRPCConnection.error(subscriptionError) + log.debug { "Recreating data feed." } + + val newObservable = createDataFeed().updates as ReconnectingObservableImpl + onReconnect() + return newObservable._subscribeWithReconnect(observerHandle, onNext, onStop, onDisconnect, onReconnect) + } + + override fun subscribe(onNext: (T) -> Unit, onStop: () -> Unit, onDisconnect: () -> Unit, onReconnect: () -> Unit): ObserverHandle { + val observerNotifier = ObserverHandle() + // TODO - change the establish connection method to be non-blocking + observersPool.execute { + _subscribeWithReconnect(observerNotifier, onNext, onStop, onDisconnect, onReconnect, initialStartWith) + } + return observerNotifier + } + + override fun startWithValues(values: Iterable): ReconnectingObservable { + initialStartWith = values + return this + } + } + + private class ErrorInterceptingHandler(val reconnectingRPCConnection: ReconnectingRPCConnection, val observersPool: ExecutorService) : InvocationHandler { + private fun Method.isStartFlow() = name.startsWith("startFlow") || name.startsWith("startTrackedFlow") + + override fun invoke(proxy: Any, method: Method, args: Array?): Any? { + val result: Any? = try { + log.debug { "Invoking RPC $method..." } + method.invoke(reconnectingRPCConnection.proxy, *(args ?: emptyArray())).also { + log.debug { "RPC $method invoked successfully." } + } + } catch (e: InvocationTargetException) { + fun retry() = if (method.isStartFlow()) { + // Don't retry flows + throw CouldNotStartFlowException(e.targetException) + } else { + this.invoke(proxy, method, args) + } + + when (e.targetException) { + is RejectedCommandException -> { + log.error("Node is being shutdown. Operation ${method.name} rejected. Retrying when node is up...", e) + reconnectingRPCConnection.error(e) + this.invoke(proxy, method, args) + } + is ConnectionFailureException -> { + log.error("Failed to perform operation ${method.name}. Connection dropped. Retrying....", e) + reconnectingRPCConnection.error(e) + retry() + } + is RPCException -> { + log.error("Failed to perform operation ${method.name}. RPCException. Retrying....", e) + reconnectingRPCConnection.error(e) + Thread.sleep(1000) // TODO - explain why this sleep is necessary + retry() + } + else -> { + log.error("Failed to perform operation ${method.name}. Unknown error. Retrying....", e) + reconnectingRPCConnection.error(e) + retry() + } + } + } + + return when (method.returnType) { + DataFeed::class.java -> { + // Intercept the data feed methods and returned a ReconnectingObservable instance + val initialFeed: DataFeed = uncheckedCast(result) + val observable = ReconnectingObservableImpl(reconnectingRPCConnection, observersPool, initialFeed) { + // This handles reconnecting and creates new feeds. + uncheckedCast(this.invoke(reconnectingRPCConnection.proxy, method, args)) + } + initialFeed.copy(updates = observable) + } + // TODO - add handlers for Observable return types. + else -> result + } + } + } + + override fun close() { + if (!userPool) observersPool.shutdown() + retryFlowsPool.shutdown() + reconnectingRPCConnection.forceClose() + } +} + +/** + * Returned as the `updates` field when calling methods that return a [DataFeed] on the [ReconnectingCordaRPCOps]. + * + * TODO - provide a logical function to know how to retrieve missing events that happened during disconnects. + */ +interface ReconnectingObservable { + fun subscribe(onNext: (T) -> Unit): ObserverHandle = subscribe(onNext, {}, {}, {}) + fun subscribe(onNext: (T) -> Unit, onStop: () -> Unit, onDisconnect: () -> Unit, onReconnect: () -> Unit): ObserverHandle + fun startWithValues(values: Iterable): ReconnectingObservable +} + +/** + * Utility to externally control a subscribed observer. + */ +class ObserverHandle { + private val terminated = LinkedBlockingQueue>(1) + + fun stop() = terminated.put(Optional.empty()) + internal fun fail(e: Throwable) = terminated.put(Optional.of(e)) + + /** + * Returns null if the observation ended successfully. + */ + internal fun await(duration: Duration = 60.minutes): Throwable? = terminated.poll(duration.seconds, TimeUnit.SECONDS).orElse(null) +} + +/** + * Thrown when a flow start command died before receiving a [net.corda.core.messaging.FlowHandle]. + * On catching this exception, the typical behaviour is to run a "logical retry", meaning only retry the flow if the expected outcome did not occur. + */ +class CouldNotStartFlowException(cause: Throwable? = null) : RPCException("Could not start flow as connection failed", cause) + +/** + * Mainly for Kotlin users. + */ +fun Observable.asReconnecting(): ReconnectingObservable = uncheckedCast(this) + +fun Observable.asReconnectingWithInitialValues(values: Iterable): ReconnectingObservable = asReconnecting().startWithValues(values) diff --git a/docs/source/clientrpc.rst b/docs/source/clientrpc.rst index 670768c50a..144fa54ebc 100644 --- a/docs/source/clientrpc.rst +++ b/docs/source/clientrpc.rst @@ -352,39 +352,60 @@ When not in ``devMode``, the server will mask exceptions not meant for clients a This does not expose internal information to clients, strengthening privacy and security. CorDapps can have exceptions implement ``ClientRelevantError`` to allow them to reach RPC clients. -Connection management ---------------------- -It is possible to not be able to connect to the server on the first attempt. In that case, the ``CordaRPCClient.start()`` -method will throw an exception. The following code snippet is an example of how to write a simple retry mechanism for -such situations: +Reconnecting RPC clients +------------------------ -.. literalinclude:: ../../samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt +In the current version of Corda the RPC connection and all the observervables that are created by a client will just throw exceptions and die +when the node or TCP connection become unavailable. + +It is the client's responsibility to handle these errors and reconnect once the node is running again. Running RPC commands against a stopped +node will just throw exceptions. Previously created Observables will not emit any events after the node restarts. The client must explicitly re-run the command and +re-subscribe to receive more events. + +RPCs which have a side effect, such as starting flows, may have executed on the node even if the return value is not received by the client. +The only way to confirm is to perform a business-level query and retry accordingly. The sample `runFlowWithLogicalRetry` helps with this. + +In case users require such a functionality to write a resilient RPC client we have a sample that showcases how this can be implemented and also +a thorough test that demonstrates it works as expected. + +The code that performs the reconnecting logic is: `ReconnectingCordaRPCOps.kt `_. + +.. note:: This sample code is not exposed as an official Corda API, and must be included directly in the client codebase and adjusted. + +The usage is showcased in the: `RpcReconnectTests.kt `_. +In case resiliency is a requirement, then it is recommended that users will write a similar test. + +How to initialize the `ReconnectingCordaRPCOps`: + +.. literalinclude:: ../../node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcReconnectTests.kt :language: kotlin - :start-after: DOCSTART rpcClientConnectionWithRetry - :end-before: DOCEND rpcClientConnectionWithRetry + :start-after: DOCSTART rpcReconnectingRPC + :end-before: DOCEND rpcReconnectingRPC -.. warning:: The list of ``NetworkHostAndPort`` passed to this function should represent one or more addresses reflecting the number of - instances of a node configured to service the client RPC request. See ``haAddressPool`` in `CordaRPCClient`_ for further information on - using an RPC Client for load balancing and failover. -After a successful connection, it is possible for the server to become unavailable. In this case, all RPC calls will throw -an exception and created observables will no longer receive observations. Below is an example of how to reconnect and -back-fill any data that might have been missed while the connection was down. This is done by using the ``onError`` handler -on the ``Observable`` returned by ``CordaRPCOps``. +How to track the vault : -.. literalinclude:: ../../samples/bank-of-corda-demo/src/main/kotlin/net/corda/bank/api/BankOfCordaClientApi.kt +.. literalinclude:: ../../node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcReconnectTests.kt :language: kotlin - :start-after: DOCSTART rpcClientConnectionRecovery - :end-before: DOCEND rpcClientConnectionRecovery + :start-after: DOCSTART rpcReconnectingRPCVaultTracking + :end-before: DOCEND rpcReconnectingRPCVaultTracking -In this code snippet it is possible to see that the function ``performRpcReconnect`` creates an RPC connection and implements -the error handler upon subscription to an ``Observable``. The call to this ``onError`` handler will be triggered upon failover, at which -point the client will terminate its existing subscription, close its RPC connection and recursively call ``performRpcReconnect``, -which will re-subscribe once the RPC connection is re-established. -Within the body of the ``subscribe`` function itself, the client code receives instances of ``StateMachineInfo``. Upon re-connecting, this code receives -*all* the instances of ``StateMachineInfo``, some of which may already been delivered to the client code prior to previous disconnect. -It is the responsibility of the client code to handle potential duplicated instances of ``StateMachineInfo`` as appropriate. +How to start a flow with a logical retry function that checks for the side effects of the flow: + +.. literalinclude:: ../../node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcReconnectTests.kt + :language: kotlin + :start-after: DOCSTART rpcReconnectingRPCFlowStarting + :end-before: DOCEND rpcReconnectingRPCFlowStarting + + +Note that, as shown by the test, during reconnecting some events might be lost. + +.. literalinclude:: ../../node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcReconnectTests.kt + :language: kotlin + :start-after: DOCSTART missingVaultEvents + :end-before: DOCEND missingVaultEvents + Wire security ------------- diff --git a/node/src/integration-test/kotlin/net/corda/node/services/rpc/RandomFailingProxy.kt b/node/src/integration-test/kotlin/net/corda/node/services/rpc/RandomFailingProxy.kt new file mode 100644 index 0000000000..4a9a276ea0 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/services/rpc/RandomFailingProxy.kt @@ -0,0 +1,101 @@ +package net.corda.node.services.rpc + +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.net.ServerSocket +import java.net.Socket +import java.net.SocketException +import java.util.* +import java.util.concurrent.Executors +import java.util.concurrent.atomic.AtomicBoolean + +/** + * Simple proxy that can be restarted and introduces random latencies. + * + * Also acts as a mock load balancer. + */ +class RandomFailingProxy(val serverPort: Int, val remotePort: Int) : AutoCloseable { + private val threadPool = Executors.newCachedThreadPool() + private val stopCopy = AtomicBoolean(false) + private var currentServerSocket: ServerSocket? = null + private val rnd = ThreadLocal.withInitial { Random() } + + fun start(): RandomFailingProxy { + stopCopy.set(false) + currentServerSocket = ServerSocket(serverPort) + threadPool.execute { + try { + currentServerSocket.use { serverSocket -> + while (!stopCopy.get() && !serverSocket!!.isClosed) { + handleConnection(serverSocket.accept()) + } + } + } catch (e: SocketException) { + // The Server socket could be closed + } + } + return this + } + + private fun handleConnection(socket: Socket) { + threadPool.execute { + socket.use { _ -> + try { + Socket("localhost", remotePort).use { target -> + // send message to node + threadPool.execute { + try { + socket.getInputStream().flakeyCopyTo(target.getOutputStream()) + } catch (e: IOException) { + // Thrown when the connection to the target server dies. + } + } + target.getInputStream().flakeyCopyTo(socket.getOutputStream()) + } + } catch (e: IOException) { + // Thrown when the connection to the target server dies. + } + } + } + } + + fun stop(): RandomFailingProxy { + stopCopy.set(true) + currentServerSocket?.close() + return this + } + + private val failOneConnection = AtomicBoolean(false) + fun failConnection() { + failOneConnection.set(true) + } + + override fun close() { + try { + stop() + threadPool.shutdownNow() + } catch (e: Exception) { + // Nothing can be done. + } + } + + private fun InputStream.flakeyCopyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long { + var bytesCopied: Long = 0 + val buffer = ByteArray(bufferSize) + var bytes = read(buffer) + while (bytes >= 0 && !stopCopy.get()) { + // Introduce intermittent slowness. + if (rnd.get().nextInt().rem(700) == 0) { + Thread.sleep(rnd.get().nextInt(2000).toLong()) + } + if (failOneConnection.compareAndSet(true, false)) { + throw IOException("Randomly dropped one connection") + } + out.write(buffer, 0, bytes) + bytesCopied += bytes + bytes = read(buffer) + } + return bytesCopied + } +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcReconnectTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcReconnectTests.kt new file mode 100644 index 0000000000..41d09e9c7d --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/services/rpc/RpcReconnectTests.kt @@ -0,0 +1,285 @@ +package net.corda.node.services.rpc + +import net.corda.client.rpc.internal.ReconnectingCordaRPCOps +import net.corda.client.rpc.internal.asReconnecting +import net.corda.core.contracts.Amount +import net.corda.core.flows.StateMachineRunId +import net.corda.core.internal.concurrent.transpose +import net.corda.core.messaging.StateMachineUpdate +import net.corda.core.node.services.Vault +import net.corda.core.node.services.vault.PageSpecification +import net.corda.core.node.services.vault.QueryCriteria +import net.corda.core.node.services.vault.builder +import net.corda.core.utilities.OpaqueBytes +import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.getOrThrow +import net.corda.finance.contracts.asset.Cash +import net.corda.finance.flows.CashIssueAndPaymentFlow +import net.corda.finance.schemas.CashSchemaV1 +import net.corda.node.services.Permissions +import net.corda.testing.core.DUMMY_BANK_A_NAME +import net.corda.testing.core.DUMMY_BANK_B_NAME +import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.OutOfProcess +import net.corda.testing.driver.driver +import net.corda.testing.driver.internal.OutOfProcessImpl +import net.corda.testing.node.User +import net.corda.testing.node.internal.FINANCE_CORDAPPS +import org.junit.Test +import java.util.* +import java.util.concurrent.CountDownLatch +import java.util.concurrent.atomic.AtomicInteger +import kotlin.concurrent.thread +import kotlin.math.absoluteValue +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +/** + * This is a slow test! + */ +class RpcReconnectTests { + + companion object { + private val log = contextLogger() + } + + /** + * This test showcases and stress tests the demo [ReconnectingCordaRPCOps]. + * + * Note that during node failure events can be lost and starting flows can become unreliable. + * The only available way to retry failed flows is to attempt a "logical retry" which is also showcased. + * + * This test runs flows in a loop and in the background kills the node or restarts it. + * Also the RPC connection is made through a proxy that introduces random latencies and is also periodically killed. + */ + @Test + fun `test that the RPC client is able to reconnect and proceed after node failure, restart, or connection reset`() { + val nrOfFlowsToRun = 450 // Takes around 5 minutes. + val nodeRunningTime = { Random().nextInt(12000) + 8000 } + + val demoUser = User("demo", "demo", setOf(Permissions.all())) + + val nodePort = 20006 + val proxyPort = 20007 + val tcpProxy = RandomFailingProxy(serverPort = proxyPort, remotePort = nodePort).start() + + // When this reaches 0 - the test will end. + val flowsCountdownLatch = CountDownLatch(nrOfFlowsToRun) + + // These are the expected progress steps for the CashIssueAndPayFlow. + val expectedProgress = listOf( + "Starting", + "Issuing cash", + "Generating transaction", + "Signing transaction", + "Finalising transaction", + "Broadcasting transaction to participants", + "Paying recipient", + "Generating anonymous identities", + "Generating transaction", + "Signing transaction", + "Finalising transaction", + "Requesting signature by notary service", + "Requesting signature by Notary service", + "Validating response from Notary service", + "Broadcasting transaction to participants", + "Done" + ) + + driver(DriverParameters(cordappsForAllNodes = FINANCE_CORDAPPS, startNodesInProcess = false, inMemoryDB = false)) { + fun startBankA() = startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("rpcSettings.address" to "localhost:$nodePort")) + + var (bankA, bankB) = listOf( + startBankA(), + startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)) + ).transpose().getOrThrow() + + val notary = defaultNotaryIdentity + val baseAmount = Amount.parseCurrency("0 USD") + val issuerRef = OpaqueBytes.of(0x01) + + // Create a reconnecting rpc client through the TCP proxy. + val bankAAddress = bankA.rpcAddress.copy(port = proxyPort) + // DOCSTART rpcReconnectingRPC + val bankAReconnectingRpc = ReconnectingCordaRPCOps(bankAAddress, demoUser.username, demoUser.password) + // DOCEND rpcReconnectingRPC + + // Observe the vault and collect the observations. + val vaultEvents = Collections.synchronizedList(mutableListOf>()) + // DOCSTART rpcReconnectingRPCVaultTracking + val vaultFeed = bankAReconnectingRpc.vaultTrackByWithPagingSpec( + Cash.State::class.java, + QueryCriteria.VaultQueryCriteria(), + PageSpecification(1, 1)) + val vaultObserverHandle = vaultFeed.updates.asReconnecting().subscribe { update: Vault.Update -> + log.info("vault update produced ${update.produced.map { it.state.data.amount }} consumed ${update.consumed.map { it.ref }}") + vaultEvents.add(update) + } + // DOCEND rpcReconnectingRPCVaultTracking + + // Observe the stateMachine and collect the observations. + val stateMachineEvents = Collections.synchronizedList(mutableListOf()) + val stateMachineObserverHandle = bankAReconnectingRpc.stateMachinesFeed().updates.asReconnecting().subscribe { update -> + log.info(update.toString()) + stateMachineEvents.add(update) + } + + // While the flows are running, randomly apply a different failure scenario. + val nrRestarts = AtomicInteger() + thread(name = "Node killer") { + while (true) { + if (flowsCountdownLatch.count == 0L) break + + // Let the node run for a random time interval. + nodeRunningTime().also { ms -> + log.info("Running node for ${ms / 1000} s.") + Thread.sleep(ms.toLong()) + } + + if (flowsCountdownLatch.count == 0L) break + + when (Random().nextInt().rem(6).absoluteValue) { + 0 -> { + log.info("Forcefully killing node and proxy.") + (bankA as OutOfProcessImpl).onStopCallback() + (bankA as OutOfProcess).process.destroyForcibly() + tcpProxy.stop() + bankA = startBankA().get() + tcpProxy.start() + } + 1 -> { + log.info("Forcefully killing node.") + (bankA as OutOfProcessImpl).onStopCallback() + (bankA as OutOfProcess).process.destroyForcibly() + bankA = startBankA().get() + } + 2 -> { + log.info("Shutting down node.") + bankA.stop() + tcpProxy.stop() + bankA = startBankA().get() + tcpProxy.start() + } + 3, 4 -> { + log.info("Killing proxy.") + tcpProxy.stop() + Thread.sleep(Random().nextInt(5000).toLong()) + tcpProxy.start() + } + 5 -> { + log.info("Dropping connection.") + tcpProxy.failConnection() + } + } + nrRestarts.incrementAndGet() + } + } + + // Start nrOfFlowsToRun and provide a logical retry function that checks the vault. + val flowProgressEvents = mutableMapOf>() + for (amount in (1..nrOfFlowsToRun)) { + // DOCSTART rpcReconnectingRPCFlowStarting + bankAReconnectingRpc.runFlowWithLogicalRetry( + runFlow = { rpc -> + log.info("Starting CashIssueAndPaymentFlow for $amount") + val flowHandle = rpc.startTrackedFlowDynamic( + CashIssueAndPaymentFlow::class.java, + baseAmount.plus(Amount.parseCurrency("$amount USD")), + issuerRef, + bankB.nodeInfo.legalIdentities.first(), + false, + notary + ) + val flowId = flowHandle.id + log.info("Started flow $amount with flowId: $flowId") + flowProgressEvents.addEvent(flowId, null) + + // No reconnecting possible. + flowHandle.progress.subscribe( + { prog -> + flowProgressEvents.addEvent(flowId, prog) + log.info("Progress $flowId : $prog") + }, + { error -> + log.error("Error thrown in the flow progress observer", error) + }) + flowHandle.id + }, + hasFlowStarted = { rpc -> + // Query for a state that is the result of this flow. + val criteria = QueryCriteria.VaultCustomQueryCriteria(builder { CashSchemaV1.PersistentCashState::pennies.equal(amount.toLong() * 100) }, status = Vault.StateStatus.ALL) + val results = rpc.vaultQueryByCriteria(criteria, Cash.State::class.java) + log.info("$amount - Found states ${results.states}") + // The flow has completed if a state is found + results.states.isNotEmpty() + }, + onFlowConfirmed = { + flowsCountdownLatch.countDown() + log.info("Flow started for $amount. Remaining flows: ${flowsCountdownLatch.count}") + } + ) + // DOCEND rpcReconnectingRPCFlowStarting + + Thread.sleep(Random().nextInt(250).toLong()) + } + + log.info("Started all flows") + + // Wait until all flows have been started. + flowsCountdownLatch.await() + + log.info("Confirmed all flows.") + + // Wait for all events to come in and flows to finish. + Thread.sleep(4000) + + val nrFailures = nrRestarts.get() + log.info("Checking results after $nrFailures restarts.") + + // The progress status for each flow can only miss the last events, because the node might have been killed. + val missingProgressEvents = flowProgressEvents.filterValues { expectedProgress.subList(0, it.size) != it } + assertTrue(missingProgressEvents.isEmpty(), "The flow progress tracker is missing events: $missingProgressEvents") + + // DOCSTART missingVaultEvents + // Check that enough vault events were received. + // This check is fuzzy because events can go missing during node restarts. + // Ideally there should be nrOfFlowsToRun events receive but some might get lost for each restart. + assertTrue(vaultEvents!!.size + nrFailures * 2 >= nrOfFlowsToRun, "Not all vault events were received") + // DOCEND missingVaultEvents + + // Query the vault and check that states were created for all flows. + val allCashStates = bankAReconnectingRpc + .vaultQueryByWithPagingSpec(Cash.State::class.java, QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.CONSUMED), PageSpecification(1, 10000)) + .states + + val allCash = allCashStates.map { it.state.data.amount.quantity }.toSet() + val missingCash = (1..nrOfFlowsToRun).filterNot { allCash.contains(it.toLong() * 100) } + log.info("MISSING: $missingCash") + + assertEquals(nrOfFlowsToRun, allCashStates.size, "Not all flows were executed successfully") + + // Check that no flow was triggered twice. + val duplicates = allCashStates.groupBy { it.state.data.amount }.filterValues { it.size > 1 } + assertTrue(duplicates.isEmpty(), "${duplicates.size} flows were retried illegally.") + + log.info("SM EVENTS: ${stateMachineEvents!!.size}") + // State machine events are very likely to get lost more often because they seem to be sent with a delay. + assertTrue(stateMachineEvents.count { it is StateMachineUpdate.Added } > nrOfFlowsToRun / 2, "Too many Added state machine events lost.") + assertTrue(stateMachineEvents.count { it is StateMachineUpdate.Removed } > nrOfFlowsToRun / 2, "Too many Removed state machine events lost.") + + // Stop the observers. + vaultObserverHandle.stop() + stateMachineObserverHandle.stop() + + bankAReconnectingRpc.close() + } + + tcpProxy.close() + } + + @Synchronized + fun MutableMap>.addEvent(id: StateMachineRunId, progress: String?): Boolean { + return getOrPut(id) { mutableListOf() }.let { if (progress != null) it.add(progress) else false } + } +} + diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/DriverInternal.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/DriverInternal.kt index fb4b37f6f9..c761b96498 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/DriverInternal.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/internal/DriverInternal.kt @@ -35,7 +35,7 @@ data class OutOfProcessImpl( override val useHTTPS: Boolean, val debugPort: Int?, override val process: Process, - private val onStopCallback: () -> Unit + val onStopCallback: () -> Unit ) : OutOfProcess, NodeHandleInternal { override val rpcUsers: List = configuration.rpcUsers.map { User(it.username, it.password, it.permissions) } override fun stop() { From 4afbf78c79c0e11370c0d9eb317d705b65e49493 Mon Sep 17 00:00:00 2001 From: JamesHR3 <45565019+JamesHR3@users.noreply.github.com> Date: Mon, 15 Apr 2019 10:27:39 +0100 Subject: [PATCH 22/75] CORDA-2633 - Restructure evolution serialization errors to print reason first (#4978) (#5014) (cherry picked from commit 9963a6609ffc8b72b2e9323ab918aa0158b0f8cc) --- .../internal/amqp/EvolutionSerializerFactory.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt index c8b16a6f5d..8821860355 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializerFactory.kt @@ -22,9 +22,10 @@ interface EvolutionSerializerFactory { class EvolutionSerializationException(remoteTypeInformation: RemoteTypeInformation, reason: String) : NotSerializableException( """ - Cannot construct evolution serializer for remote type ${remoteTypeInformation.prettyPrint(false)} + Cannot construct evolution serializer for remote type ${remoteTypeInformation.typeIdentifier.name}: $reason - $reason + Full type information: + ${remoteTypeInformation.prettyPrint(false)} """.trimIndent() ) From 4e29d024e6217bd414ee09d7066592213e6f8495 Mon Sep 17 00:00:00 2001 From: Dominic Fox <40790090+r3domfox@users.noreply.github.com> Date: Tue, 16 Apr 2019 17:11:03 +0100 Subject: [PATCH 23/75] CORDA-2696 backport change from #4848 to release/4 (#5017) --- .../core/internal/cordapp/CordappResolver.kt | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappResolver.kt b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappResolver.kt index ff7fe11207..86f04c4839 100644 --- a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappResolver.kt +++ b/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappResolver.kt @@ -3,6 +3,7 @@ package net.corda.core.internal.cordapp import net.corda.core.cordapp.Cordapp import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.internal.VisibleForTesting +import net.corda.core.internal.warnOnce import net.corda.core.utilities.loggerFor import java.util.concurrent.ConcurrentHashMap @@ -10,6 +11,7 @@ import java.util.concurrent.ConcurrentHashMap * Provides a way to acquire information about the calling CorDapp. */ object CordappResolver { + private val logger = loggerFor() private val cordappClasses: ConcurrentHashMap> = ConcurrentHashMap() @@ -29,17 +31,20 @@ object CordappResolver { */ @Synchronized fun register(cordapp: Cordapp) { - cordapp.cordappClasses.forEach { - val cordapps = cordappClasses[it] - if (cordapps != null) { - // we do not register CorDapps that originate from the same file. - if (cordapps.none { it.jarHash == cordapp.jarHash }) { - logger.warn("More than one CorDapp registered for $it.") - cordappClasses[it] = cordappClasses[it]!! + cordapp - } - } else { - cordappClasses[it] = setOf(cordapp) + val contractClasses = cordapp.contractClassNames.toSet() + val existingClasses = cordappClasses.keys + val classesToRegister = cordapp.cordappClasses.toSet() + val notAlreadyRegisteredClasses = classesToRegister - existingClasses + val alreadyRegistered= HashMap(cordappClasses).apply { keys.retainAll(classesToRegister) } + + notAlreadyRegisteredClasses.forEach { cordappClasses[it] = setOf(cordapp) } + + for ((className, registeredCordapps) in alreadyRegistered) { + if (registeredCordapps.any { it.jarHash == cordapp.jarHash }) continue + if (className in contractClasses) { + logger.warnOnce("More than one CorDapp registered for contract $className.") } + cordappClasses[className] = registeredCordapps + cordapp } } @@ -81,4 +86,4 @@ object CordappResolver { internal fun clear() { cordappClasses.clear() } -} +} \ No newline at end of file From 4e10f0901666fc5e6cdda35d4fba22c7b9b22d5b Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Tue, 16 Apr 2019 17:23:19 +0100 Subject: [PATCH 24/75] DOCS - Fix tut-two-party-flow kotlin docs + make both versions easier to read (#5024) (#5032) - `DOCSTART`s were not in sync - Incorrect indentation - Documentation not very clear (cherry picked from commit 360660e4d77ab17b68617f8d9c46fab451890cbe) --- .../java/tutorial/twoparty/IOUFlowResponder.java | 4 ++-- .../kotlin/tutorial/twoparty/IOUFlowResponder.kt | 5 ++--- docs/source/tut-two-party-flow.rst | 12 ++++++------ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlowResponder.java b/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlowResponder.java index d210940891..1f7396f7d7 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlowResponder.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlowResponder.java @@ -18,10 +18,10 @@ public class IOUFlowResponder extends FlowLogic { this.otherPartySession = otherPartySession; } + // DOCSTART 1 @Suspendable @Override public Void call() throws FlowException { - // DOCSTART 01 class SignTxFlow extends SignTransactionFlow { private SignTxFlow(FlowSession otherPartySession) { super(otherPartySession); @@ -44,6 +44,6 @@ public class IOUFlowResponder extends FlowLogic { subFlow(new ReceiveFinalityFlow(otherPartySession, expectedTxId)); return null; - // DOCEND 01 } + // DOCEND 1 } diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/twoparty/IOUFlowResponder.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/twoparty/IOUFlowResponder.kt index 7a87c3b351..ceb0c73db9 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/twoparty/IOUFlowResponder.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/twoparty/IOUFlowResponder.kt @@ -7,7 +7,6 @@ import net.corda.core.flows.* import net.corda.docs.kotlin.tutorial.helloworld.IOUFlow import net.corda.docs.kotlin.tutorial.helloworld.IOUState -// DOCSTART 01 // Add these imports: import net.corda.core.contracts.requireThat import net.corda.core.transactions.SignedTransaction @@ -15,9 +14,9 @@ import net.corda.core.transactions.SignedTransaction // Define IOUFlowResponder: @InitiatedBy(IOUFlow::class) class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic() { + // DOCSTART 1 @Suspendable override fun call() { - // DOCSTART 01 val signTransactionFlow = object : SignTransactionFlow(otherPartySession) { override fun checkTransaction(stx: SignedTransaction) = requireThat { val output = stx.tx.outputs.single().data @@ -30,6 +29,6 @@ class IOUFlowResponder(val otherPartySession: FlowSession) : FlowLogic() { val expectedTxId = subFlow(signTransactionFlow).id subFlow(ReceiveFinalityFlow(otherPartySession, expectedTxId)) - // DOCEND 01 } + // DOCEND 1 } diff --git a/docs/source/tut-two-party-flow.rst b/docs/source/tut-two-party-flow.rst index 5c66b3b550..4fc0b98b9f 100644 --- a/docs/source/tut-two-party-flow.rst +++ b/docs/source/tut-two-party-flow.rst @@ -87,15 +87,15 @@ to respond, we need to update its responder flow to first receive the partially .. literalinclude:: example-code/src/main/kotlin/net/corda/docs/kotlin/tutorial/twoparty/IOUFlowResponder.kt :language: kotlin - :start-after: DOCSTART 01 - :end-before: DOCEND 01 - :dedent: 8 + :start-after: DOCSTART 1 + :end-before: DOCEND 1 + :dedent: 4 .. literalinclude:: example-code/src/main/java/net/corda/docs/java/tutorial/twoparty/IOUFlowResponder.java :language: java - :start-after: DOCSTART 01 - :end-before: DOCEND 01 - :dedent: 8 + :start-after: DOCSTART 1 + :end-before: DOCEND 1 + :dedent: 4 We could write our own flow to handle this process. However, there is also a pre-defined flow called ``SignTransactionFlow`` that can handle the process automatically. The only catch is that ``SignTransactionFlow`` is an From 95a3631628cc11d366b10782d8ecfb4d7b161caa Mon Sep 17 00:00:00 2001 From: bpaunescu Date: Wed, 17 Apr 2019 19:05:06 +0100 Subject: [PATCH 25/75] CORDA-2861 - give the message executor its own Artemis session and producer (#5036) --- .../node/services/messaging/P2PMessagingClient.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt index ecf512f9df..5ee4089d0b 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/P2PMessagingClient.kt @@ -108,6 +108,8 @@ class P2PMessagingClient(val config: NodeConfiguration, var eventsSubscription: Subscription? = null var p2pConsumer: P2PMessagingConsumer? = null var locator: ServerLocator? = null + var executorProducer: ClientProducer? = null + var executorSession: ClientSession? = null var producer: ClientProducer? = null var producerSession: ClientSession? = null var bridgeSession: ClientSession? = null @@ -170,8 +172,10 @@ class P2PMessagingClient(val config: NodeConfiguration, // size of 1MB is acknowledged. val createNewSession = { sessionFactory!!.createSession(ArtemisMessagingComponent.NODE_P2P_USER, ArtemisMessagingComponent.NODE_P2P_USER, false, true, true, false, ActiveMQClient.DEFAULT_ACK_BATCH_SIZE) } + executorSession = createNewSession() producerSession = createNewSession() bridgeSession = createNewSession() + executorSession!!.start() producerSession!!.start() bridgeSession!!.start() @@ -179,6 +183,7 @@ class P2PMessagingClient(val config: NodeConfiguration, // Create a queue, consumer and producer for handling P2P network messages. // Create a general purpose producer. producer = producerSession!!.createProducer() + executorProducer = executorSession!!.createProducer() inboxes += RemoteInboxAddress(myIdentity).queueName serviceIdentity?.let { @@ -190,8 +195,8 @@ class P2PMessagingClient(val config: NodeConfiguration, p2pConsumer = P2PMessagingConsumer(inboxes, createNewSession, isDrainingModeOn, drainingModeWasChangedEvents) messagingExecutor = MessagingExecutor( - producerSession!!, - producer!!, + executorSession!!, + executorProducer!!, versionInfo, this@P2PMessagingClient, ourSenderUUID = ourSenderUUID @@ -435,6 +440,10 @@ class P2PMessagingClient(val config: NodeConfiguration, producer = null producerSession!!.commit() + close(executorProducer) + executorProducer = null + executorSession!!.commit() + close(bridgeNotifyConsumer) knownQueues.clear() eventsSubscription?.unsubscribe() From a29f417a674f8754cc4c7c97081a75aef1301cbf Mon Sep 17 00:00:00 2001 From: Dominic Fox <40790090+r3domfox@users.noreply.github.com> Date: Thu, 18 Apr 2019 14:20:00 +0100 Subject: [PATCH 26/75] CORDA-2860 relax property type checking (#5028) (#5038) * CORDA-2860 relax property type checking * Remove redundant check * Unit test * Comment linking ticket to test * More descriptive comment * Fix test that was broken by jacocoData field being silently added to class * Revert to previous behaviour around opaques, suppress validation --- .../internal/amqp/PropertyDescriptor.kt | 26 +++++++++-------- .../model/LocalTypeInformationBuilder.kt | 6 ++-- .../internal/SetsSerializationTest.kt | 28 +++++++++++++++++-- .../internal/model/LocalTypeModelTests.kt | 8 +++--- 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertyDescriptor.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertyDescriptor.kt index e68882fd77..f3abc48ee8 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertyDescriptor.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/PropertyDescriptor.kt @@ -4,6 +4,7 @@ import com.google.common.reflect.TypeToken import net.corda.core.KeepForDJVM import net.corda.core.internal.isPublic import net.corda.core.serialization.SerializableCalculatedProperty +import net.corda.core.utilities.contextLogger import net.corda.serialization.internal.amqp.MethodClassifier.* import java.lang.reflect.Field import java.lang.reflect.Method @@ -32,21 +33,20 @@ data class PropertyDescriptor(val field: Field?, val setter: Method?, val getter */ fun validate() { getter?.apply { - val getterType = genericReturnType field?.apply { - if (!getterType.isSupertypeOf(genericReturnType)) - throw AMQPNotSerializableException( - declaringClass, - "Defined getter for parameter $name returns type $getterType " + + if (!getter.returnType.boxesOrIsAssignableFrom(field.type)) + throw AMQPNotSerializableException( + declaringClass, + "Defined getter for parameter $name returns type ${getter.returnType} " + "yet underlying type is $genericType") } } setter?.apply { - val setterType = genericParameterTypes[0]!! + val setterType = setter.parameterTypes[0]!! field?.apply { - if (!genericType.isSupertypeOf(setterType)) + if (!field.type.boxesOrIsAssignableFrom(setterType)) throw AMQPNotSerializableException( declaringClass, "Defined setter for parameter $name takes parameter of type $setterType " + @@ -54,7 +54,7 @@ data class PropertyDescriptor(val field: Field?, val setter: Method?, val getter } getter?.apply { - if (!genericReturnType.isSupertypeOf(setterType)) + if (!getter.returnType.boxesOrIsAssignableFrom(setterType)) throw AMQPNotSerializableException( declaringClass, "Defined setter for parameter $name takes parameter of type $setterType, " + @@ -64,6 +64,9 @@ data class PropertyDescriptor(val field: Field?, val setter: Method?, val getter } } +private fun Class<*>.boxesOrIsAssignableFrom(other: Class<*>) = + isAssignableFrom(other) || kotlin.javaPrimitiveType == other + private fun Type.isSupertypeOf(that: Type) = TypeToken.of(this).isSupertypeOf(that) // match an uppercase letter that also has a corresponding lower case equivalent @@ -85,7 +88,7 @@ private val propertyMethodRegex = Regex("(?get|set|is)(?\\p{Lu}.*)") * take a single parameter of a type compatible with exampleProperty and isExampleProperty must * return a boolean */ -internal fun Class.propertyDescriptors(): Map { +internal fun Class.propertyDescriptors(warnInvalid: Boolean = true): Map { val fieldProperties = superclassChain().declaredFields().byFieldName() return superclassChain().declaredMethods() @@ -93,8 +96,9 @@ internal fun Class.propertyDescriptors(): Map): Map = - rawType.propertyDescriptors().asSequence().mapNotNull { (name, descriptor) -> + rawType.propertyDescriptors(warnIfNonComposable).asSequence().mapNotNull { (name, descriptor) -> if (descriptor.field == null || descriptor.getter == null) null else { val paramType = (descriptor.getter.genericReturnType).resolveAgainstContext() @@ -310,7 +310,7 @@ internal data class LocalTypeInformationBuilder(val lookup: LocalTypeLookup, parameter.name to index }.toMap() - return rawType.propertyDescriptors().asSequence().mapNotNull { (name, descriptor) -> + return rawType.propertyDescriptors(warnIfNonComposable).asSequence().mapNotNull { (name, descriptor) -> val normalisedName = when { name in constructorParameterIndices -> name name.decapitalize() in constructorParameterIndices -> name.decapitalize() @@ -353,7 +353,7 @@ internal data class LocalTypeInformationBuilder(val lookup: LocalTypeLookup, } private fun getterSetterProperties(rawType: Class<*>): Sequence> = - rawType.propertyDescriptors().asSequence().mapNotNull { (name, descriptor) -> + rawType.propertyDescriptors(warnIfNonComposable).asSequence().mapNotNull { (name, descriptor) -> if (descriptor.getter == null || descriptor.setter == null || descriptor.field == null) null else { val paramType = descriptor.getter.genericReturnType diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt index 74f92c2e41..de1378fac4 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt @@ -6,14 +6,15 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.node.serialization.kryo.kryoMagic import net.corda.node.services.statemachine.DataSessionMessage +import net.corda.serialization.internal.amqp.propertyDescriptors import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.kryoSpecific -import org.junit.Assert.assertArrayEquals -import org.junit.Assert.assertEquals +import org.junit.Assert.* import org.junit.Rule import org.junit.Test import java.io.ByteArrayOutputStream import java.util.* +import org.assertj.core.api.Assertions.assertThat class SetsSerializationTest { private companion object { @@ -64,4 +65,27 @@ class SetsSerializationTest { } assertArrayEquals(output.toByteArray(), serializedForm.bytes) } + + open class P + class VarOfP(var p: Set

) + + /* + See CORDA-2860. + + When a class has a var parameter of type Set, Kotlin generates getters and setters with the following (Java) signatures: + + public Set getP(); + public void setP(Set p); + + The PropertyDescriptor.validate method used to check that the return type of the getter was a supertype of the parameter type of the + setter. Unfortunately, Set is not a strict supertype of Set, so this check would fail, throwing an exception. + + We now check only for compatibility of the erased classes, so the call to propertyDescriptors() below should now succeed, returning the + property descriptor for "p". + */ + @Test + fun `type variance on setter getter pair does not fail validation`() { + assertThat(VarOfP::class.java.propertyDescriptors()).containsKey("p") + } + } diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/model/LocalTypeModelTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/model/LocalTypeModelTests.kt index 7a10fdb560..aa15770638 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/model/LocalTypeModelTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/model/LocalTypeModelTests.kt @@ -40,7 +40,7 @@ class LocalTypeModelTests { @Test fun `Primitives and collections`() { - assertInformation>("CollectionHolder") + assertInformation>("CollectionHolder") assertInformation>(""" StringKeyedCollectionHolder(list: List, map: Map, array: List[]): CollectionHolder @@ -85,9 +85,9 @@ class LocalTypeModelTests { fun `interfaces and superclasses`() { assertInformation>("SuperSuper") assertInformation>("Super: SuperSuper") - assertInformation>(""" - Abstract: Super, SuperSuper - a: LocalDateTime[] + assertInformation>(""" + Abstract: Super, SuperSuper + a: String[] b: Double """) assertInformation(""" From bb41d2941d3b1a4765be1e830aecb3f708c2927e Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Wed, 1 May 2019 11:13:31 +0100 Subject: [PATCH 27/75] CORDA-2847 -Support custom serialisers when attaching missing attachments to txs (#5046) (#5062) (cherry picked from commit 2e4c5d79f6d98122e51849f3fd2a46ad2aad22e5) --- .../core/transactions/TransactionBuilder.kt | 71 ++++++++++++------- .../UnusedFinanceSerializers.kt | 27 +++++++ 2 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 samples/simm-valuation-demo/contracts-states/src/main/kotlin/net/corda/vega/plugin/customserializers/UnusedFinanceSerializers.kt diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index 69f9013678..c05ee4b314 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -17,11 +17,13 @@ import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationFactory import net.corda.core.utilities.contextLogger import java.io.NotSerializableException +import java.lang.Exception import java.security.PublicKey import java.time.Duration import java.time.Instant import java.util.ArrayDeque import java.util.UUID +import java.util.regex.Pattern import kotlin.collections.ArrayList import kotlin.collections.component1 import kotlin.collections.component2 @@ -67,6 +69,10 @@ open class TransactionBuilder( private fun defaultLockId() = (Strand.currentStrand() as? FlowStateMachine<*>)?.id?.uuid ?: UUID.randomUUID() private val log = contextLogger() private const val CORDA_VERSION_THAT_INTRODUCED_FLATTENED_COMMANDS = 4 + + private val ID_PATTERN = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*" + private val FQCP = Pattern.compile("$ID_PATTERN(/$ID_PATTERN)+") + private fun isValidJavaClass(identifier: String) = FQCP.matcher(identifier).matches() } private val inputsWithTransactionState = arrayListOf>() @@ -168,42 +174,52 @@ open class TransactionBuilder( * @return true if a new dependency was successfully added. */ private fun addMissingDependency(services: ServicesForResolution, wireTx: WireTransaction): Boolean { - try { + return try { wireTx.toLedgerTransaction(services).verify() - } catch (e: NoClassDefFoundError) { - val missingClass = e.message ?: throw e - addMissingAttachment(missingClass, services) - return true - } catch (e: TransactionDeserialisationException) { - if (e.cause is NotSerializableException && e.cause.cause is ClassNotFoundException) { - val missingClass = e.cause.cause!!.message ?: throw e - addMissingAttachment(missingClass.replace(".", "/"), services) - return true + // The transaction verified successfully without adding any extra dependency. + false + } catch (e: Throwable) { + val rootError = e.rootCause + when { + // Handle various exceptions that can be thrown during verification and drill down the wrappings. + // Note: this is a best effort to preserve backwards compatibility. + rootError is ClassNotFoundException -> addMissingAttachment((rootError.message ?: throw e).replace(".", "/"), services, e) + rootError is NoClassDefFoundError -> addMissingAttachment(rootError.message ?: throw e, services, e) + + // Ignore these exceptions as they will break unit tests. + // The point here is only to detect missing dependencies. The other exceptions are irrelevant. + e is TransactionVerificationException -> false + e is TransactionResolutionException -> false + e is IllegalStateException -> false + e is IllegalArgumentException -> false + + // Fail early if none of the expected scenarios were hit. + else -> { + log.error("""The transaction currently built will not validate because of an unknown error most likely caused by a + missing dependency in the transaction attachments. + Please contact the developer of the CorDapp for further instructions. + """.trimIndent(), e) + throw e + } } - return false - } catch (e: NotSerializableException) { - if (e.cause is ClassNotFoundException) { - val missingClass = e.cause!!.message ?: throw e - addMissingAttachment(missingClass.replace(".", "/"), services) - return true - } - return false - // Ignore these exceptions as they will break unit tests. - // The point here is only to detect missing dependencies. The other exceptions are irrelevant. - } catch (tve: TransactionVerificationException) { - } catch (tre: TransactionResolutionException) { - } catch (ise: IllegalStateException) { - } catch (ise: IllegalArgumentException) { } - return false } - private fun addMissingAttachment(missingClass: String, services: ServicesForResolution) { + private fun addMissingAttachment(missingClass: String, services: ServicesForResolution, originalException: Throwable): Boolean { + if (!isValidJavaClass(missingClass)) { + log.warn("Could not autodetect a valid attachment for the transaction being built.") + throw originalException + } + val attachment = services.attachments.internalFindTrustedAttachmentForClass(missingClass) - ?: throw IllegalArgumentException("""The transaction currently built is missing an attachment for class: $missingClass. + + if (attachment == null) { + log.error("""The transaction currently built is missing an attachment for class: $missingClass. Attempted to find a suitable attachment but could not find any in the storage. Please contact the developer of the CorDapp for further instructions. """.trimIndent()) + throw originalException + } log.warnOnce("""The transaction currently built is missing an attachment for class: $missingClass. Automatically attaching contract dependency $attachment. @@ -211,6 +227,7 @@ open class TransactionBuilder( """.trimIndent()) addAttachment(attachment.id) + return true } /** diff --git a/samples/simm-valuation-demo/contracts-states/src/main/kotlin/net/corda/vega/plugin/customserializers/UnusedFinanceSerializers.kt b/samples/simm-valuation-demo/contracts-states/src/main/kotlin/net/corda/vega/plugin/customserializers/UnusedFinanceSerializers.kt new file mode 100644 index 0000000000..fb2bb2ce37 --- /dev/null +++ b/samples/simm-valuation-demo/contracts-states/src/main/kotlin/net/corda/vega/plugin/customserializers/UnusedFinanceSerializers.kt @@ -0,0 +1,27 @@ +package net.corda.vega.plugin.customserializers + +import net.corda.core.serialization.SerializationCustomSerializer +import net.corda.finance.contracts.asset.Cash +import net.corda.finance.schemas.CashSchema + +/** + * This just references a random class from the finance Cordapp for testing purposes. + */ +@Suppress("UNUSED") +class UnusedFinanceSerializer : SerializationCustomSerializer { + class Proxy + override fun toProxy(obj: CashSchema): Proxy =Proxy() + override fun fromProxy(proxy: Proxy): CashSchema = CashSchema +} + +class Unused +@Suppress("UNUSED") +class UnusedFinanceSerializer1 : SerializationCustomSerializer { + init { + // Just instantiate some finance class. + Cash() + } + class Proxy + override fun toProxy(obj: Unused): Proxy =Proxy() + override fun fromProxy(proxy: Proxy): Unused = Unused() +} From cdc29dd4ca427b88c9af62da4dc8c9b18ee0f967 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Wed, 1 May 2019 11:15:50 +0100 Subject: [PATCH 28/75] CORDA-2477 - Improve Signature Constraints documentation * CORDA-2477 Improve Signature Constraints documentation (#5041) The Signature Constraint documentation in `api-contract-constraints` was very limited and referred to the design doc for most information. Information was extracted from the design doc and added to the main documentation. (cherry picked from commit cb85dd1e927f1bdd42ba483f53191f8be2ed4722) * CORDA-2477 Add codesets so backported docs show properly --- .../core/contracts/AttachmentConstraint.kt | 2 +- .../core/internal/JarSignatureCollector.kt | 2 +- docs/source/api-contract-constraints.rst | 221 +++++++++++++----- docs/source/app-upgrade-notes.rst | 6 +- docs/source/versioning.rst | 2 +- .../testing/core/JarSignatureCollectorTest.kt | 2 +- 6 files changed, 172 insertions(+), 63 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt index b2d86bc012..a5e5e7cd7d 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/AttachmentConstraint.kt @@ -108,7 +108,7 @@ object AutomaticPlaceholderConstraint : AttachmentConstraint { /** * An [AttachmentConstraint] that verifies that the attachment has signers that fulfil the provided [PublicKey]. - * See: [Signature Constraints](https://docs.corda.net/design/data-model-upgrades/signature-constraints.html) + * See: [Signature Constraints](https://docs.corda.net/api-contract-constraints.html#signature-constraints) * * @property key A [PublicKey] that must be fulfilled by the owning keys of the attachment's signing parties. */ diff --git a/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt b/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt index 38435916d5..5357cfbae6 100644 --- a/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt +++ b/core/src/main/kotlin/net/corda/core/internal/JarSignatureCollector.kt @@ -52,7 +52,7 @@ object JarSignatureCollector { """ Mismatch between signers ${firstSignerSet.toOrderedPublicKeys()} for file $firstFile and signers ${otherSignerSet.toOrderedPublicKeys()} for file ${otherFile}. - See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the + See https://docs.corda.net/api-contract-constraints.html#signature-constraints for details of the constraints applied to attachment signatures. """.trimIndent().replace('\n', ' ')) } diff --git a/docs/source/api-contract-constraints.rst b/docs/source/api-contract-constraints.rst index 7b2b039764..5b6a280346 100644 --- a/docs/source/api-contract-constraints.rst +++ b/docs/source/api-contract-constraints.rst @@ -1,4 +1,10 @@ .. highlight:: kotlin +.. role:: kotlin(code) + :language: kotlin +.. raw:: html + + + API: Contract Constraints ========================= @@ -7,6 +13,9 @@ API: Contract Constraints .. contents:: +Reasons for Contract Constraints +-------------------------------- + *Contract constraints* solve two problems faced by any decentralised ledger that supports evolution of data and code: 1. Controlling and agreeing upon upgrades @@ -21,51 +30,127 @@ This constraint specifies which versions of an application can be used to provid New versions released after a transaction is signed and finalised won't affect prior transactions because the old code is attached to it. -There are several types of constraint: - -1. Hash constraint: exactly one version of the app can be used with this state. -2. Compatibility zone whitelisted (or CZ whitelisted) constraint: the compatibility zone operator lists the hashes of the versions that can be used with this contract class name. -3. Signature constraint: any version of the app signed by the given ``CompositeKey`` can be used. -4. Always accept constraint: any app can be used at all. This is insecure but convenient for testing. - -The actual app version used is defined by the attachments on a transaction that consumes a state: the JAR containing the state and contract classes, and optionally -its dependencies, are all attached to the transaction. Other nodes will download these JARs from a node if they haven't seen them before, -so they can be used for verification. The ``TransactionBuilder`` will manage the details of constraints for you, by selecting both constraints -and attachments to ensure they line up correctly. Therefore you only need to have a basic understanding of this topic unless you are -doing something sophisticated. - -The best kind of constraint to use is the **signature constraint**. If you sign your application it will be used automatically. -We recommend signature constraints because they let you smoothly migrate existing data to new versions of your application. -Hash and zone whitelist constraints are left over from earlier Corda versions before signature constraints were -implemented. They make it harder to upgrade applications than when using signature constraints, so they're best avoided. -Signature constraints can specify flexible threshold policies, but if you use the automatic support then a state will -require the attached app to be signed by every key that the first attachment was signed by. Thus if the app that was used -to issue the states was signed by Alice and Bob, every transaction must use an attachment signed by Alice and Bob. - -**Constraint propagation.** Constraints are picked when a state is created for the first time in an issuance transaction. Once created, -the constraint used by equivalent output states (i.e. output states that use the same contract class name) must match the -input state, so it can't be changed and you can't combine states with incompatible constraints together in the same transaction. - .. _implicit_vs_explicit_upgrades: -**Implicit vs explicit.** Constraints are not the only way to manage upgrades to transactions. There are two ways of handling +Implicit vs Explicit Contract upgrades +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Constraints are not the only way to manage upgrades to transactions. There are two ways of handling upgrades to a smart contract in Corda: -1. *Implicit:* By pre-authorising multiple implementations of the contract ahead of time, using constraints. -2. *Explicit:* By creating a special *contract upgrade transaction* and getting all participants of a state to sign it using the - contract upgrade flows. - -This article focuses on the first approach. To learn about the second please see :doc:`upgrading-cordapps`. +* **Implicit**: By pre-authorising multiple implementations of the contract ahead of time, using constraints. +* **Explicit**: By creating a special *contract upgrade transaction* and getting all participants of a state to sign it using the + contract upgrade flows. The advantage of pre-authorising upgrades using constraints is that you don't need the heavyweight process of creating upgrade transactions for every state on the ledger. The disadvantage is that you place more faith in third parties, who could potentially change the app in ways you did not expect or agree with. The advantage of using the explicit upgrade approach is that you can upgrade states regardless of their constraint, including in cases where you didn't -anticipate a need to do so. But it requires everyone to sign, requires everyone to manually authorise the upgrade, +anticipate a need to do so. But it requires everyone to sign, manually authorise the upgrade, consumes notary and ledger resources, and is just in general more complex. +This article focuses on the first approach. To learn about the second please see :doc:`upgrading-cordapps`. + .. _implicit_constraint_types: +Types of Contract Constraints +----------------------------- + +Corda supports several types of constraints to cover a wide set of client requirements: + +* **Hash constraint**: Exactly one version of the app can be used with this state. This prevents the app from being upgraded in the future while still + making use of the state created with the original version. +* **Compatibility zone whitelisted (or CZ whitelisted) constraint**: The compatibility zone operator lists the hashes of the versions that can be used with a contract class name. +* **Signature constraint**: Any version of the app signed by the given ``CompositeKey`` can be used. This allows app issuers to express the + complex social and business relationships that arise around code ownership. For example, a Signature Constraint allows a new version of an + app to be produced and applied to an existing state as long as it has been signed by the same key(s) as the original version. +* **Always accept constraint**: Any version of the app can be used. This is insecure but convenient for testing. + +.. _signature_constraints: + +Signature Constraints +--------------------- + +The best kind of constraint to use is the **Signature Constraint**. If you sign your application it will be used automatically. +We recommend signature constraints because they let you express complex social and business relationships while allowing +smooth migration of existing data to new versions of your application. + +Signature constraints can specify flexible threshold policies, but if you use the automatic support then a state will +require the attached app to be signed by every key that the first attachment was signed by. Thus if the app that was used +to issue the states was signed by Alice and Bob, every transaction must use an attachment signed by Alice and Bob. Doing so allows the +app to be upgraded and changed while still remaining valid for use with the previously issued states. + +More complex policies can be expressed through Signature Constraints if required. Allowing policies where only a number of the possible +signers must sign the new version of an app that is interacting with previously issued states. Accepting different versions of apps in this +way makes it possible for multiple versions to be valid across the network as long as the majority (or possibly a minority) agree with the +logic provided by the apps. + +Hash and zone whitelist constraints are left over from earlier Corda versions before Signature Constraints were +implemented. They make it harder to upgrade applications than when using signature constraints, so they're best avoided. + +Further information into the design of Signature Constraints can be found in its :doc:`design document `. + +Signing CorDapps for use with Signature Constraints +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Expanding on the previous section, for an app to use Signature Constraints, it must be signed by a ``CompositeKey`` or a simpler ``PublicKey``. +The signers of the app can consist of a single organisation or multiple organisations. Once the app has been signed, it can be distributed +across the nodes that intend to use it. + +Each transaction received by a node will then verify that the apps attached to it have the correct signers as specified by its +Signature Constraints. This ensures that the version of each app is acceptable to the transaction's input states. + +More information on how to sign an app directly from Gradle can be found in the +:ref:`CorDapp Jar signing ` section of the documentation. + +Using Signature Constraints in transactions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the app is signed, Signature Constraints will be used by default (in most situations) by the ``TransactionBuilder`` when adding output states. +This is expanded upon in :ref:`contract_constraints_in_transactions`. + +.. note:: Signature Constraints are used by default except when a new transaction contains an input state with a Hash Constraint. In this + situation the Hash Constraint is used. + +.. _app_versioning_with_signature_constraints: + +App versioning with Signature Constraints +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Signed apps require a version number to be provided, see :doc:`versioning`. You can't import two different +JARs that claim to be the same version, provide the same contract classes and which are both signed. At runtime +the node will throw a ``DuplicateContractClassException`` exception if this condition is violated. + +Hash Constraints +---------------- + +Issues when using the HashAttachmentConstraint +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When setting up a new network, it is possible to encounter errors when states are issued with the ``HashAttachmentConstraint``, +but not all nodes have that same version of the CorDapp installed locally. + +In this case, flows will fail with a ``ContractConstraintRejection``, and are sent to the flow hospital. +From there, they are suspended, waiting to be retried on node restart. +This gives the node operator the opportunity to recover from those errors, which in the case of constraint violations means +adding the right cordapp jar to the ``cordapps`` folder. + +.. _relax_hash_constraints_checking_ref: + +Hash constrained states in private networks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Where private networks started life using CorDapps with hash constrained states, we have introduced a mechanism to relax the checking of +these hash constrained states when upgrading to signed CorDapps using signature constraints. + +The Java system property ``-Dnet.corda.node.disableHashConstraints="true"`` may be set to relax the hash constraint checking behaviour. For +this to work, every participant of the network must set the property to the same value. Therefore, this mode should only be used upon +"out of band" agreement by all participants in a network. + +.. warning:: This flag should remain enabled until every hash constrained state is exited from the ledger. + +.. _contract_state_agreement: + Contract/State Agreement ------------------------ @@ -118,39 +203,64 @@ CorDapp is 4 or greater, then transaction verification will fail with a ``Transa the owning ``Contract`` *can* be identified, but the ``ContractState`` has been bundled with a different contract, then transaction verification will fail with a ``TransactionContractConflictException``. -.. _contract_downgrade_rule_ref: +.. _contract_constraints_in_transactions: -App versioning with signature constraints ------------------------------------------ +Using Contract Constraints in Transactions +------------------------------------------ -Signed apps require a version number to be provided, see :doc:`versioning`. You can't import two different -JARs that claim to be the same version, provide the same contract classes and which are both signed. At runtime -the node will throw a ``DuplicateContractClassException`` exception if this condition is violated. +The app version used by a transaction is defined by its attachments. The JAR containing the state and contract classes, and optionally its +dependencies, are all attached to the transaction. Nodes will download this JAR from other nodes if they haven't seen it before, +so it can be used for verification. -Issues when using the HashAttachmentConstraint ----------------------------------------------- +The ``TransactionBuilder`` will manage the details of constraints for you, by selecting both constraints +and attachments to ensure they line up correctly. Therefore you only need to have a basic understanding of this topic unless you are +doing something sophisticated. -When setting up a new network, it is possible to encounter errors when states are issued with the ``HashAttachmentConstraint``, -but not all nodes have that same version of the CorDapp installed locally. +By default the ``TransactionBuilder`` will use :ref:`signature_constraints` for any issuance transactions if the app attached to it is +signed. -In this case, flows will fail with a ``ContractConstraintRejection``, and the failed flow will be sent to the flow hospital. -From there it's suspended waiting to be retried on node restart. -This gives the node operator the opportunity to recover from those errors, which in the case of constraint violations means -adding the right cordapp jar to the ``cordapps`` folder. +To manually define the Contract Constraint of an output state, see the example below: -.. _relax_hash_constraints_checking_ref: +.. container:: codeset -Hash constrained states in private networks -------------------------------------------- + .. sourcecode:: java -Where private networks started life using CorDapps with hash constrained states, we have introduced a mechanism to relax the checking of -these hash constrained states when upgrading to signed CorDapps using signature constraints. + TransactionBuilder transaction() { + TransactionBuilder transaction = new TransactionBuilder(notary()); + // Signature Constraint used if app is signed + transaction.addOutputState(state); + // Explicitly using a Signature Constraint + transaction.addOutputState(state, CONTRACT_ID, new SignatureAttachmentConstraint(getOurIdentity().getOwningKey())); + // Explicitly using a Hash Constraint + transaction.addOutputState(state, CONTRACT_ID, new HashAttachmentConstraint(getServiceHub().getCordappProvider().getContractAttachmentID(CONTRACT_ID))); + // Explicitly using a Whitelisted by Zone Constraint + transaction.addOutputState(state, CONTRACT_ID, WhitelistedByZoneAttachmentConstraint.INSTANCE); + // Explicitly using an Always Accept Constraint + transaction.addOutputState(state, CONTRACT_ID, AlwaysAcceptAttachmentConstraint.INSTANCE); -The Java system property ``-Dnet.corda.node.disableHashConstraints="true"`` may be set to relax the hash constraint checking behaviour. + // other transaction stuff + return transaction; + } -This mode should only be used upon "out of band" agreement by all participants in a network. -Please also beware that this flag should remain enabled until every hash constrained state is exited from the ledger. + .. sourcecode:: kotlin + + private fun transaction(): TransactionBuilder { + val transaction = TransactionBuilder(notary()) + // Signature Constraint used if app is signed + transaction.addOutputState(state) + // Explicitly using a Signature Constraint + transaction.addOutputState(state, constraint = SignatureAttachmentConstraint(ourIdentity.owningKey)) + // Explicitly using a Hash Constraint + transaction.addOutputState(state, constraint = HashAttachmentConstraint(serviceHub.cordappProvider.getContractAttachmentID(CONTRACT_ID)!!)) + // Explicitly using a Whitelisted by Zone Constraint + transaction.addOutputState(state, constraint = WhitelistedByZoneAttachmentConstraint) + // Explicitly using an Always Accept Constraint + transaction.addOutputState(state, constraint = AlwaysAcceptAttachmentConstraint) + + // other transaction stuff + return transaction + } CorDapps as attachments ----------------------- @@ -168,12 +278,11 @@ node or will be automatically fetched over the network when receiving a transact app into multiple modules: one which contains just states, contracts and core data types. And another which contains the rest of the app. See :ref:`cordapp-structure`. - Constraints propagation ----------------------- As was mentioned above, the ``TransactionBuilder`` API gives the CorDapp developer or even malicious node owner the possibility -to construct output states with a constraint of his choosing. +to construct output states with a constraint of their choosing. For the ledger to remain in a consistent state, the expected behavior is for output state to inherit the constraints of input states. This guarantees that for example, a transaction can't output a state with the ``AlwaysAcceptAttachmentConstraint`` when the diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index e9a104b0a8..956a272e7a 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -102,7 +102,7 @@ properly for future releases. future not hold true. You should know the platform version of the node releases you want to target. The new ``versionId`` number is a version code for **your** app, and is unrelated to Corda's own versions. -It is used to informative purposes only. See ":ref:`contract_downgrade_rule_ref`" for more information. +It is used to informative purposes only. See ":ref:`app_versioning_with_signature_constraints`" for more information. **Split your app into contract and workflow JARs.** The duplication between ``contract`` and ``workflow`` blocks exists because you should split your app into two separate JARs/modules, one that contains on-ledger validation code like states and contracts, and one @@ -359,13 +359,13 @@ to be governed by a contract that is either: 1. The outer class of the state class, if the state is an inner class of a contract. This is a common design pattern. 2. Annotated with ``@BelongsToContract`` which specifies the contract class explicitly. -Learn more by reading ":ref:`implicit_constraint_types`". If an app targets Corda 3 or lower (i.e. does not specify a target version), +Learn more by reading :ref:`contract_state_agreement`. If an app targets Corda 3 or lower (i.e. does not specify a target version), states that point to contracts outside their package will trigger a log warning but validation will proceed. Step 9. Learn about signature constraints and JAR signing --------------------------------------------------------- -:doc:`design/data-model-upgrades/signature-constraints` are a new data model feature introduced in Corda 4. They make it much easier to +:ref:`signature_constraints` are a new data model feature introduced in Corda 4. They make it much easier to deploy application upgrades smoothly and in a decentralised manner. Signature constraints are the new default mode for CorDapps, and the act of upgrading your app to use the version 4 Gradle plugins will result in your app being automatically signed, and new states automatically using new signature constraints selected automatically based on these signing keys. diff --git a/docs/source/versioning.rst b/docs/source/versioning.rst index f5793da47d..8068a3c54d 100644 --- a/docs/source/versioning.rst +++ b/docs/source/versioning.rst @@ -99,6 +99,6 @@ sophisticated or proprietary business logic, machine learning models, even user being Corda flows or services. .. important:: The ``versionId`` specified for the JAR manifest is checked by the platform and is used for informative purposes only. - See ":ref:`contract_downgrade_rule_ref`" for more information. + See ":ref:`app_versioning_with_signature_constraints`" for more information. .. note:: You can read the original design doc here: :doc:`design/targetversion/design`. \ No newline at end of file diff --git a/testing/test-utils/src/test/kotlin/net/corda/testing/core/JarSignatureCollectorTest.kt b/testing/test-utils/src/test/kotlin/net/corda/testing/core/JarSignatureCollectorTest.kt index aede6afef2..53ca08d444 100644 --- a/testing/test-utils/src/test/kotlin/net/corda/testing/core/JarSignatureCollectorTest.kt +++ b/testing/test-utils/src/test/kotlin/net/corda/testing/core/JarSignatureCollectorTest.kt @@ -110,7 +110,7 @@ class JarSignatureCollectorTest { """ Mismatch between signers [O=Alice Corp, L=Madrid, C=ES, O=Bob Plc, L=Rome, C=IT] for file _signable1 and signers [O=Bob Plc, L=Rome, C=IT] for file _signable2. - See https://docs.corda.net/design/data-model-upgrades/signature-constraints.html for details of the + See https://docs.corda.net/api-contract-constraints.html#signature-constraints for details of the constraints applied to attachment signatures. """.trimIndent().replace('\n', ' ') ) { dir.getJarSigners(FILENAME) } From b4ba2ca4c1de2f4d5dca60d29f1d2a560775d604 Mon Sep 17 00:00:00 2001 From: Bartman250 Date: Tue, 9 Apr 2019 19:37:28 +0100 Subject: [PATCH 29/75] CORDA-2834 Clarify error message when base directory doesn't exist (#4992) * setting non existant base directory now yields suitable error message * corrections to missing base directory message and comments added for clarity * added check for valid base directory before node loggin and bootstrapping * removed uneeded import * quantum computer joke back by popular demand and not back at the same time --- .../net/corda/node/internal/NodeStartup.kt | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 7b1af64b81..603bb8190f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -76,6 +76,24 @@ open class NodeStartupCli : CordaCliWrapper("corda", "Runs a Corda Node") { override fun additionalSubCommands() = setOf(networkCacheCli, justGenerateNodeInfoCli, justGenerateRpcSslCertsCli, initialRegistrationCli, validateConfigurationCli) + override fun call(): Int { + if (!validateBaseDirectory()) { + return ExitCodes.FAILURE + } + return super.call() + } + + private fun validateBaseDirectory(): Boolean { + //Ensure that the base directory actually exists before initialising and the rest of the node. + val baseDirectory = cmdLineOptions.baseDirectory + + if (!baseDirectory.exists()) { + printError("Base directory $baseDirectory does not exist. Node will now shutdown") + return false + } + return true + } + override fun runProgram(): Int { return when { InitialRegistration.checkRegistrationMode(cmdLineOptions.baseDirectory) -> { @@ -146,8 +164,10 @@ open class NodeStartup : NodeStartupLogging { Node.printBasicNodeInfo(LOGS_CAN_BE_FOUND_IN_STRING, System.getProperty("log-path")) // Step 5. Load and validate node configuration. - val rawConfig = cmdLineOptions.rawConfiguration().doOnErrors(cmdLineOptions::logRawConfigurationErrors).optional ?: return ExitCodes.FAILURE - val configuration = cmdLineOptions.parseConfiguration(rawConfig).doIfValid { logRawConfig(rawConfig) }.doOnErrors(::logConfigurationErrors).optional ?: return ExitCodes.FAILURE + val rawConfig = cmdLineOptions.rawConfiguration().doOnErrors(cmdLineOptions::logRawConfigurationErrors).optional + ?: return ExitCodes.FAILURE + val configuration = cmdLineOptions.parseConfiguration(rawConfig).doIfValid { logRawConfig(rawConfig) }.doOnErrors(::logConfigurationErrors).optional + ?: return ExitCodes.FAILURE // Step 6. Check if we can access the certificates directory if (requireCertificates && !canReadCertificatesDirectory(configuration.certificatesDirectory, configuration.devMode)) return ExitCodes.FAILURE @@ -390,7 +410,8 @@ open class NodeStartup : NodeStartupLogging { "When I discovered my toaster wasn't\nwaterproof, I was shocked.", "Where do cryptographers go for\nentertainment? The security theatre.", "How did the Java programmer get rich?\nThey inherited a factory.", - "Why did the developer quit his job?\nHe didn't get ar-rays." + "Why did the developer quit his job?\nHe didn't get ar-rays.", + "Quantum computer jokes are both\n funny and not funny at the same time." ) if (Emoji.hasEmojiTerminal) From 11281561533fb23fd34dab143b91dc8def4947ad Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Thu, 25 Apr 2019 16:34:05 +0100 Subject: [PATCH 30/75] CORDA-2821 Fix issue with Quasar errors redirecting to useless page (#4982) * Changed dead url to the new location. * Modified documentation. Linked new place for intelliJ guide. * Changed url to the new link. --- docs/source/testing.rst | 44 +++++++++++++++++++ docs/source/tutorial-cordapp.rst | 36 +-------------- .../SingleThreadedStateMachineManager.kt | 2 +- 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/docs/source/testing.rst b/docs/source/testing.rst index 93cd889533..24b7143f87 100644 --- a/docs/source/testing.rst +++ b/docs/source/testing.rst @@ -33,3 +33,47 @@ as follows: How to manually test each of these areas differs and is currently not fully specified. For now the best thing to do is to ensure the program starts, that you can interact with it, and that no exceptions are generated in normal operation. + +Running tests in IntelliJ +------------------------- + +We recommend editing your IntelliJ preferences so that you use the Gradle runner - this means that the quasar utils +plugin will make sure that some flags (like ``-javaagent`` - see :ref:`below `) are +set for you. + +To switch to using the Gradle runner: + +* Navigate to ``Build, Execution, Deployment -> Build Tools -> Gradle -> Runner`` (or search for `runner`) + + * Windows: this is in "Settings" + * MacOS: this is in "Preferences" + +* Set "Delegate IDE build/run actions to gradle" to true +* Set "Run test using:" to "Gradle Test Runner" + +.. _tutorial_cordapp_alternative_test_runners: + +If you would prefer to use the built in IntelliJ JUnit test runner, you can add some code to your ``build.gradle`` file and +it will copy your quasar JAR file to the lib directory. + +.. note:: Before creating the IntelliJ run configurations for these unit tests + go to Run -> Edit Configurations -> Defaults -> JUnit, add + ``-javaagent:lib/quasar.jar`` + to the VM options, and set Working directory to ``$PROJECT_DIR$`` + so that the ``Quasar`` instrumentation is correctly configured. + +Add the following to your ``build.gradle`` file - ideally to a ``build.gradle`` that already contains the quasar-utils plugin line: + +.. sourcecode:: groovy + + apply plugin: 'net.corda.plugins.quasar-utils' + + task installQuasar(type: Copy) { + destinationDir rootProject.file("lib") + from(configurations.quasar) { + rename 'quasar-core(.*).jar', 'quasar.jar' + } + } + + +and then you can run ``gradlew installQuasar``. \ No newline at end of file diff --git a/docs/source/tutorial-cordapp.rst b/docs/source/tutorial-cordapp.rst index eaa3b20fbb..f7d30e9e97 100644 --- a/docs/source/tutorial-cordapp.rst +++ b/docs/source/tutorial-cordapp.rst @@ -486,41 +486,7 @@ You can run the CorDapp's integration tests by running the ``Run Integration Tes Running tests in IntelliJ ~~~~~~~~~~~~~~~~~~~~~~~~~ -We recommend editing your IntelliJ preferences so that you use the Gradle runner - this means that the quasar utils -plugin will make sure that some flags (like ``-javaagent`` - see :ref:`below `) are -set for you. - -To switch to using the Gradle runner: - -* Navigate to ``Build, Execution, Deployment -> Build Tools -> Gradle -> Runner`` (or search for `runner`) - - * Windows: this is in "Settings" - * MacOS: this is in "Preferences" - -* Set "Delegate IDE build/run actions to gradle" to true -* Set "Run test using:" to "Gradle Test Runner" - -.. _tutorial_cordapp_alternative_test_runners: - -If you would prefer to use the built in IntelliJ JUnit test runner, you can add some code to your ``build.gradle`` file and -it will copy your quasar JAR file to the lib directory. You will also need to specify ``-javaagent:lib/quasar.jar`` -and set the run directory to the project root directory for each test. - -Add the following to your ``build.gradle`` file - ideally to a ``build.gradle`` that already contains the quasar-utils plugin line: - -.. sourcecode:: groovy - - apply plugin: 'net.corda.plugins.quasar-utils' - - task installQuasar(type: Copy) { - destinationDir rootProject.file("lib") - from(configurations.quasar) { - rename 'quasar-core(.*).jar', 'quasar.jar' - } - } - - -and then you can run ``gradlew installQuasar``. +See :ref:`Running tests in IntelliJ` Debugging your CorDapp ---------------------- diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt index c2fcdc9da4..76994a826e 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt @@ -309,7 +309,7 @@ class SingleThreadedStateMachineManager( private fun checkQuasarJavaAgentPresence() { check(SuspendableHelper.isJavaAgentActive()) { """Missing the '-javaagent' JVM argument. Make sure you run the tests with the Quasar java agent attached to your JVM. - #See https://docs.corda.net/troubleshooting.html - 'Fiber classes not instrumented' for more details.""".trimMargin("#") + #See https://docs.corda.net/head/testing.html#running-tests-in-intellij - 'Fiber classes not instrumented' for more details.""".trimMargin("#") } } From 08c4f1a2fbfe6f1d7d3172cfb21c11a6faf46bce Mon Sep 17 00:00:00 2001 From: Ben Wyeth Date: Mon, 25 Mar 2019 12:20:26 +0000 Subject: [PATCH 31/75] CORDA-2781 - Replace manual versions with substitutions (#4927) * been through the docs manually and picked out substitutions * hopefully addressing the url inconsistencies --- docs/source/generating-a-node.rst | 2 +- docs/source/network-bootstrapper.rst | 20 +++++--------------- docs/source/testnet-explorer-corda.rst | 12 ++++++------ 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/docs/source/generating-a-node.rst b/docs/source/generating-a-node.rst index 8bc8f7329d..53e115c7af 100644 --- a/docs/source/generating-a-node.rst +++ b/docs/source/generating-a-node.rst @@ -17,7 +17,7 @@ A node can be created manually by creating a folder that contains the following * **Optional:** A webserver JAR entitled ``corda-webserver.jar`` that will connect to the node via RPC - * The (deprecated) default webserver can be downloaded from http://r3.bintray.com/corda/net/corda/corda-webserver/ (under /|corda_version|/corda-webserver-|corda_version|.jar) + * The (deprecated) default webserver can be downloaded from http://r3.bintray.com/corda/net/corda/corda-webserver/ (under /|corda_version|/corda-|corda_version|.jar) * A Spring Boot alternative can be found here: https://github.com/corda/spring-webserver The remaining files and folders described in :doc:`node-structure` will be generated at runtime. diff --git a/docs/source/network-bootstrapper.rst b/docs/source/network-bootstrapper.rst index f4ef61fde7..cf6a3b565a 100644 --- a/docs/source/network-bootstrapper.rst +++ b/docs/source/network-bootstrapper.rst @@ -29,9 +29,7 @@ The Corda Network Bootstrapper can be downloaded from `here +``java -jar network-bootstrapper-|corda_version|.jar --dir `` For example running the command on a directory containing these files: @@ -155,9 +153,7 @@ can use the Network Bootstrapper on the following network structure: Then run the Network Bootstrapper again from the root dir: -.. sourcecode:: bash - - java -jar corda-tools-network-bootstrapper-|corda_version|.jar --dir +``java -jar network-bootstrapper-|corda_version|.jar --dir `` Which will give the following: @@ -228,9 +224,7 @@ For example, with the following pre-generated network: Then run the Network Bootstrapper again from the root dir: -.. sourcecode:: bash - - java -jar corda-tools-network-bootstrapper-|corda_version|.jar --dir +``java -jar network-bootstrapper-|corda_version|.jar --dir `` To give the following: @@ -277,15 +271,11 @@ Overriding network parameters via a file You can provide a network parameters overrides file using the following syntax: -.. sourcecode:: bash - - java -jar corda-tools-network-bootstrapper-|corda_version|.jar --network-parameter-overrides= +``java -jar network-bootstrapper-|corda_version|.jar --network-parameter-overrides=`` Or alternatively, by using the short form version: -.. sourcecode:: bash - - java -jar corda-tools-network-bootstrapper-|corda_version|.jar -n= +``java -jar network-bootstrapper-|corda_version|.jar -n=`` The network parameter overrides file is a HOCON file with the following fields, all of which are optional. Any field that is not provided will be ignored. If a field is not provided and you are bootstrapping a new network, a sensible default value will be used. If a field is not provided and you diff --git a/docs/source/testnet-explorer-corda.rst b/docs/source/testnet-explorer-corda.rst index 6a637ecbcf..56f7ea47d3 100644 --- a/docs/source/testnet-explorer-corda.rst +++ b/docs/source/testnet-explorer-corda.rst @@ -34,21 +34,21 @@ couple of resources. .. code:: bash - wget https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-finance-contracts/|corda_version|/corda-finance-contracts-|corda_version|.jar - wget https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-finance-workflows/|corda_version|/corda-finance-workflows-|corda_version|.jar + wget https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-finance-contracts-|corda_version|-corda/corda-finance-contracts-|corda_version|-corda.jar + wget https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-finance-workflows-|corda_version|-corda/corda-finance-workflows-|corda_version|-corda.jar This is required to run some flows to check your connections, and to issue/transfer cash to counterparties. Copy it to the Corda installation location: .. code:: bash - sudo cp /home//corda-finance-*-|corda_version|.jar /opt/corda/cordapps/ + sudo cp /home//corda-finance-|corda_version|-corda.jar /opt/corda/cordapps/ #. Run the following to create a config file for the finance CorDapp: .. code:: bash - echo "issuableCurrencies = [ USD ]" > /opt/corda/cordapps/config/corda-finance-|corda_version|.conf + echo "issuableCurrencies = [ USD ]" > /opt/corda/cordapps/config/corda-finance-|corda_version|-corda.conf #. Restart the Corda node: @@ -70,7 +70,7 @@ couple of resources. .. code:: bash - http://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-tools-explorer/|corda_version|/corda-tools-explorer-|corda_version|.jar + http://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda-tools-explorer/|corda_version|-corda/corda-tools-explorer-|corda_version|-corda.jar .. warning:: This Node Explorer is incompatible with the Corda Enterprise distribution and vice versa as they currently use different serialisation schemes (Kryo vs AMQP). @@ -79,7 +79,7 @@ couple of resources. .. code:: bash - java -jar corda-tools-explorer-|corda_version|.jar + java -jar corda-tools-explorer-|corda_version|-corda.jar .. image:: resources/explorer-login.png From 8eca590e36c9154322b3c09c1ad15caffdbd98ed Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Mon, 8 Apr 2019 12:18:39 +0100 Subject: [PATCH 33/75] CORDA-2710 - Add Java samples to upgrading to Corda 4 documentation (#4979) --- docs/source/app-upgrade-notes.rst | 99 ++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/docs/source/app-upgrade-notes.rst b/docs/source/app-upgrade-notes.rst index 956a272e7a..8b485beaea 100644 --- a/docs/source/app-upgrade-notes.rst +++ b/docs/source/app-upgrade-notes.rst @@ -304,39 +304,94 @@ reduced the exposure. If you are constructing a MockServices for testing contracts, and your contract uses the Cash contract from the finance app, you now need to explicitly add ``net.corda.finance.contracts`` to the list of ``cordappPackages``. This is a part of the work to disentangle -the finance app (which is really a demo app) from the Corda internals. Example:: +the finance app (which is really a demo app) from the Corda internals. Example: - val ledgerServices = MockServices( - listOf("net.corda.examples.obligation", "net.corda.testing.contracts"), - identityService = makeTestIdentityService(), - initialIdentity = TestIdentity(CordaX500Name("TestIdentity", "", "GB")) - ) +.. container:: codeset -becomes:: + .. sourcecode:: kotlin - val ledgerServices = MockServices( - listOf("net.corda.examples.obligation", "net.corda.testing.contracts", "net.corda.finance.contracts"), - identityService = makeTestIdentityService(), - initialIdentity = TestIdentity(CordaX500Name("TestIdentity", "", "GB")) - ) + val ledgerServices = MockServices( + listOf("net.corda.examples.obligation", "net.corda.testing.contracts"), + initialIdentity = TestIdentity(CordaX500Name("TestIdentity", "", "GB")), + identityService = makeTestIdentityService() + ) + + .. sourcecode:: java + + MockServices ledgerServices = new MockServices( + Arrays.asList("net.corda.examples.obligation", "net.corda.testing.contracts"), + new TestIdentity(new CordaX500Name("TestIdentity", "", "GB")), + makeTestIdentityService() + ); + +becomes: + +.. container:: codeset + + .. sourcecode:: kotlin + + val ledgerServices = MockServices( + listOf("net.corda.examples.obligation", "net.corda.testing.contracts", "net.corda.finance.contracts"), + initialIdentity = TestIdentity(CordaX500Name("TestIdentity", "", "GB")), + identityService = makeTestIdentityService() + ) + + .. sourcecode:: java + + MockServices ledgerServices = new MockServices( + Arrays.asList("net.corda.examples.obligation", "net.corda.testing.contracts", "net.corda.finance.contracts"), + new TestIdentity(new CordaX500Name("TestIdentity", "", "GB")), + makeTestIdentityService() + ); You may need to use the new ``TestCordapp`` API when testing with the node driver or mock network, especially if you decide to stick with the pre-Corda 4 ``FinalityFlow`` API. The previous way of pulling in CorDapps into your tests (i.e. via using the ``cordappPackages`` parameter) does not honour CorDapp versioning. The new API ``TestCordapp.findCordapp()`` discovers the CorDapps that contain the provided packages scanning the classpath, so you have to ensure that the classpath the tests are running under contains either the CorDapp ``.jar`` or (if using Gradle) the relevant Gradle sub-project. In the first case, the versioning information in the CorDapp ``.jar`` file will be maintained. In the second case, the versioning information will be retrieved from the Gradle ``cordapp`` task. -For example, if you are using ``MockNetwork`` for your tests, the following code:: +For example, if you are using ``MockNetwork`` for your tests, the following code: - val mockNetwork = MockNetwork( - cordappPackages = listOf("net.corda.examples.obligation", "net.corda.finance.contracts"), - notarySpecs = listOf(MockNetworkNotarySpec(notary)) - ) +.. container:: codeset -would need to be transformed into:: + .. sourcecode:: kotlin - val mockNetwork = MockNetwork(MockNetworkParameters( - cordappsForAllNodes = listOf(TestCordapp.findCordapp("net.corda.businessnetworks.membership")), - notarySpecs = listOf(MockNetworkNotarySpec(notary)) - )) + val mockNetwork = MockNetwork( + cordappPackages = listOf("net.corda.examples.obligation", "net.corda.finance.contracts"), + notarySpecs = listOf(MockNetworkNotarySpec(notary)) + ) + + .. sourcecode:: java + + MockNetwork mockNetwork = new MockNetwork( + Arrays.asList("net.corda.examples.obligation", "net.corda.finance.contracts"), + new MockNetworkParameters().withNotarySpecs(Arrays.asList(new MockNetworkNotarySpec(notary))) + ); + +would need to be transformed into: + +.. container:: codeset + + .. sourcecode:: kotlin + + val mockNetwork = MockNetwork( + MockNetworkParameters( + cordappsForAllNodes = listOf( + TestCordapp.findCordapp("net.corda.examples.obligation.contracts"), + TestCordapp.findCordapp("net.corda.examples.obligation.flows") + ), + notarySpecs = listOf(MockNetworkNotarySpec(notary)) + ) + ) + + .. sourcecode:: java + + MockNetwork mockNetwork = new MockNetwork( + new MockNetworkParameters( + Arrays.asList( + TestCordapp.findCordapp("net.corda.examples.obligation.contracts"), + TestCordapp.findCordapp("net.corda.examples.obligation.flows") + ) + ).withNotarySpecs(Arrays.asList(new MockNetworkNotarySpec(notary))) + ); Note that every package should exist in only one CorDapp, otherwise the discovery process won't be able to determine which one to use and you will most probably see an exception telling you ``There is more than one CorDapp containing the package``. For instance, if you have 2 CorDapps containing the packages ``net.corda.examples.obligation.contracts`` and ``net.corda.examples.obligation.flows``, you will get this error if you specify the package ``net.corda.examples.obligation``. From 6b3998c7ae70eda12b92dfd92c9ef1c19f9760d4 Mon Sep 17 00:00:00 2001 From: rui-r3 <49317333+rui-r3@users.noreply.github.com> Date: Tue, 23 Apr 2019 10:24:47 +0100 Subject: [PATCH 34/75] CORDA-2651 - Remove null values from changelog list (#5022) * CORDA-2651 Check if resources are in classpath before passing them to Liquibase * CORDA-2651 Add missing stop * CORDA-2651 Change exception type. Improve exception log message. * CORDA-2651 Add null check when getting resources from class loader * CORDA-2651 Do not include null values in the changelog list --- .../nodeapi/internal/persistence/SchemaMigration.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt index a867ed43cf..b8226e7e2d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt @@ -13,7 +13,6 @@ import net.corda.nodeapi.internal.MigrationHelpers.getMigrationResource import net.corda.core.schemas.MappedSchema import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.cordapp.CordappLoader -import sun.security.x509.X500Name import java.io.ByteArrayInputStream import java.io.InputStream import java.nio.file.Path @@ -99,7 +98,7 @@ class SchemaMigration( // Collect all changelog file referenced in the included schemas. // For backward compatibility reasons, when failOnMigrationMissing=false, we don't manage CorDapps via Liquibase but use the hibernate hbm2ddl=update. - val changelogList = schemas.map { mappedSchema -> + val changelogList = schemas.mapNotNull { mappedSchema -> val resource = getMigrationResource(mappedSchema, classLoader) when { resource != null -> resource @@ -115,6 +114,7 @@ class SchemaMigration( } System.setProperty(NODE_X500_NAME, ourName.toString()) val customResourceAccessor = CustomResourceAccessor(dynamicInclude, changelogList, classLoader) + checkResourcesInClassPath(changelogList) // current version of Liquibase appears to be non-threadsafe // this is apparent when multiple in-process nodes are all running migrations simultaneously @@ -209,6 +209,7 @@ class SchemaMigration( if (preV4Baseline.isNotEmpty()) { val dynamicInclude = "master.changelog.json" // Virtual file name of the changelog that includes all schemas. + checkResourcesInClassPath(preV4Baseline) dataSource.connection.use { connection -> val customResourceAccessor = CustomResourceAccessor(dynamicInclude, preV4Baseline, classLoader) val liquibase = Liquibase(dynamicInclude, customResourceAccessor, getLiquibaseDatabase(JdbcConnection(connection))) @@ -217,6 +218,14 @@ class SchemaMigration( } return isExistingDBWithoutLiquibase || isFinanceAppWithLiquibaseNotMigrated } + + private fun checkResourcesInClassPath(resources: List) { + for (resource in resources) { + if (resource != null && classLoader.getResource(resource) == null) { + throw DatabaseMigrationException("Could not find Liquibase database migration script $resource. Please ensure the jar file containing it is deployed in the cordapps directory.") + } + } + } } open class DatabaseMigrationException(message: String) : IllegalArgumentException(message) { From d67968fa885343c281d40bbba632457552b9ea4f Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Thu, 25 Apr 2019 17:23:11 +0100 Subject: [PATCH 35/75] CORDA-2632 - Handle exceptions when file does not exist. (#4967) * corda-2632 Added catch clause to handle argument exceptions and display the proper error. * Created specific exception for when the file is missing instead of the general catch all exception. * Changes according to code review. --- .../net/corda/client/jackson/StringToMethodCallParser.kt | 5 ++++- .../main/kotlin/net/corda/tools/shell/InteractiveShell.kt | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/client/jackson/src/main/kotlin/net/corda/client/jackson/StringToMethodCallParser.kt b/client/jackson/src/main/kotlin/net/corda/client/jackson/StringToMethodCallParser.kt index fb4becec14..3ea6fc8452 100644 --- a/client/jackson/src/main/kotlin/net/corda/client/jackson/StringToMethodCallParser.kt +++ b/client/jackson/src/main/kotlin/net/corda/client/jackson/StringToMethodCallParser.kt @@ -127,7 +127,7 @@ open class StringToMethodCallParser @JvmOverloads constructor( return method.parameters.mapIndexed { index, param -> when { param.isNamePresent -> param.name - // index + 1 because the first Kotlin reflection param is 'this', but that doesn't match Java reflection. + // index + 1 because the first Kotlin reflection param is 'this', but that doesn't match Java reflection. kf != null -> kf.parameters[index + 1].name ?: throw UnparseableCallException.ReflectionDataMissing(method.name, index) else -> throw UnparseableCallException.ReflectionDataMissing(method.name, index) } @@ -153,6 +153,7 @@ open class StringToMethodCallParser @JvmOverloads constructor( class MissingParameter(methodName: String, val paramName: String, command: String) : UnparseableCallException("Parameter $paramName missing from attempt to invoke $methodName in command: $command") class TooManyParameters(methodName: String, command: String) : UnparseableCallException("Too many parameters provided for $methodName: $command") class ReflectionDataMissing(methodName: String, argIndex: Int) : UnparseableCallException("Method $methodName missing parameter name at index $argIndex") + class NoSuchFile(filename: String) : UnparseableCallException("File $filename not found") class FailedParse(e: Exception) : UnparseableCallException(e.message ?: e.toString(), e) } @@ -200,6 +201,8 @@ open class StringToMethodCallParser @JvmOverloads constructor( val entryType = om.typeFactory.constructType(argType) try { om.readValue(entry.traverse(om), entryType) + } catch (e: java.nio.file.NoSuchFileException) { + throw UnparseableCallException.NoSuchFile(e.file ?: entry.toString()) } catch (e: Exception) { throw UnparseableCallException.FailedParse(e) } diff --git a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt index 86444f179f..94c8091bcf 100644 --- a/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt +++ b/tools/shell/src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt @@ -499,7 +499,9 @@ object InteractiveShell { } } catch (e: StringToMethodCallParser.UnparseableCallException) { out.println(e.message, Color.red) - out.println("Please try 'man run' to learn what syntax is acceptable") + if (e !is StringToMethodCallParser.UnparseableCallException.NoSuchFile) { + out.println("Please try 'man run' to learn what syntax is acceptable") + } } catch (e: Exception) { out.println("RPC failed: ${e.rootCause}", Color.red) } finally { From 2f7a2babb42d359d3a8e39ba54d4476e9a83a1c3 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Mon, 15 Apr 2019 11:30:47 +0100 Subject: [PATCH 36/75] CORDA-2602 - Update getting setup guide java details (#5019) Remove some information that is way to specific for installing the oracle jdk. Add mention of zulu openjdk to the docs. Specify that the docs will go through how to install the oracle jdk only but provide links to amazon corretto, openjdk and zulu. Also say that we support intellij 2019 which has been released now. --- docs/source/getting-set-up.rst | 56 ++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/docs/source/getting-set-up.rst b/docs/source/getting-set-up.rst index 7e727046fb..62afd07b70 100644 --- a/docs/source/getting-set-up.rst +++ b/docs/source/getting-set-up.rst @@ -6,10 +6,22 @@ Software requirements Corda uses industry-standard tools: -* **Java 8 JVM** - we require at least version **|java_version|**, but do not currently support Java 9 or higher. - We have tested with Oracle JDK, Amazon Corretto, and Red Hat's OpenJDK builds. Please note that OpenJDK builds - usually exclude JavaFX, which our GUI tools require. -* **IntelliJ IDEA** - supported versions **2017.x** and **2018.x** (with Kotlin plugin version |kotlin_version|) +* **Java 8 JVM** - we require at least version |java_version|, but do not currently support Java 9 or higher. + + We have tested with the following builds: + + * `Oracle JDK `_ + + * `Amazon Corretto `_ + + * `Red Hat's OpenJDK `_ + + * `Zulu's OpenJDK `_ + + Please note that OpenJDK builds usually exclude JavaFX, which our GUI tools require. + +* **IntelliJ IDEA** - supported versions **2017.x**, **2018.x** and **2019.x** (with Kotlin plugin version |kotlin_version|) + * **Gradle** - we use 4.10 and the ``gradlew`` script in the project / samples directories will download it for you. Please note: @@ -46,6 +58,16 @@ The set-up instructions are available for the following platforms: .. _windows-label: +.. note:: These setup instructions will guide you on how to install the Oracle JDK. Each JDK can be found on their respective sites: + + * `Oracle `_ + + * `Amazon Corretto `_ + + * `Red Hat OpenJDK `_ + + * `Zulu OpenJDK `_ + Windows ------- @@ -54,12 +76,10 @@ Windows Java ^^^^ 1. Visit http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html -2. Scroll down to "Java SE Development Kit 8uXXX" (where "XXX" is the latest minor version number) -3. Toggle "Accept License Agreement" -4. Click the download link for jdk-8uXXX-windows-x64.exe (where "XXX" is the latest minor version number) -5. Download and run the executable to install Java (use the default settings) -6. Add Java to the PATH environment variable by following the instructions at https://docs.oracle.com/javase/7/docs/webnotes/install/windows/jdk-installation-windows.html#path -7. Open a new command prompt and run ``java -version`` to test that Java is installed correctly +2. Click the download link for jdk-8uXXX-windows-x64.exe (where "XXX" is the latest minor version number) +3. Download and run the executable to install Java (use the default settings) +4. Add Java to the PATH environment variable by following the instructions in the `Oracle documentation `_ +5. Open a new command prompt and run ``java -version`` to test that Java is installed correctly Git ^^^ @@ -72,7 +92,7 @@ IntelliJ ^^^^^^^^ 1. Visit https://www.jetbrains.com/idea/download/download-thanks.html?code=IIC 2. Download and run the executable to install IntelliJ Community Edition (use the default settings) -3. Ensure the Kotlin plugin in Intellij is updated to version |kotlin_version| +3. Ensure the Kotlin plugin in Intellij is updated to version |kotlin_version| (new installs will contains this version) .. _mac-label: @@ -84,17 +104,15 @@ Mac Java ^^^^ 1. Visit http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html -2. Scroll down to "Java SE Development Kit 8uXXX" (where "XXX" is the latest minor version number) -3. Toggle "Accept License Agreement" -4. Click the download link for jdk-8uXXX-macosx-x64.dmg (where "XXX" is the latest minor version number) -5. Download and run the executable to install Java (use the default settings) -6. Open a new terminal window and run ``java -version`` to test that Java is installed correctly +2. Click the download link for jdk-8uXXX-macosx-x64.dmg (where "XXX" is the latest minor version number) +3. Download and run the executable to install Java (use the default settings) +4. Open a new terminal window and run ``java -version`` to test that Java is installed correctly IntelliJ ^^^^^^^^ 1. Visit https://www.jetbrains.com/idea/download/download-thanks.html?platform=mac&code=IIC 2. Download and run the executable to install IntelliJ Community Edition (use the default settings) -3. Ensure the Kotlin plugin in Intellij is updated to version |kotlin_version| +3. Ensure the Kotlin plugin in Intellij is updated to version |kotlin_version| (new installs will contains this version) .. _deb-ubuntu-label: @@ -123,7 +141,7 @@ Jetbrains offers a pre-built snap package that allows for easy, one-step install 1. To download the snap, navigate to https://snapcraft.io/intellij-idea-community 2. Click ``Install``, then ``View in Desktop Store``. Choose ``Ubuntu Software`` in the Launch Application window. -3. Ensure the Kotlin plugin in Intellij is updated to version |kotlin_version| +3. Ensure the Kotlin plugin in Intellij is updated to version |kotlin_version| (new installs will contains this version) .. _fedora-label: @@ -151,7 +169,7 @@ IntelliJ 1. Visit https://www.jetbrains.com/idea/download/download-thanks.html?platform=linux&code=IIC 2. Unpack the ``tar.gz`` file using the following command ``tar xfz ideaIC-.tar.gz -C /opt`` 3. Run IntelliJ with ``/opt/ideaIC-/bin/idea.sh`` -4. Ensure the Kotlin plugin in IntelliJ is updated to version |kotlin_version| +4. Ensure the Kotlin plugin in IntelliJ is updated to version |kotlin_version| (new installs will contains this version) Next steps From 1802e48963cdcfa6d34f44d6a40f2897d067aaa8 Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Tue, 9 Apr 2019 20:14:37 +0100 Subject: [PATCH 37/75] CORDA-2586 - explorer exception handling (#4957) * Initial version of new(old) dialog that won't print a stacktrace for rpc exceptions. * Decoupled CordaVersionProvider. Moved common files to common-logging to lower dependencies on the node explorer. * Removed unused import and duplicate documentation comment. * Moved error code rewrite policy in the new common/logging module according to PR review. * Removed extra line. * Updated log4j configurations with new package name where logging policies will be contained. * Included common-logging module with cliutils. --- common/logging/build.gradle | 26 +++++++ .../net/corda/common/logging/CordaVersion.kt | 28 +++++++ .../common/logging}/ErrorCodeRewritePolicy.kt | 2 +- .../logging}/ExceptionsErrorCodeFunctions.kt | 8 +- config/dev/log4j2.xml | 2 +- node/build.gradle | 1 + .../net/corda/node/internal/NodeStartup.kt | 8 +- settings.gradle | 4 + testing/test-common/build.gradle | 1 + .../src/main/resources/log4j2-test.xml | 2 +- tools/blobinspector/build.gradle | 1 + tools/bootstrapper/build.gradle | 1 + tools/cliutils/build.gradle | 1 + .../corda/cliutils/CordaVersionProvider.kt | 23 +----- .../cliutils/InstallShellExtensionsParser.kt | 3 +- tools/explorer/build.gradle | 1 + .../explorer/ui/AdvancedExceptionDialog.kt | 77 +++++++++++++++++++ .../net/corda/explorer/views/LoginView.kt | 10 ++- tools/shell-cli/build.gradle | 1 + webserver/build.gradle | 1 + 20 files changed, 167 insertions(+), 34 deletions(-) create mode 100644 common/logging/build.gradle create mode 100644 common/logging/src/main/kotlin/net/corda/common/logging/CordaVersion.kt rename {tools/cliutils/src/main/kotlin/net/corda/cliutils => common/logging/src/main/kotlin/net/corda/common/logging}/ErrorCodeRewritePolicy.kt (96%) rename {tools/cliutils/src/main/kotlin/net/corda/cliutils => common/logging/src/main/kotlin/net/corda/common/logging}/ExceptionsErrorCodeFunctions.kt (83%) create mode 100644 tools/explorer/src/main/kotlin/net/corda/explorer/ui/AdvancedExceptionDialog.kt diff --git a/common/logging/build.gradle b/common/logging/build.gradle new file mode 100644 index 0000000000..e778d934c6 --- /dev/null +++ b/common/logging/build.gradle @@ -0,0 +1,26 @@ +apply plugin: 'kotlin' + +apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'com.jfrog.artifactory' + +dependencies { + compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: kotlin_version + compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version + + compile group: "com.typesafe", name: "config", version: typesafe_config_version + compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" + + + compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version" + + + testCompile project(":test-utils") +} + +jar { + baseName 'corda-common-logging' +} + +publish { + name jar.baseName +} \ No newline at end of file diff --git a/common/logging/src/main/kotlin/net/corda/common/logging/CordaVersion.kt b/common/logging/src/main/kotlin/net/corda/common/logging/CordaVersion.kt new file mode 100644 index 0000000000..caac7faf72 --- /dev/null +++ b/common/logging/src/main/kotlin/net/corda/common/logging/CordaVersion.kt @@ -0,0 +1,28 @@ +package net.corda.common.logging + +import com.jcabi.manifests.Manifests + +class CordaVersion { + companion object { + private const val UNKNOWN = "Unknown" + const val current_major_release = "4.0-SNAPSHOT" + const val platformEditionCode = "OS" + + private fun manifestValue(name: String): String? = if (Manifests.exists(name)) Manifests.read(name) else null + + val releaseVersion: String by lazy { manifestValue("Corda-Release-Version") ?: UNKNOWN } + val revision: String by lazy { manifestValue("Corda-Revision") ?: UNKNOWN } + val vendor: String by lazy { manifestValue("Corda-Vendor") ?: UNKNOWN } + val platformVersion: Int by lazy { manifestValue("Corda-Platform-Version")?.toInt() ?: 1 } + + internal val semanticVersion: String by lazy { if(releaseVersion == UNKNOWN) current_major_release else releaseVersion } + } + + fun getVersion(): Array { + return if (Manifests.exists("Corda-Release-Version") && Manifests.exists("Corda-Revision")) { + arrayOf("Version: $releaseVersion", "Revision: $revision", "Platform Version: $platformVersion", "Vendor: $vendor") + } else { + arrayOf("No version data is available in the MANIFEST file.") + } + } +} \ No newline at end of file diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/ErrorCodeRewritePolicy.kt b/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt similarity index 96% rename from tools/cliutils/src/main/kotlin/net/corda/cliutils/ErrorCodeRewritePolicy.kt rename to common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt index dd0d587339..5f0e24bef9 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/ErrorCodeRewritePolicy.kt +++ b/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt @@ -1,4 +1,4 @@ -package net.corda.cliutils +package net.corda.common.logging import org.apache.logging.log4j.core.Core import org.apache.logging.log4j.core.LogEvent diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/ExceptionsErrorCodeFunctions.kt b/common/logging/src/main/kotlin/net/corda/common/logging/ExceptionsErrorCodeFunctions.kt similarity index 83% rename from tools/cliutils/src/main/kotlin/net/corda/cliutils/ExceptionsErrorCodeFunctions.kt rename to common/logging/src/main/kotlin/net/corda/common/logging/ExceptionsErrorCodeFunctions.kt index ec7d74fb0a..5fea6054e4 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/ExceptionsErrorCodeFunctions.kt +++ b/common/logging/src/main/kotlin/net/corda/common/logging/ExceptionsErrorCodeFunctions.kt @@ -1,11 +1,11 @@ -package net.corda.cliutils +package net.corda.common.logging import org.apache.logging.log4j.Level import org.apache.logging.log4j.message.Message import org.apache.logging.log4j.message.SimpleMessage import java.util.* -internal fun Message.withErrorCodeFor(error: Throwable?, level: Level): Message { +fun Message.withErrorCodeFor(error: Throwable?, level: Level): Message { return when { error != null && level.isInRange(Level.FATAL, Level.WARN) -> CompositeMessage("$formattedMessage [errorCode=${error.errorCode()}, moreInformationAt=${error.errorCodeLocationUrl()}]", format, parameters, throwable) @@ -13,9 +13,9 @@ internal fun Message.withErrorCodeFor(error: Throwable?, level: Level): Message } } -private fun Throwable.errorCodeLocationUrl() = "https://errors.corda.net/${CordaVersionProvider.platformEditionCode}/${CordaVersionProvider.semanticVersion}/${errorCode()}" +fun Throwable.errorCodeLocationUrl() = "https://errors.corda.net/${CordaVersion.platformEditionCode}/${CordaVersion.semanticVersion}/${errorCode()}" -private fun Throwable.errorCode(hashedFields: (Throwable) -> Array = Throwable::defaultHashedFields): String { +fun Throwable.errorCode(hashedFields: (Throwable) -> Array = Throwable::defaultHashedFields): String { val hash = staticLocationBasedHash(hashedFields) return hash.toBase(36) diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml index c19afbb322..0c63d75373 100644 --- a/config/dev/log4j2.xml +++ b/config/dev/log4j2.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} diff --git a/node/build.gradle b/node/build.gradle index bcd228fae2..8357d4b3c9 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -73,6 +73,7 @@ dependencies { compile project(':tools:cliutils') compile project(':common-validation') compile project(':common-configuration-parsing') + compile project(':common-logging') // Backwards compatibility goo: Apps expect confidential-identities to be loaded by default. // We could eventually gate this on a target-version check. diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index 603bb8190f..1a8ae97eb8 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -4,8 +4,8 @@ import io.netty.channel.unix.Errors import net.corda.cliutils.printError import net.corda.cliutils.CliWrapperBase import net.corda.cliutils.CordaCliWrapper -import net.corda.cliutils.CordaVersionProvider import net.corda.cliutils.ExitCodes +import net.corda.common.logging.CordaVersion import net.corda.core.contracts.HashAttachmentConstraint import net.corda.core.crypto.Crypto import net.corda.core.internal.* @@ -269,9 +269,9 @@ open class NodeStartup : NodeStartupLogging { open fun getVersionInfo(): VersionInfo { return VersionInfo( PLATFORM_VERSION, - CordaVersionProvider.releaseVersion, - CordaVersionProvider.revision, - CordaVersionProvider.vendor + CordaVersion.releaseVersion, + CordaVersion.revision, + CordaVersion.vendor ) } diff --git a/settings.gradle b/settings.gradle index a1392c809f..5cf9069aa3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -70,6 +70,10 @@ project(":common-validation").projectDir = new File("$settingsDir/common/validat include 'common-configuration-parsing' project(":common-configuration-parsing").projectDir = new File("$settingsDir/common/configuration-parsing") + + +include 'common-logging' +project(":common-logging").projectDir = new File("$settingsDir/common/logging") // Common libraries - end apply from: 'buildCacheSettings.gradle' diff --git a/testing/test-common/build.gradle b/testing/test-common/build.gradle index 9044d46319..e81dd16090 100644 --- a/testing/test-common/build.gradle +++ b/testing/test-common/build.gradle @@ -6,6 +6,7 @@ dependencies { compile project(':core') compile project(':node-api') compile project(':tools:cliutils') + compile project(":common-logging") // Unit testing helpers. compile "junit:junit:$junit_version" diff --git a/testing/test-common/src/main/resources/log4j2-test.xml b/testing/test-common/src/main/resources/log4j2-test.xml index 221a56127b..d8d489af1f 100644 --- a/testing/test-common/src/main/resources/log4j2-test.xml +++ b/testing/test-common/src/main/resources/log4j2-test.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} diff --git a/tools/blobinspector/build.gradle b/tools/blobinspector/build.gradle index eea34a298f..1e7b6d0b22 100644 --- a/tools/blobinspector/build.gradle +++ b/tools/blobinspector/build.gradle @@ -6,6 +6,7 @@ apply plugin: 'com.jfrog.artifactory' dependencies { compile project(':client:jackson') compile project(':tools:cliutils') + compile project(":common-logging") compile "org.slf4j:jul-to-slf4j:$slf4j_version" compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version" diff --git a/tools/bootstrapper/build.gradle b/tools/bootstrapper/build.gradle index 3e1a4c8ab3..ae708803aa 100644 --- a/tools/bootstrapper/build.gradle +++ b/tools/bootstrapper/build.gradle @@ -7,6 +7,7 @@ description 'Network bootstrapper' dependencies { compile project(':node-api') compile project(':tools:cliutils') + compile project(":common-logging") compile project(':common-configuration-parsing') compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" diff --git a/tools/cliutils/build.gradle b/tools/cliutils/build.gradle index 0044b66fe2..6128b3171d 100644 --- a/tools/cliutils/build.gradle +++ b/tools/cliutils/build.gradle @@ -7,6 +7,7 @@ description 'CLI Utilities' dependencies { compile project(":core") + compile project(":common-logging") compile "info.picocli:picocli:$picocli_version" compile "commons-io:commons-io:$commons_io_version" diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaVersionProvider.kt b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaVersionProvider.kt index 0231f9d1d5..de95c7af3b 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaVersionProvider.kt +++ b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaVersionProvider.kt @@ -1,33 +1,16 @@ package net.corda.cliutils -import com.jcabi.manifests.Manifests import picocli.CommandLine +import net.corda.common.logging.CordaVersion /** * Simple version printing when command is called with --version or -V flag. Assuming that we reuse Corda-Release-Version and Corda-Revision * in the manifest file. */ class CordaVersionProvider : CommandLine.IVersionProvider { - companion object { - private const val UNKNOWN = "Unknown" - const val current_major_release = "4.0-SNAPSHOT" - const val platformEditionCode = "OS" - - private fun manifestValue(name: String): String? = if (Manifests.exists(name)) Manifests.read(name) else null - - val releaseVersion: String by lazy { manifestValue("Corda-Release-Version") ?: UNKNOWN } - val revision: String by lazy { manifestValue("Corda-Revision") ?: UNKNOWN } - val vendor: String by lazy { manifestValue("Corda-Vendor") ?: UNKNOWN } - val platformVersion: Int by lazy { manifestValue("Corda-Platform-Version")?.toInt() ?: 1 } - - internal val semanticVersion: String by lazy { if(releaseVersion == UNKNOWN) current_major_release else releaseVersion } - } + val version = CordaVersion() override fun getVersion(): Array { - return if (Manifests.exists("Corda-Release-Version") && Manifests.exists("Corda-Revision")) { - arrayOf("Version: $releaseVersion", "Revision: $revision", "Platform Version: $platformVersion", "Vendor: $vendor") - } else { - arrayOf("No version data is available in the MANIFEST file.") - } + return version.getVersion() } } \ No newline at end of file diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/InstallShellExtensionsParser.kt b/tools/cliutils/src/main/kotlin/net/corda/cliutils/InstallShellExtensionsParser.kt index 32b26401b6..68a682e5e1 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/InstallShellExtensionsParser.kt +++ b/tools/cliutils/src/main/kotlin/net/corda/cliutils/InstallShellExtensionsParser.kt @@ -10,6 +10,7 @@ import java.nio.file.Path import java.nio.file.Paths import java.nio.file.StandardCopyOption import java.util.* +import net.corda.common.logging.CordaVersion private class ShellExtensionsGenerator(val parent: CordaCliWrapper) { private companion object { @@ -72,7 +73,7 @@ private class ShellExtensionsGenerator(val parent: CordaCliWrapper) { // If on Windows, Path.toString() returns a path with \ instead of /, but for bash Windows users we want to convert those back to /'s private fun Path.toStringWithDeWindowsfication(): String = this.toAbsolutePath().toString().replace("\\", "/") - private fun jarVersion(alias: String) = "# $alias - Version: ${CordaVersionProvider.releaseVersion}, Revision: ${CordaVersionProvider.revision}" + private fun jarVersion(alias: String) = "# $alias - Version: ${CordaVersion.releaseVersion}, Revision: ${CordaVersion.revision}" private fun getAutoCompleteFileLocation(alias: String) = userHome / ".completion" / alias private fun generateAutoCompleteFile(alias: String) { diff --git a/tools/explorer/build.gradle b/tools/explorer/build.gradle index 8f6f77bfa4..add342ec68 100644 --- a/tools/explorer/build.gradle +++ b/tools/explorer/build.gradle @@ -19,6 +19,7 @@ dependencies { compile project(':finance:contracts') compile project(':finance:workflows') compile project(':tools:worldmap') + compile project(':common-logging') // Log4J: logging framework (with SLF4J bindings) compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/ui/AdvancedExceptionDialog.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/ui/AdvancedExceptionDialog.kt new file mode 100644 index 0000000000..82706d2ca6 --- /dev/null +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/ui/AdvancedExceptionDialog.kt @@ -0,0 +1,77 @@ +package net.corda.explorer.ui + + +import impl.org.controlsfx.i18n.Localization.getString +import impl.org.controlsfx.i18n.Localization.localize +import javafx.event.ActionEvent +import javafx.event.EventHandler +import net.corda.common.logging.errorCodeLocationUrl +import org.controlsfx.dialog.ProgressDialog +import javafx.scene.control.* +import javafx.scene.layout.GridPane +import javafx.scene.layout.Priority +import javafx.scene.text.Text +import javafx.scene.text.TextFlow +import java.awt.Desktop +import java.io.PrintWriter +import java.io.StringWriter +import java.net.URI + +/* + Will generate a window showing the exception message with a generated link and if requested a stacktrace. + The link opens the default browser towards the error.corda.com/ redirection pages. + */ +class AdvancedExceptionDialog(_exception: Throwable) : Dialog() { + + internal val exception = _exception + + init { + val dialogPane = super.getDialogPane() + + //Dialog title + super.setTitle(getString("exception.dlg.title")) + dialogPane.headerText = getString("exception.dlg.header") + dialogPane.styleClass.add("exception-dialog") + dialogPane.stylesheets.add(ProgressDialog::class.java.getResource("dialogs.css").toExternalForm()) + dialogPane.buttonTypes.addAll(ButtonType.OK) + + + val hyperlink = Hyperlink(exception.errorCodeLocationUrl()) + hyperlink.onAction = EventHandler { + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + Desktop.getDesktop().browse( URI(exception.errorCodeLocationUrl())) + } //This should be tested out on other platforms, works on my mac but the stackoverflow opinions are mixed. + } + + + val textFlow = TextFlow(Text("${exception.message}\n"), hyperlink) + + dialogPane.content = textFlow + } +} + +//Attach a stacktrace for the exception that was used in the initialization of the dialog. +fun AdvancedExceptionDialog.withStacktrace() : AdvancedExceptionDialog +{ + val sw = StringWriter() + val pw = PrintWriter(sw) + exception.printStackTrace(pw) + val textArea = TextArea(sw.toString()).apply { + isEditable = false + isWrapText = false + maxWidth = Double.MAX_VALUE + maxHeight = Double.MAX_VALUE + } + + GridPane.setVgrow(textArea, Priority.ALWAYS) + GridPane.setHgrow(textArea, Priority.ALWAYS) + + val root = GridPane().apply { + maxWidth = Double.MAX_VALUE + add(Label(localize(getString("exception.dlg.label"))), 0, 0) + add(textArea,0 ,1) + } + + dialogPane.expandableContent = root + return this +} \ No newline at end of file diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/LoginView.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/LoginView.kt index 440d1f0c08..082f738c8a 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/LoginView.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/LoginView.kt @@ -4,9 +4,11 @@ import javafx.beans.property.SimpleIntegerProperty import javafx.scene.control.* import net.corda.client.jfx.model.NodeMonitorModel import net.corda.client.jfx.model.objectProperty +import net.corda.client.rpc.RPCException import net.corda.core.utilities.NetworkHostAndPort import net.corda.explorer.model.SettingsModel -import org.controlsfx.dialog.ExceptionDialog +import net.corda.explorer.ui.AdvancedExceptionDialog +import net.corda.explorer.ui.withStacktrace import tornadofx.* import kotlin.system.exitProcess @@ -50,10 +52,14 @@ class LoginView : View(WINDOW_TITLE) { } getModel().commit() LoginStatus.loggedIn + } catch (e: RPCException) { + e.printStackTrace() + AdvancedExceptionDialog(e).apply { initOwner(root.scene.window) }.showAndWait() + LoginStatus.exception } catch (e: Exception) { // TODO : Handle this in a more user friendly way. e.printStackTrace() - ExceptionDialog(e).apply { initOwner(root.scene.window) }.showAndWait() + AdvancedExceptionDialog(e).withStacktrace().apply { initOwner(root.scene.window) }.showAndWait() LoginStatus.exception } finally { root.isDisable = false diff --git a/tools/shell-cli/build.gradle b/tools/shell-cli/build.gradle index bb1b7b6bb9..303c582eb2 100644 --- a/tools/shell-cli/build.gradle +++ b/tools/shell-cli/build.gradle @@ -11,6 +11,7 @@ apply plugin: 'com.jfrog.artifactory' dependencies { compile project(':tools:shell') compile project(':tools:cliutils') + compile project(":common-logging") compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" compile "org.slf4j:jul-to-slf4j:$slf4j_version" diff --git a/webserver/build.gradle b/webserver/build.gradle index 204fbe94dc..ca228a9800 100644 --- a/webserver/build.gradle +++ b/webserver/build.gradle @@ -29,6 +29,7 @@ dependencies { compile project(':client:rpc') compile project(':client:jackson') compile project(':tools:cliutils') + compile project(":common-logging") // Web stuff: for HTTP[S] servlets compile "org.eclipse.jetty:jetty-servlet:$jetty_version" From fa380123395325ee58d75735d0e03a20b3e7b69e Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Wed, 24 Apr 2019 11:45:10 +0100 Subject: [PATCH 38/75] CORDA-2572 - Add peer information to stacktrace of received FlowException (#4998) When a `UnexpectedFlowEndException` or a `FlowException` is received the peer that the exception was thrown from will be added to the stacktrace. This is due to it being easier to see and a field that developers are much less likely to override. A nullable field `peer` has been added to `FlowException` and `UnexpectedFlowEndException`. This is read later on (when peer info is not available) to append the peer info to the stacktrace. --- .../net/corda/core/flows/FlowException.kt | 7 ++++ .../statemachine/FlowStateMachineImpl.kt | 38 +++++++++++++++++-- .../DeliverSessionMessageTransition.kt | 13 +++++++ .../statemachine/FlowFrameworkTests.kt | 29 ++++++++++++++ 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowException.kt b/core/src/main/kotlin/net/corda/core/flows/FlowException.kt index 80a648227a..6395961e06 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowException.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowException.kt @@ -2,6 +2,7 @@ package net.corda.core.flows import net.corda.core.CordaException import net.corda.core.CordaRuntimeException +import net.corda.core.identity.Party // DOCSTART 1 /** @@ -28,6 +29,9 @@ open class FlowException(message: String?, cause: Throwable?, var originalErrorI constructor(cause: Throwable?) : this(cause?.toString(), cause) constructor() : this(null, null) + // private field with obscure name to ensure it is not overridden + private var peer: Party? = null + override fun getErrorId(): Long? = originalErrorId } // DOCEND 1 @@ -42,5 +46,8 @@ class UnexpectedFlowEndException(message: String, cause: Throwable?, val origina constructor(message: String, cause: Throwable?) : this(message, cause, null) constructor(message: String) : this(message, null) + // private field with obscure name to ensure it is not overridden + private var peer: Party? = null + override fun getErrorId(): Long? = originalErrorId } diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt index 9353f8fb7f..36a4e1ecb0 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowStateMachineImpl.kt @@ -159,10 +159,7 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, is FlowContinuation.Resume -> { return continuation.result } - is FlowContinuation.Throw -> { - continuation.throwable.fillInStackTrace() - throw continuation.throwable - } + is FlowContinuation.Throw -> throw continuation.throwable.fillInLocalStackTrace() FlowContinuation.ProcessEvents -> continue@eventLoop FlowContinuation.Abort -> abortFiber() } @@ -173,6 +170,39 @@ class FlowStateMachineImpl(override val id: StateMachineRunId, } } + private fun Throwable.fillInLocalStackTrace(): Throwable { + fillInStackTrace() + // provide useful information that can be displayed to the user + // reflection use to access private field + when (this) { + is UnexpectedFlowEndException -> { + DeclaredField(UnexpectedFlowEndException::class.java, "peer", this).value?.let { + stackTrace = arrayOf( + StackTraceElement( + "Received unexpected counter-flow exception from peer ${it.name}", + "", + "", + -1 + ) + ) + stackTrace + } + } + is FlowException -> { + DeclaredField(FlowException::class.java, "peer", this).value?.let { + stackTrace = arrayOf( + StackTraceElement( + "Received counter-flow exception from peer ${it.name}", + "", + "", + -1 + ) + ) + stackTrace + } + } + } + return this + } + /** * Immediately processes the passed in event. Always called with an open database transaction. * diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/transitions/DeliverSessionMessageTransition.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/transitions/DeliverSessionMessageTransition.kt index a16c212d8e..127f23f1e2 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/transitions/DeliverSessionMessageTransition.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/transitions/DeliverSessionMessageTransition.kt @@ -1,6 +1,10 @@ package net.corda.node.services.statemachine.transitions +import net.corda.core.flows.FlowException import net.corda.core.flows.UnexpectedFlowEndException +import net.corda.core.identity.Party +import net.corda.core.internal.DeclaredField +import net.corda.core.internal.declaredField import net.corda.node.services.statemachine.Action import net.corda.node.services.statemachine.ConfirmSessionMessage import net.corda.node.services.statemachine.DataSessionMessage @@ -120,6 +124,15 @@ class DeliverSessionMessageTransition( return when (sessionState) { is SessionState.Initiated -> { + when (exception) { + // reflection used to access private field + is UnexpectedFlowEndException -> DeclaredField( + UnexpectedFlowEndException::class.java, + "peer", + exception + ).value = sessionState.peerParty + is FlowException -> DeclaredField(FlowException::class.java, "peer", exception).value = sessionState.peerParty + } val checkpoint = currentState.checkpoint val sessionId = event.sessionMessage.recipientSessionId val flowError = FlowError(payload.errorId, exception) diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index b31c446986..ccfd98962e 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -11,6 +11,7 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.random63BitValue import net.corda.core.flows.* import net.corda.core.identity.Party +import net.corda.core.internal.DeclaredField import net.corda.core.internal.concurrent.flatMap import net.corda.core.messaging.MessageRecipients import net.corda.core.node.services.PartyInfo @@ -38,6 +39,7 @@ import net.corda.testing.node.internal.* import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType +import org.assertj.core.api.Condition import org.junit.After import org.junit.Before import org.junit.Test @@ -45,6 +47,7 @@ import rx.Notification import rx.Observable import java.time.Instant import java.util.* +import java.util.function.Predicate import kotlin.reflect.KClass import kotlin.test.assertFailsWith @@ -186,6 +189,7 @@ class FlowFrameworkTests { .isThrownBy { receivingFiber.resultFuture.getOrThrow() } .withMessage("Nothing useful") .withStackTraceContaining(ReceiveFlow::class.java.name) // Make sure the stack trace is that of the receiving flow + .withStackTraceContaining("Received counter-flow exception from peer") bobNode.database.transaction { assertThat(bobNode.internals.checkpointStorage.checkpoints()).isEmpty() } @@ -208,6 +212,29 @@ class FlowFrameworkTests { assertThat((lastMessage.payload as ErrorSessionMessage).flowException!!.stackTrace).isEmpty() } + @Test + fun `sub-class of FlowException can have a peer field without causing serialisation problems`() { + val exception = MyPeerFlowException("Nothing useful", alice) + bobNode.registerCordappFlowFactory(ReceiveFlow::class) { + ExceptionFlow { exception } + } + + val receivingFiber = aliceNode.services.startFlow(ReceiveFlow(bob)) as FlowStateMachineImpl + + mockNet.runNetwork() + + assertThatExceptionOfType(MyPeerFlowException::class.java) + .isThrownBy { receivingFiber.resultFuture.getOrThrow() } + .has(Condition(Predicate { it.peer == alice }, "subclassed peer field has original value")) + .has(Condition(Predicate { + DeclaredField( + FlowException::class.java, + "peer", + it + ).value == bob + }, "FlowException's private peer field has value set")) + } + private class ConditionalExceptionFlow(val otherPartySession: FlowSession, val sendPayload: Any) : FlowLogic() { @Suspendable override fun call() { @@ -732,6 +759,8 @@ internal class MyFlowException(override val message: String) : FlowException() { override fun hashCode(): Int = message.hashCode() } +internal class MyPeerFlowException(override val message: String, val peer: Party) : FlowException() + @InitiatingFlow internal class SendAndReceiveFlow(private val otherParty: Party, private val payload: Any, private val otherPartySession: FlowSession? = null) : FlowLogic() { constructor(otherPartySession: FlowSession, payload: Any) : this(otherPartySession.counterparty, payload, otherPartySession) From 97f31ef0f34453493eb9f096e9e3f7a5b905f05f Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Wed, 1 May 2019 17:09:26 +0100 Subject: [PATCH 39/75] CORDA-2884 - Docker build tasks will pull the corda jar from artifactory. (#5067) (#5071) * Changed corda jar to be pulled from artifactory. * Changes according to PR review. --- docker/build.gradle | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docker/build.gradle b/docker/build.gradle index 2891b251ab..886eeae381 100644 --- a/docker/build.gradle +++ b/docker/build.gradle @@ -16,9 +16,22 @@ apply plugin: 'application' mainClassName = 'net.corda.core.ConfigExporterMain' apply plugin: 'com.github.johnrengelman.shadow' +repositories { + maven { + url "${artifactory_contextUrl}/corda-releases" + } + maven { + url "${artifactory_contextUrl}/corda-dev" + } +} + +configurations { + artifactoryCorda +} dependencies{ compile project(':node') + artifactoryCorda "net.corda:corda:${project.version}" } shadowJar { @@ -38,11 +51,11 @@ docker{ task buildDockerFolder(dependsOn: [":node:capsule:buildCordaJAR", shadowJar]) { doLast { - def cordaJar = project(":node:capsule").buildCordaJAR.archivePath + def cordaJar = configurations.artifactoryCorda.singleFile project.copy { into new File(project.buildDir, "docker-temp") from "src/bash/run-corda.sh" - from cordaJar + from cordaJar.path from shadowJar.archivePath from "src/config/starting-node.conf" from "src/bash/generate-config.sh" From fe41d9b4bb3eee9860e09dc404f871ee47c2efd3 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Fri, 3 May 2019 13:45:47 +0100 Subject: [PATCH 40/75] DOCS - Add documentation on Corda Services / Service classes (#5079) (#5096) (cherry picked from commit a9a0f422affa9d1c4efefdc63b05382cc663b10d) --- docs/source/api-service-classes.rst | 100 ++++++++++++++++++++++++++++ docs/source/corda-api.rst | 1 + 2 files changed, 101 insertions(+) create mode 100644 docs/source/api-service-classes.rst diff --git a/docs/source/api-service-classes.rst b/docs/source/api-service-classes.rst new file mode 100644 index 0000000000..e9e7098c90 --- /dev/null +++ b/docs/source/api-service-classes.rst @@ -0,0 +1,100 @@ +.. highlight:: kotlin +.. raw:: html + + + + +API: Service Classes +==================== + +Service classes are long-lived instances that can trigger or be triggered by flows from within a node. A Service class is limited to a +single instance per node. During startup, the node handles the creation of the service. + +Services allow related, reusable, functions to be separated into their own class where their functionality is +grouped together. These functions can then be called from other services or flows. + +Creating a Service +------------------ + +To define a Service class: + + * Add the ``CordaService`` annotation + * Add a constructor with a single parameter of ``AppServiceHub`` + * Extend ``SingletonSerializeAsToken`` + +Below is an empty implementation of a Service class: + +.. container:: codeset + + .. sourcecode:: kotlin + + @CordaService + class MyCordaService(private val serviceHub: AppServiceHub) : SingletonSerializeAsToken() { + + init { + // code ran at service creation / node startup + } + + // public api of service + } + + .. sourcecode:: java + + @CordaService + public class MyCordaService extends SingletonSerializeAsToken { + + private AppServiceHub serviceHub; + + public MyCordaService(AppServiceHub serviceHub) { + this.serviceHub = serviceHub; + // code ran at service creation / node startup + } + + // public api of service + } + +The ``AppServiceHub`` provides the ``ServiceHub`` functionality to the Service class, with the extra ability to start flows. Starting flows +from ``AppServiceHub`` is explained further in :ref:`Starting Flows from a Service `. + +Code can be run during node startup when the class is being initialised. To do so, place the code into the ``init`` block or constructor. +This is useful when a service needs to establish a connection to an external database or setup observables via ``ServiceHub.trackBy`` during +its startup. These can then persist during the service's lifetime. + +Retrieving a Service +-------------------- + +A Service class can be retrieved by calling ``ServiceHub.cordaService`` which returns the single instance of the class passed into the function: + +.. container:: codeset + + .. sourcecode:: kotlin + + val service: MyCordaService = serviceHub.cordaService(MyCordaService::class.java) + + .. sourcecode:: java + + MyCordaService service = serviceHub.cordaService(MyCordaService.class); + +.. warning:: ``ServiceHub.cordaService`` should not be called during initialisation of a flow and should instead be called in line where + needed or set after the flow's ``call`` function has been triggered. + +.. _starting_flows_from_a_service: + +Starting Flows from a Service +----------------------------- + +Starting flows via a service can lead to deadlock within the node's flow worker queue, which will prevent new flows from +starting. To avoid this, the rules bellow should be followed: + + * When called from a running flow, the service must invoke the new flow from another thread. The existing flow cannot await the + execution of the new flow. + * When ``ServiceHub.trackBy`` is placed inside the service, flows started inside the observable must be placed onto another thread. + * Flows started by other means, do not require any special treatment. + +.. note:: It is possible to avoid deadlock without following these rules depending on the number of flows running within the node. But, if the + number of flows violating these rules reaches the flow worker queue size, then the node will deadlock. It is best practice to + abide by these rules to remove this possibility. + + + + diff --git a/docs/source/corda-api.rst b/docs/source/corda-api.rst index 4321741dd4..8e36ae359b 100644 --- a/docs/source/corda-api.rst +++ b/docs/source/corda-api.rst @@ -15,6 +15,7 @@ The following are the core APIs that are used in the development of CorDapps: api-flows api-identity api-service-hub + api-service-classes api-rpc api-core-types api-testing From 750033540ace946ef0f351c6aa84f40da6683bb7 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Tue, 16 Apr 2019 12:33:45 +0100 Subject: [PATCH 41/75] CORDA-2535 - Fixes to IRS demo (#4995) * CORDA-2535 Move classes from test src to main For some reason a load of classes were in the test src rather than the main src even though they were needed during runtime. These have been moved back into main so they are compiled for use in the demo app. * CORDA-2535 Fix missing trade id irs-demo home page Change `deal.ref` to `deal.common.tradeID` * CORDA-2535 Highlight in irs-demo that `node` module should not be depended on The demo relies on `node` unless some larger refactoring is made. This is being kept as it is, but to discourage developers from doing the same in their CorDapps, a comment has been added to `build.gradle` explaining why it is there and that developers should not do the same. --- samples/irs-demo/cordapp/workflows-irs/build.gradle | 4 ++++ .../kotlin/net.corda.irs}/flows/UpdateBusinessDayFlow.kt | 0 .../kotlin/net/corda/irs/web/api/InterestRatesSwapDemoAPI.kt | 0 samples/irs-demo/web/src/main/resources/static/view/deal.html | 2 +- samples/irs-demo/web/src/main/resources/static/view/home.html | 4 ++-- 5 files changed, 7 insertions(+), 3 deletions(-) rename samples/irs-demo/cordapp/workflows-irs/src/{test/kotlin/net/corda/irs => main/kotlin/net.corda.irs}/flows/UpdateBusinessDayFlow.kt (100%) rename samples/irs-demo/web/src/{test => main}/kotlin/net/corda/irs/web/api/InterestRatesSwapDemoAPI.kt (100%) diff --git a/samples/irs-demo/cordapp/workflows-irs/build.gradle b/samples/irs-demo/cordapp/workflows-irs/build.gradle index 1d4dde4086..553bf50f13 100644 --- a/samples/irs-demo/cordapp/workflows-irs/build.gradle +++ b/samples/irs-demo/cordapp/workflows-irs/build.gradle @@ -14,6 +14,10 @@ dependencies { // Corda integration dependencies cordaCompile project(':core') + + // only included to control the `DemoClock` as part of the demo application + // normally `:node` should not be depended on in any CorDapps + cordaCompile project(':node') // Cordapp dependencies // Specify your cordapp's dependencies below, including dependent cordapps diff --git a/samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt b/samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/UpdateBusinessDayFlow.kt similarity index 100% rename from samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/flows/UpdateBusinessDayFlow.kt rename to samples/irs-demo/cordapp/workflows-irs/src/main/kotlin/net.corda.irs/flows/UpdateBusinessDayFlow.kt diff --git a/samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/api/InterestRatesSwapDemoAPI.kt b/samples/irs-demo/web/src/main/kotlin/net/corda/irs/web/api/InterestRatesSwapDemoAPI.kt similarity index 100% rename from samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/api/InterestRatesSwapDemoAPI.kt rename to samples/irs-demo/web/src/main/kotlin/net/corda/irs/web/api/InterestRatesSwapDemoAPI.kt diff --git a/samples/irs-demo/web/src/main/resources/static/view/deal.html b/samples/irs-demo/web/src/main/resources/static/view/deal.html index 30feb20acd..03cee7d575 100644 --- a/samples/irs-demo/web/src/main/resources/static/view/deal.html +++ b/samples/irs-demo/web/src/main/resources/static/view/deal.html @@ -20,7 +20,7 @@ Trade ID - {{deal.ref}} + {{deal.common.tradeID}} Valuation Date diff --git a/samples/irs-demo/web/src/main/resources/static/view/home.html b/samples/irs-demo/web/src/main/resources/static/view/home.html index 7dc3f8af57..c6734e8f6f 100644 --- a/samples/irs-demo/web/src/main/resources/static/view/home.html +++ b/samples/irs-demo/web/src/main/resources/static/view/home.html @@ -45,8 +45,8 @@ - - {{deal.ref}} + + {{deal.common.tradeID}} {{renderX500Name(deal.fixedLeg.fixedRatePayer)}} {{deal.fixedLeg.notional}} {{renderX500Name(deal.floatingLeg.floatingRatePayer)}} From 5a0b2b6f48a9af00db7425d08210d259597f0543 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Mon, 8 Apr 2019 12:16:19 +0100 Subject: [PATCH 42/75] CORDA-2528 - Update contract testing documentation (#4977) --- docs/source/tutorial-test-dsl.rst | 42 ++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/docs/source/tutorial-test-dsl.rst b/docs/source/tutorial-test-dsl.rst index 4401fa54b0..b77f794dd7 100644 --- a/docs/source/tutorial-test-dsl.rst +++ b/docs/source/tutorial-test-dsl.rst @@ -15,6 +15,33 @@ This tutorial will take you through the steps required to write a contract test The testing DSL allows one to define a piece of the ledger with transactions referring to each other, and ways of verifying their correctness. +Setting up the test +------------------- + +Before writing the individual tests, the general test setup must be configured: + +.. container:: codeset + + .. sourcecode:: kotlin + + class CommercialPaperTest { + private val miniCorp = TestIdentity(CordaX500Name("MiniCorp", "London", "GB")) + private val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")) + private val ledgerServices = MockServices(listOf("net.corda.finance.schemas"), megaCorp, miniCorp) + ... + } + + .. sourcecode:: java + + public class CommercialPaperTest { + private TestIdentity megaCorp = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB")); + private TestIdentity miniCorp = new TestIdentity(new CordaX500Name("MiniCorp", "London", "GB")); + MockServices ledgerServices = new MockServices(Arrays.asList("net.corda.finance.schemas"), megaCorp, miniCorp); + ... + } + +The ``ledgerServices`` object will provide configuration to the ``ledger`` DSL in the individual tests. + Testing single transactions --------------------------- @@ -24,25 +51,22 @@ We start with the empty ledger: .. sourcecode:: kotlin - class CommercialPaperTest{ + class CommercialPaperTest { @Test fun emptyLedger() { - ledger { + ledgerServices.ledger { + ... } } - ... } .. sourcecode:: java - import org.junit.Test; - - import static net.corda.testing.NodeTestUtils.ledger; - public class CommercialPaperTest { @Test public void emptyLedger() { - ledger(l -> { + ledger(ledgerServices, l -> { + ... return null; }); } @@ -90,7 +114,7 @@ Let's add a ``CommercialPaper`` transaction: @Test public void simpleCPDoesntCompile() { ICommercialPaperState inState = getPaper(); - ledger(l -> { + ledger(ledgerServices, l -> { l.transaction(tx -> { tx.input(inState); }); From 239376f699591c68983b69b08c1dd324f682f9e6 Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Tue, 9 Apr 2019 19:56:01 +0100 Subject: [PATCH 43/75] CORDA-2519 - Changed crash version to our latest. (#4976) * Changed crash version to our latest. * Changed short revision to long revision on the crash dependency from jitpack. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 187eb72efb..f839df4c71 100644 --- a/build.gradle +++ b/build.gradle @@ -53,7 +53,7 @@ buildscript { ext.dependency_checker_version = '4.0.0' ext.commons_collections_version = '4.1' ext.beanutils_version = '1.9.3' - ext.crash_version = 'cadb53544fbb3c0fb901445da614998a6a419488' + ext.crash_version = '810d2b774b85d4938be01b9b65e29e4fddbc450b' ext.jsr305_version = constants.getProperty("jsr305Version") ext.shiro_version = '1.4.0' ext.artifactory_plugin_version = constants.getProperty('artifactoryPluginVersion') From 95b6cd1c6dbd42f75ba9f1cd477b7d5b049038af Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Fri, 3 May 2019 14:37:22 +0100 Subject: [PATCH 44/75] CORDA-2817 - Changelog for bug fix to stop commands being combined. (#5021) --- docs/source/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 34b444a34f..34506e237f 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -4,6 +4,11 @@ Changelog Here's a summary of what's changed in each Corda release. For guidance on how to upgrade code from the previous release, see :doc:`app-upgrade-notes`. +Unreleased +---------- + +* Fix a bug in Corda 4.0 that combined commands in ``TransactionBuilder`` if they only differed by the signers list. The behaviour is now consistent with prior Corda releases. + .. _changelog_v4.0: Version 4.0 From 67808d985edcada2cfe86f2405683162a002eb1f Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Wed, 8 May 2019 14:08:52 +0100 Subject: [PATCH 45/75] CORDA-2363 - Fix ClassNotFound handling (#5078) (#5107) (cherry picked from commit 25f335861b1bdcb539c65746548d889c105cf784) --- .../net/corda/core/transactions/TransactionBuilder.kt | 11 ++++++++++- .../serialization/internal/amqp/AMQPExceptions.kt | 10 ++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index c05ee4b314..a72634db0b 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -27,6 +27,7 @@ import java.util.regex.Pattern import kotlin.collections.ArrayList import kotlin.collections.component1 import kotlin.collections.component2 +import kotlin.reflect.KClass /** * A TransactionBuilder is a transaction class that's mutable (unlike the others which are all immutable). It is @@ -170,6 +171,13 @@ open class TransactionBuilder( wireTx } + // Returns the first exception in the hierarchy that matches one of the [types]. + private tailrec fun Throwable.rootClassNotFoundCause(vararg types: KClass<*>): Throwable = when { + this::class in types -> this + this.cause == null -> this + else -> this.cause!!.rootClassNotFoundCause(*types) + } + /** * @return true if a new dependency was successfully added. */ @@ -179,7 +187,8 @@ open class TransactionBuilder( // The transaction verified successfully without adding any extra dependency. false } catch (e: Throwable) { - val rootError = e.rootCause + val rootError = e.rootClassNotFoundCause(ClassNotFoundException::class, NoClassDefFoundError::class) + when { // Handle various exceptions that can be thrown during verification and drill down the wrappings. // Note: this is a best effort to preserve backwards compatibility. diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPExceptions.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPExceptions.kt index ea81fb0d69..e4655d8f34 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPExceptions.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/AMQPExceptions.kt @@ -23,10 +23,12 @@ internal inline fun ifThrowsAppend(strToAppendFn: () -> String, block: () -> try { return block() } catch (th: Throwable) { - if (th is AMQPNotSerializableException) { - th.classHierarchy.add(strToAppendFn()) - } else { - th.setMessage("${strToAppendFn()} -> ${th.message}") + when (th) { + is AMQPNotSerializableException -> th.classHierarchy.add(strToAppendFn()) + // Do not overwrite the message of these exceptions as it may be used. + is ClassNotFoundException -> {} + is NoClassDefFoundError -> {} + else -> th.setMessage("${strToAppendFn()} -> ${th.message}") } throw th } From 3083d6e4dcd0bb2eea6a85351289a76be21883cf Mon Sep 17 00:00:00 2001 From: Matthew Nesbit Date: Thu, 9 May 2019 15:18:43 +0100 Subject: [PATCH 46/75] CORDA-2856 - Update the proton-j library to latest version (#5105) (#5109) --- build.gradle | 2 +- .../engine/ConnectionStateMachine.kt | 23 +++++++------------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index f839df4c71..859260ce94 100644 --- a/build.gradle +++ b/build.gradle @@ -68,7 +68,7 @@ buildscript { ext.proguard_version = constants.getProperty('proguardVersion') ext.jsch_version = '0.1.54' ext.commons_cli_version = '1.4' - ext.protonj_version = '0.27.1' // This is now aligned with the Artemis version, but retaining in case we ever need to diverge again for a bug fix. + ext.protonj_version = '0.33.0' // Overide Artemis version ext.snappy_version = '0.4' ext.class_graph_version = '4.6.12' ext.jcabi_manifests_version = '1.1' diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/ConnectionStateMachine.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/ConnectionStateMachine.kt index 6cb39ecfd2..8ec6c84940 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/ConnectionStateMachine.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/ConnectionStateMachine.kt @@ -22,7 +22,6 @@ import org.apache.qpid.proton.amqp.transport.ReceiverSettleMode import org.apache.qpid.proton.amqp.transport.SenderSettleMode import org.apache.qpid.proton.engine.* import org.apache.qpid.proton.message.Message -import org.apache.qpid.proton.message.ProtonJMessage import org.slf4j.MDC import java.net.InetSocketAddress import java.nio.ByteBuffer @@ -380,8 +379,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, val link = delivery.link if (link is Receiver) { if (delivery.isReadable && !delivery.isPartial) { - val pending = delivery.pending() - val amqpMessage = decodeAMQPMessage(pending, link) + val amqpMessage = decodeAMQPMessage(link) val payload = (amqpMessage.body as Data).value.array val connection = event.connection val channel = connection?.context as? Channel @@ -420,7 +418,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, } } - private fun encodeAMQPMessage(message: ProtonJMessage): ByteBuf { + private fun encodeAMQPMessage(message: Message): ByteBuf { val buffer = PooledByteBufAllocator.DEFAULT.heapBuffer(1500) try { try { @@ -438,7 +436,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, } private fun encodePayloadBytes(msg: SendableMessageImpl): ByteBuf { - val message = Proton.message() as ProtonJMessage + val message = Proton.message() message.body = Data(Binary(msg.payload)) message.isDurable = true message.properties = Properties() @@ -450,16 +448,11 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, return encodeAMQPMessage(message) } - private fun decodeAMQPMessage(pending: Int, link: Receiver): Message { - val msgBuf = PooledByteBufAllocator.DEFAULT.heapBuffer(pending) - try { - link.recv(NettyWritable(msgBuf)) - val amqpMessage = Proton.message() - amqpMessage.decode(msgBuf.array(), msgBuf.arrayOffset() + msgBuf.readerIndex(), msgBuf.readableBytes()) - return amqpMessage - } finally { - msgBuf.release() - } + private fun decodeAMQPMessage(link: Receiver): Message { + val amqpMessage = Proton.message() + val buf = link.recv() + amqpMessage.decode(buf) + return amqpMessage } fun transportWriteMessage(msg: SendableMessageImpl) { From f5dfc598863ade5f9935d11d67fbda003e5cc00e Mon Sep 17 00:00:00 2001 From: Richard Crook Date: Thu, 25 Apr 2019 11:18:25 +0100 Subject: [PATCH 47/75] CORDA-2929 : Improvements to docker image : compatible with v3.3; image size; truststore (#4965) * CORDA-2929 * test-docker * test fix --- docker/src/bash/generate-config.sh | 10 ++-- docker/src/bash/run-corda.sh | 5 +- docker/src/docker/Dockerfile | 80 +++++++++++++-------------- docker/src/docker/DockerfileAL | 87 ++++++++++++++---------------- docker/test-docker.sh | 70 ++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 101 deletions(-) create mode 100755 docker/test-docker.sh diff --git a/docker/src/bash/generate-config.sh b/docker/src/bash/generate-config.sh index 4bdcff673f..8c05d3e996 100755 --- a/docker/src/bash/generate-config.sh +++ b/docker/src/bash/generate-config.sh @@ -49,11 +49,11 @@ function generateGenericCZConfig(){ java -jar config-exporter.jar "GENERIC-CZ" "/opt/corda/starting-node.conf" "${CONFIG_FOLDER}/node.conf" java -Djava.security.egd=file:/dev/./urandom -Dcapsule.jvm.args="${JVM_ARGS}" -jar /opt/corda/bin/corda.jar \ - initial-registration \ - --base-directory=/opt/corda \ - --config-file=/etc/corda/node.conf \ - --network-root-truststore-password=${NETWORK_TRUST_PASSWORD} \ - --network-root-truststore=${CERTIFICATES_FOLDER}/${TRUST_STORE_NAME} &&\ + --initial-registration \ + --base-directory /opt/corda \ + --config-file ${CONFIG_FOLDER}/node.conf \ + --network-root-truststore-password ${NETWORK_TRUST_PASSWORD} \ + --network-root-truststore ${CERTIFICATES_FOLDER}/${TRUST_STORE_NAME} &&\ echo "Succesfully registered with ${DOORMAN_URL}, starting corda" && \ run-corda fi diff --git a/docker/src/bash/run-corda.sh b/docker/src/bash/run-corda.sh index b2cd117619..8cfd34b1f8 100755 --- a/docker/src/bash/run-corda.sh +++ b/docker/src/bash/run-corda.sh @@ -1,10 +1,7 @@ #!/usr/bin/env bash -: ${JVM_ARGS='-XX:+UseG1GC'} - -JVM_ARGS="-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap "${JVM_ARGS} if [[ ${JVM_ARGS} == *"Xmx"* ]]; then echo "WARNING: the use of the -Xmx flag is not recommended within docker containers. Use the --memory option passed to the container to limit heap size" fi -java -Djava.security.egd=file:/dev/./urandom -Dcapsule.jvm.args="${JVM_ARGS}" -jar /opt/corda/bin/corda.jar --base-directory=/opt/corda --config-file=/etc/corda/node.conf ${CORDA_ARGS} \ No newline at end of file +java -Djava.security.egd=file:/dev/./urandom -Dcapsule.jvm.args="${JVM_ARGS}" -jar /opt/corda/bin/corda.jar --base-directory /opt/corda --config-file ${CONFIG_FOLDER}/node.conf ${CORDA_ARGS} \ No newline at end of file diff --git a/docker/src/docker/Dockerfile b/docker/src/docker/Dockerfile index 5340c68638..2100ec59f9 100644 --- a/docker/src/docker/Dockerfile +++ b/docker/src/docker/Dockerfile @@ -1,35 +1,34 @@ FROM azul/zulu-openjdk:8u192 -RUN apt-get update && apt-get -y upgrade && apt-get -y install bash curl unzip +## Add packages, clean cache, create dirs, create corda user and change ownership +RUN apt-get update && \ + apt-get -y upgrade && \ + apt-get -y install bash curl unzip && \ + rm -rf /var/lib/apt/lists/* && \ + mkdir -p /opt/corda/cordapps && \ + mkdir -p /opt/corda/persistence && \ + mkdir -p /opt/corda/certificates && \ + mkdir -p /opt/corda/drivers && \ + mkdir -p /opt/corda/logs && \ + mkdir -p /opt/corda/bin && \ + mkdir -p /opt/corda/additional-node-infos && \ + mkdir -p /etc/corda && \ + addgroup corda && \ + useradd corda -g corda -m -d /opt/corda && \ + chown -R corda:corda /opt/corda && \ + chown -R corda:corda /etc/corda -# Create dirs -RUN mkdir -p /opt/corda/cordapps -RUN mkdir -p /opt/corda/persistence -RUN mkdir -p /opt/corda/certificates -RUN mkdir -p /opt/corda/drivers -RUN mkdir -p /opt/corda/logs -RUN mkdir -p /opt/corda/bin -RUN mkdir -p /opt/corda/additional-node-infos -RUN mkdir -p /etc/corda - -# Create corda user -RUN addgroup corda && \ - useradd corda -g corda -m -d /opt/corda - -WORKDIR /opt/corda - -ENV CORDAPPS_FOLDER="/opt/corda/cordapps" -ENV PERSISTENCE_FOLDER="/opt/corda/persistence" -ENV CERTIFICATES_FOLDER="/opt/corda/certificates" -ENV DRIVERS_FOLDER="/opt/corda/drivers" -ENV CONFIG_FOLDER="/etc/corda" - -ENV MY_P2P_PORT=10200 -ENV MY_RPC_PORT=10201 -ENV MY_RPC_ADMIN_PORT=10202 - -RUN chown -R corda:corda /opt/corda -RUN chown -R corda:corda /etc/corda +ENV CORDAPPS_FOLDER="/opt/corda/cordapps" \ + PERSISTENCE_FOLDER="/opt/corda/persistence" \ + CERTIFICATES_FOLDER="/opt/corda/certificates" \ + DRIVERS_FOLDER="/opt/corda/drivers" \ + CONFIG_FOLDER="/etc/corda" \ + MY_P2P_PORT=10200 \ + MY_RPC_PORT=10201 \ + MY_RPC_ADMIN_PORT=10202 \ + PATH=$PATH:/opt/corda/bin \ + JVM_ARGS="-XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap " \ + CORDA_ARGS="" ##CORDAPPS FOLDER VOLUME ["/opt/corda/cordapps"] @@ -46,25 +45,18 @@ VOLUME ["/opt/corda/additional-node-infos"] ##CONFIG LOCATION VOLUME ["/etc/corda"] - ##CORDA JAR -ADD --chown=corda:corda corda.jar /opt/corda/bin/corda.jar +COPY --chown=corda:corda corda.jar /opt/corda/bin/corda.jar ##CONFIG MANIPULATOR JAR -ADD --chown=corda:corda config-exporter.jar /opt/corda/config-exporter.jar +COPY --chown=corda:corda config-exporter.jar /opt/corda/config-exporter.jar ##CONFIG GENERATOR SHELL SCRIPT -ADD --chown=corda:corda generate-config.sh /opt/corda/bin/config-generator +COPY --chown=corda:corda generate-config.sh /opt/corda/bin/config-generator ##CORDA RUN SCRIPT -ADD --chown=corda:corda run-corda.sh /opt/corda/bin/run-corda +COPY --chown=corda:corda run-corda.sh /opt/corda/bin/run-corda ##BASE CONFIG FOR GENERATOR -ADD --chown=corda:corda starting-node.conf /opt/corda/starting-node.conf -##SET EXECUTABLE PERMISSIONS -RUN chmod +x /opt/corda/bin/config-generator -RUN chmod +x /opt/corda/bin/run-corda - -ENV PATH=$PATH:/opt/corda/bin - -EXPOSE $MY_P2P_PORT -EXPOSE $MY_RPC_PORT +COPY --chown=corda:corda starting-node.conf /opt/corda/starting-node.conf USER "corda" -CMD ["run-corda"] +EXPOSE ${MY_P2P_PORT} ${MY_RPC_PORT} ${MY_RPC_ADMIN_PORT} +WORKDIR /opt/corda +CMD ["run-corda"] \ No newline at end of file diff --git a/docker/src/docker/DockerfileAL b/docker/src/docker/DockerfileAL index 34bc5311cc..b5ce21ab00 100644 --- a/docker/src/docker/DockerfileAL +++ b/docker/src/docker/DockerfileAL @@ -1,39 +1,37 @@ FROM amazonlinux:2 -RUN amazon-linux-extras enable corretto8 -RUN yum -y install java-1.8.0-amazon-corretto-devel -RUN yum -y install bash -RUN yum -y install curl -RUN yum -y install unzip +## Add packages, clean cache, create dirs, create corda user and change ownership +RUN amazon-linux-extras enable corretto8 && \ + yum -y install java-1.8.0-amazon-corretto-devel && \ + yum -y install bash && \ + yum -y install curl && \ + yum -y install unzip && \ + yum clean all && \ + rm -rf /var/cache/yum && \ + mkdir -p /opt/corda/cordapps && \ + mkdir -p /opt/corda/persistence && \ + mkdir -p /opt/corda/certificates && \ + mkdir -p /opt/corda/drivers && \ + mkdir -p /opt/corda/logs && \ + mkdir -p /opt/corda/bin && \ + mkdir -p /opt/corda/additional-node-infos && \ + mkdir -p /etc/corda && \ + groupadd corda && \ + useradd corda -g corda -m -d /opt/corda && \ + chown -R corda:corda /opt/corda && \ + chown -R corda:corda /etc/corda -# Create dirs -RUN mkdir -p /opt/corda/cordapps -RUN mkdir -p /opt/corda/persistence -RUN mkdir -p /opt/corda/certificates -RUN mkdir -p /opt/corda/drivers -RUN mkdir -p /opt/corda/logs -RUN mkdir -p /opt/corda/bin -RUN mkdir -p /opt/corda/additional-node-infos -RUN mkdir -p /etc/corda - -# Create corda user -RUN groupadd corda && \ - useradd corda -g corda -m -d /opt/corda - -WORKDIR /opt/corda - -ENV CORDAPPS_FOLDER="/opt/corda/cordapps" -ENV PERSISTENCE_FOLDER="/opt/corda/persistence" -ENV CERTIFICATES_FOLDER="/opt/corda/certificates" -ENV DRIVERS_FOLDER="/opt/corda/drivers" -ENV CONFIG_FOLDER="/etc/corda" - -ENV MY_P2P_PORT=10200 -ENV MY_RPC_PORT=10201 -ENV MY_RPC_ADMIN_PORT=10202 - -RUN chown -R corda:corda /opt/corda -RUN chown -R corda:corda /etc/corda +ENV CORDAPPS_FOLDER="/opt/corda/cordapps" \ + PERSISTENCE_FOLDER="/opt/corda/persistence" \ + CERTIFICATES_FOLDER="/opt/corda/certificates" \ + DRIVERS_FOLDER="/opt/corda/drivers" \ + CONFIG_FOLDER="/etc/corda" \ + MY_P2P_PORT=10200 \ + MY_RPC_PORT=10201 \ + MY_RPC_ADMIN_PORT=10202 \ + PATH=$PATH:/opt/corda/bin \ + JVM_ARGS="-XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap " \ + CORDA_ARGS="" ##CORDAPPS FOLDER VOLUME ["/opt/corda/cordapps"] @@ -50,25 +48,18 @@ VOLUME ["/opt/corda/additional-node-infos"] ##CONFIG LOCATION VOLUME ["/etc/corda"] - ##CORDA JAR -ADD --chown=corda:corda corda.jar /opt/corda/bin/corda.jar +COPY --chown=corda:corda corda.jar /opt/corda/bin/corda.jar ##CONFIG MANIPULATOR JAR -ADD --chown=corda:corda config-exporter.jar /opt/corda/config-exporter.jar +COPY --chown=corda:corda config-exporter.jar /opt/corda/config-exporter.jar ##CONFIG GENERATOR SHELL SCRIPT -ADD --chown=corda:corda generate-config.sh /opt/corda/bin/config-generator +COPY --chown=corda:corda generate-config.sh /opt/corda/bin/config-generator ##CORDA RUN SCRIPT -ADD --chown=corda:corda run-corda.sh /opt/corda/bin/run-corda +COPY --chown=corda:corda run-corda.sh /opt/corda/bin/run-corda ##BASE CONFIG FOR GENERATOR -ADD --chown=corda:corda starting-node.conf /opt/corda/starting-node.conf -##SET EXECUTABLE PERMISSIONS -RUN chmod +x /opt/corda/bin/config-generator -RUN chmod +x /opt/corda/bin/run-corda - -ENV PATH=$PATH:/opt/corda/bin - -EXPOSE $MY_P2P_PORT -EXPOSE $MY_RPC_PORT +COPY --chown=corda:corda starting-node.conf /opt/corda/starting-node.conf USER "corda" -CMD ["run-corda"] +EXPOSE ${MY_P2P_PORT} ${MY_RPC_PORT} ${MY_RPC_ADMIN_PORT} +WORKDIR /opt/corda +CMD ["run-corda"] \ No newline at end of file diff --git a/docker/test-docker.sh b/docker/test-docker.sh new file mode 100755 index 0000000000..e5b35a294c --- /dev/null +++ b/docker/test-docker.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# Tests Corda docker by registering with a test doorman +# usage: ./test-docker.sh +# example: ./test-docker.sh corda/corda-corretto-4.0:RELEASE +IMAGE=${1:-corda/corda-corretto-4.0} +SALT=${RANDOM} + +# Start up test-doorman, if not already running +if [ ! "$(docker ps -q -f name=test-doorman)" ]; then + if [ "$(docker ps -aq -f status=exited -f name=test-doorman)" ]; then + echo "TEST-IMAGE-${IMAGE}: test-doorman is in a status=exited state. I will remove." + docker rm -f test-doorman + fi + echo "TEST-IMAGE-${IMAGE}: test-doorman is not running. I will start." + docker run -d --rm --name test-doorman -p 8080:8080 \ + -e NMS_MONGO_CONNECTION_STRING=embed \ + -e NMS_TLS=false \ + -e NMS_DOORMAN=true \ + -e NMS_CERTMAN=false \ + cordite/network-map +else + echo "TEST-IMAGE-${IMAGE}: test-door man is already running. I will use this instance." +fi + +# Wait for test-doorman and then download truststore +while [[ "$(curl -s -o network-root-truststore.jks -w ''%{http_code}'' http://localhost:8080/network-map/truststore)" != "200" ]]; do + echo "TEST-IMAGE-${IMAGE}: waiting 5 seconds for test-doorman to serve..." + sleep 5 +done + +# Test corda docker +echo "TEST-IMAGE-${IMAGE}: Run config-generator in corda docker with image: ${IMAGE}" +docker run -d --name corda-test-${SALT} --network=host --hostname=127.0.0.1 \ + -e MY_LEGAL_NAME="O=Test-${SALT},L=Berlin,C=DE" \ + -e MY_PUBLIC_ADDRESS="localhost" \ + -e NETWORKMAP_URL="http://localhost:8080" \ + -e DOORMAN_URL="http://localhost:8080" \ + -e NETWORK_TRUST_PASSWORD="trustpass" \ + -e MY_EMAIL_ADDRESS="cordauser@r3.com" \ + -v $(pwd)/network-root-truststore.jks:/opt/corda/certificates/network-root-truststore.jks \ + -e CORDA_ARGS="--log-to-console --no-local-shell" \ + $IMAGE config-generator --generic + +# Succesfully registered (with http://localhost:8080) +docker logs -f corda-test-${SALT} | grep -q "Succesfully registered" +if [ ! "$(docker ps -q -f name=corda-test-${SALT})" ]; then + echo "TEST-IMAGE-${IMAGE}: FAIL corda-test has exited." + docker logs corda-test-${SALT} + rm -f $(pwd)/network-root-truststore.jks + docker rm -f corda-test-${SALT} + exit 1 +else + echo "TEST-IMAGE-${IMAGE}: SUCCESS : Succesfully registered with http://localhost:8080" +fi + +# Node started up and registered +docker logs -f corda-test-${SALT} | grep -q "started up and registered in" +if [ ! "$(docker ps -q -f name=corda-test-${SALT})" ]; then + echo "TEST-IMAGE-${IMAGE}: FAIL corda-test has exited." + docker logs corda-test-${SALT} + rm -f $(pwd)/network-root-truststore.jks + docker rm -f corda-test-${SALT} + exit 1 +else + echo "TEST-IMAGE-${IMAGE}: SUCCESS : Node started up and registered" + echo "TEST-IMAGE-${IMAGE}: SUCCESS : tear down" + rm -f $(pwd)/network-root-truststore.jks + docker rm -f corda-test-${SALT} + exit 0 +fi From 814ca9c9802133a4152c2e779c796b408a7650a2 Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Fri, 3 May 2019 13:25:22 +0100 Subject: [PATCH 48/75] CORDA-2888 Allow bring-your-own-config to docker image (#5095) * CORDA-2888 Allow bring-your-own-config to docker image (#2035) modify run logic so that if node config is present, do not generate a new one * Change according to PR review. * Removed whitespace from generate-config in the argument list. * Changed state of initial registration part of the script to pre cherry-pick to preserve changes that arent into enterprise. --- docker/src/bash/generate-config.sh | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docker/src/bash/generate-config.sh b/docker/src/bash/generate-config.sh index 8c05d3e996..130bc6f784 100755 --- a/docker/src/bash/generate-config.sh +++ b/docker/src/bash/generate-config.sh @@ -25,11 +25,8 @@ function generateTestnetConfig() { } function generateGenericCZConfig(){ - - if [[ -f /etc/corda/node.conf ]] ; then - echo 'WARN: existing config detected, launching corda' - run-corda - else + if ! [[ -f ${CONFIG_FOLDER}/node.conf ]] ; then + echo 'INFO: no existing node config detected, generating config skeleton' : ${NETWORKMAP_URL:? '$NETWORKMAP_URL, the Compatibility Zone to join must be set as environment variable'} : ${DOORMAN_URL:? '$DOORMAN_URL, the Doorman to use when joining must be set as environment variable'} : ${MY_LEGAL_NAME:? '$MY_LEGAL_NAME, the X500 name to use when joining must be set as environment variable'} @@ -47,16 +44,15 @@ function generateGenericCZConfig(){ MY_RPC_PORT=${MY_RPC_PORT} \ MY_RPC_ADMIN_PORT=${MY_RPC_ADMIN_PORT} \ java -jar config-exporter.jar "GENERIC-CZ" "/opt/corda/starting-node.conf" "${CONFIG_FOLDER}/node.conf" - + fi java -Djava.security.egd=file:/dev/./urandom -Dcapsule.jvm.args="${JVM_ARGS}" -jar /opt/corda/bin/corda.jar \ --initial-registration \ --base-directory /opt/corda \ --config-file ${CONFIG_FOLDER}/node.conf \ --network-root-truststore-password ${NETWORK_TRUST_PASSWORD} \ - --network-root-truststore ${CERTIFICATES_FOLDER}/${TRUST_STORE_NAME} &&\ - echo "Succesfully registered with ${DOORMAN_URL}, starting corda" && \ + --network-root-truststore ${CERTIFICATES_FOLDER}/${TRUST_STORE_NAME} && \ + echo "Successfully registered with ${DOORMAN_URL}, starting corda" && \ run-corda - fi } function downloadTestnetCerts() { @@ -109,7 +105,6 @@ while :; do shift done - : ${TRUST_STORE_NAME="network-root-truststore.jks"} : ${JVM_ARGS='-Xmx4g -Xms2g -XX:+UseG1GC'} From 925908907169ae77bdb528f44f26de4c1ef779fc Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Fri, 3 May 2019 13:26:09 +0100 Subject: [PATCH 49/75] CORDA-2641 - change documentation. (#5094) * Changed documentation to prevent future issues. * Changed according to code review. --- docs/source/docker-image.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/docker-image.rst b/docs/source/docker-image.rst index 3fd725eec7..d1ca78f2f6 100644 --- a/docs/source/docker-image.rst +++ b/docs/source/docker-image.rst @@ -69,7 +69,7 @@ It is possible to utilize the image to automatically generate a sensible minimal Joining TestNet --------------- -.. note:: Requirements: A valid registration for TestNet and a one-time code for joining TestNet. +.. note:: Requirements: A valid registration for TestNet and a one-time code for joining TestNet. Certificate and configuration folders should be accessible from the container. Docker will create folders using the permissions of it's daemon if they don't exist and the container may fail accessing them. .. code-block:: shell From ba28e05bebcf33bf3a8e1505a3707f6760aba066 Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Tue, 7 May 2019 10:32:48 +0100 Subject: [PATCH 50/75] CORDA-2888 change default dataSource.url to match the docker container structure. (#5098) * If there is no dataSourceProperties in the node.conf, the default value is modified to match the docker structure and documentation of having a persistence folder. * chagnes according to pr review. --- docker/src/bash/run-corda.sh | 8 +++++++- docs/source/docker-image.rst | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docker/src/bash/run-corda.sh b/docker/src/bash/run-corda.sh index 8cfd34b1f8..f8af508a46 100755 --- a/docker/src/bash/run-corda.sh +++ b/docker/src/bash/run-corda.sh @@ -4,4 +4,10 @@ if [[ ${JVM_ARGS} == *"Xmx"* ]]; then echo "WARNING: the use of the -Xmx flag is not recommended within docker containers. Use the --memory option passed to the container to limit heap size" fi -java -Djava.security.egd=file:/dev/./urandom -Dcapsule.jvm.args="${JVM_ARGS}" -jar /opt/corda/bin/corda.jar --base-directory /opt/corda --config-file ${CONFIG_FOLDER}/node.conf ${CORDA_ARGS} \ No newline at end of file +count=$(grep -c dataSourceProperties ${CONFIG_FOLDER}/node.conf) + +if [ "$count" -eq 0 ]; then + java -Djava.security.egd=file:/dev/./urandom -Dcapsule.jvm.args="${JVM_ARGS}" -Dcorda.dataSourceProperties.dataSource.url="jdbc:h2:file:"${PERSISTENCE_FOLDER}"/persistence;DB_CLOSE_ON_EXIT=FALSE;WRITE_DELAY=0;LOCK_TIMEOUT=10000" -jar /opt/corda/bin/corda.jar --base-directory /opt/corda --config-file ${CONFIG_FOLDER}/node.conf ${CORDA_ARGS} +else + java -Djava.security.egd=file:/dev/./urandom -Dcapsule.jvm.args="${JVM_ARGS}" -jar /opt/corda/bin/corda.jar --base-directory /opt/corda --config-file ${CONFIG_FOLDER}/node.conf ${CORDA_ARGS} +fi \ No newline at end of file diff --git a/docs/source/docker-image.rst b/docs/source/docker-image.rst index d1ca78f2f6..547e399747 100644 --- a/docs/source/docker-image.rst +++ b/docs/source/docker-image.rst @@ -6,6 +6,7 @@ Running a node connected to a Compatibility Zone in Docker .. note:: Requirements: A valid node.conf and a valid set of certificates - (signed by the CZ) + In this example, the certificates are stored at ``/home/user/cordaBase/certificates``, the node configuration is in ``/home/user/cordaBase/config/node.conf`` and the CorDapps to run are in ``/path/to/cordapps`` .. code-block:: shell @@ -33,6 +34,9 @@ If using the H2 database: 5. Persistence - the folder to hold the H2 database files must be mounted at location ``/opt/corda/persistence`` +.. note:: If there is no dataSourceProperties key in the node.conf, the docker container overrides the url for H2 to point to the persistence directory by default so that the database can be accessed outside the container + + Running a node connected to a Bootstrapped Network -------------------------------------------------- From 9840d9d5a07634aeec0c7ea6d40a414e467c467f Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Thu, 9 May 2019 18:55:26 +0100 Subject: [PATCH 51/75] RELEASE - 4.1 RC02 --- constants.properties | 2 +- docs/source/_static/versions | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index a6da6db33b..c2c1593447 100644 --- a/constants.properties +++ b/constants.properties @@ -2,7 +2,7 @@ # because some versions here need to be matched by app authors in # their own projects. So don't get fancy with syntax! -cordaVersion=4.1-RC01 +cordaVersion=4.1-RC02 gradlePluginsVersion=4.0.42 kotlinVersion=1.2.71 java8MinUpdateVersion=171 diff --git a/docs/source/_static/versions b/docs/source/_static/versions index 775f9cb83f..13e98ab34b 100644 --- a/docs/source/_static/versions +++ b/docs/source/_static/versions @@ -15,5 +15,6 @@ "https://docs.corda.net/releases/release-V3.2": "V3.2", "https://docs.corda.net/releases/release-V3.3": "V3.3", "https://docs.corda.net/releases/release-V4.0": "V4.0", + "https://docs.corda.net/releases/release-V4.1": "V4.1", "https://docs.corda.net/head/": "Master" } From cc03754c86f7dd388b444e38986dc7d63bd47000 Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Sun, 12 May 2019 21:48:50 +0100 Subject: [PATCH 52/75] CORDA-2586 - Added missing publish for common-logging lib. (#5118) --- build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 859260ce94..272205785b 100644 --- a/build.gradle +++ b/build.gradle @@ -380,7 +380,8 @@ bintrayConfig { 'corda-tools-network-bootstrapper', 'corda-tools-cliutils', 'corda-common-configuration-parsing', - 'corda-common-validation' + 'corda-common-validation', + 'corda-common-logging' ] license { name = 'Apache-2.0' From b8b344ec8686fafd5e964eba775acb0e6454f418 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Sun, 12 May 2019 21:49:51 +0100 Subject: [PATCH 53/75] RELEASE - 4.1 RC03 --- constants.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index c2c1593447..510f7b24e2 100644 --- a/constants.properties +++ b/constants.properties @@ -2,7 +2,7 @@ # because some versions here need to be matched by app authors in # their own projects. So don't get fancy with syntax! -cordaVersion=4.1-RC02 +cordaVersion=4.1-RC03 gradlePluginsVersion=4.0.42 kotlinVersion=1.2.71 java8MinUpdateVersion=171 From 23982a1b6c7e10c27f156470d25d835ce4636402 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Tue, 11 Jun 2019 18:17:03 +0100 Subject: [PATCH 54/75] RELEASE - 4.1 release notes (#5154) release notes --- docs/source/release-notes.rst | 163 +++++++++++++++++++++++++++++++++- 1 file changed, 161 insertions(+), 2 deletions(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 5b5192ee2c..3535e3eb24 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -1,3 +1,164 @@ +.. contents:: + +Release notes for Corda 4.1 +=========================== + +.. _release_notes_v4_1: + +It's been a little under 3 1/2 months since the release of Corda 4.0 and all of the brand new features that added to the powerful suite +of tools Corda offers. Now, following the release of Corda Enterprise 4.0, we are proud to release Corda 4.1, bringing over 150 fixes +and documentation updates to bring additional stability and quality of life improvements to those developing on the Corda platform. + +Information on Corda Enterprise 4.0 can be found `here _ and +`here `_. (It's worth noting that normally this document would have started with a comment +about whether or not you'd been recently domiciled under some solidified mineral material regarding the release of Corda Enterprise 4.0. Alas, we made +that joke when we shipped the first release of Corda after Enterprise 3.0 shipped, so the thunder has been stolen and repeating ourselves would be terribly gauche.) + +Corda 4.1 brings the lessons and bug fixes discovered during the process of building and shipping Enterprise 4.0 back to the open source community. As mentioned above +there are over 150 fixes and tweaks here. With this release the core feature sets of both entities are far closer aligned than past major +releases of the Corda that should make testing your CorDapps in mixed type environments much easier. + +As such, we recommend you upgrade from Corda 4.0 to Corda 4.1 as soon possible. + +Issues Fixed +~~~~~~~~~~~~ + +* Docker images do not support passing a prepared config with initial registration [`CORDA-2888 `_] +* Different hashes for container Corda and normal Corda jars [`CORDA-2884 `_] +* Auto attachment of dependencies fails to find class [`CORDA-2863 `_] +* Artemis session can't be used in more than one thread [`CORDA-2861 `_] +* Property type checking is overly strict [`CORDA-2860 `_] +* Serialisation bug (or not) when trying to run SWIFT Corda Settler tests [`CORDA-2848 `_] +* Custom serialisers not found when running mock network tests [`CORDA-2847 `_] +* Base directory error message where directory does not exist is slightly misleading [`CORDA-2834 `_] +* Progress tracker not reloadable in checkpoints written in Java [`CORDA-2825 `_] +* Missing quasar error points to non-existent page [`CORDA-2821 `_] +* ``TransactionBuilder`` can build unverifiable transactions in V5 if more than one CorDapp loaded [`CORDA-2817 `_] +* The node hangs when there is a dis-connection of Oracle database [`CORDA-2813 `_] +* Docs: fix the latex warnings in the build [`CORDA-2809 `_] +* Docs: build the docs page needs updating [`CORDA-2808 `_] +* Don't retry database transaction in abstract node start [`CORDA-2807 `_] +* Upgrade Corda Core to use Java Persistence API 2.2 [`CORDA-2804 `_] +* Network map stopped updating on Testnet staging notary [`CORDA-2803 `_] +* Improve test reliability by eliminating fixed-duration Thread.sleeps [`CORDA-2802 `_] +* Not handled exception when certificates directory is missing [`CORDA-2786 `_] +* Unable to run FinalityFlow if the initiating app has ``targetPlatformVersion=4`` and the recipient is using the old version [`CORDA-2784 `_] +* Performing a registration with an incorrect Config gives error without appropriate info [`CORDA-2783 `_] +* Regression: ``java.lang.Comparable`` is not on the default whitelist but never has been [`CORDA-2782 `_] +* Docs: replace version string with things that get substituted [`CORDA-2781 `_] +* Inconsistent docs between internal and external website [`CORDA-2779 `_] +* Change the doc substitution so that it works in code blocks as well as in other places [`CORDA-2777 `_] +* ``net.corda.core.internal.LazyStickyPool#toIndex`` can create a negative index [`CORDA-2772 `_] +* ``NetworkMapUpdater#fileWatcherSubscription`` is never assigned and hence the subscription is never cleaned up [`CORDA-2770 `_] +* Infinite recursive call in ``NetworkParameters.copy`` [`CORDA-2769 `_] +* Unexpected exception de-serializing throwable for ``OverlappingAttachmentsException`` [`CORDA-2765 `_] +* Always log config to log file [`CORDA-2763 `_] +* ``ReceiveTransactionFlow`` states to record flag gets quietly ignored if ``checkSufficientSignatures = false`` [`CORDA-2762 `_] +* Fix Driver's ``PortAllocation`` class, and then use it for Node's integration tests. [`CORDA-2759 `_] +* State machine logs an error prior to deciding to escalate to an error [`CORDA-2757 `_] +* Migrate DJVM into a separate module [`CORDA-2750 `_] +* Error in ``HikariPool`` in the performance cluster [`CORDA-2748 `_] +* Package DJVM CLI for standalone distribution [`CORDA-2747 `_] +* Unable to insert state into vault if notary not on network map [`CORDA-2745 `_] +* Create sample code and integration tests to showcase rpc operations that support reconnection [`CORDA-2743 `_] +* RPC v4 client unable to subscribe to progress tracker events from Corda 3.3 node [`CORDA-2742 `_] +* Doc Fix: Rpc client connection management section not fully working in Corda 4 [`CORDA-2741 `_] +* ``AnsiProgressRenderer`` may start reporting incorrect progress if tree contains identical steps [`CORDA-2738 `_] +* The ``FlowProgressHandle`` does not always return expected results [`CORDA-2737 `_] +* Doc fix: integration testing tutorial could do with some gradle instructions [`CORDA-2729 `_] +* Release upgrade to Corda 4 notes: include upgrading quasar.jar explicitly in the Corda Kotlin template [`CORDA-2728 `_] +* DJVM CLI log file is always empty [`CORDA-2725 `_] +* DJVM documentation incorrect around `djvm check` [`CORDA-2721 `_] +* Doc fix: reflect the CorDapp template doc changes re quasar/test running the official docs [`CORDA-2715 `_] +* Upgrade to Corda 4 test docs only have Kotlin examples [`CORDA-2710 `_] +* Log message "Cannot find flow corresponding to session" should not be a warning [`CORDA-2706 `_] +* Flow failing due to "Flow sessions were not provided" for its own identity [`CORDA-2705 `_] +* RPC user security using ``Shiro`` docs have errant commas in example config [`CORDA-2703 `_] +* The ``crlCheckSoftFail`` option is not respected, allowing transactions even if strict checking is enabled [`CORDA-2701 `_] +* Vault paging fails if setting max page size to `Int.MAX_VALUE` [`CORDA-2698 `_] +* Upgrade to Corda Gradle Plugins 4.0.41 [`CORDA-2697 `_] +* Corda complaining of duplicate classes upon start-up when it doesn't need to [`CORDA-2696 `_] +* Launching node explorer for node creates error and explorer closes [`CORDA-2694 `_] +* Transactions created in V3 cannot be verified in V4 if any of the state types were included in "depended upon" CorDapps which were not attached to the transaction [`CORDA-2692 `_] +* Reduce CorDapp scanning logging [`CORDA-2690 `_] +* Clean up verbose warning: `ProgressTracker has not been started` [`CORDA-2689 `_] +* Add a no-carpenter context [`CORDA-2688 `_] +* Improve CorDapp upgrade guidelines for migrating existing states on ledger (pre-V4) [`CORDA-2684 `_] +* ``SessionRejectException.UnknownClass`` trapped by flow hospital but no way to call dropSessionInit() [`CORDA-2683 `_] +* Repeated ``CordFormations`` can fail with ClassLoader exception. [`CORDA-2676 `_] +* Backwards compatibility break in serialisation engine when deserialising nullable fields [`CORDA-2674 `_] +* Simplify sample CorDapp projects. [`CORDA-2672 `_] +* Remove ``ExplorerSimulator`` from Node Explorer [`CORDA-2671 `_] +* Reintroduce ``pendingFlowsCount`` to the public API [`CORDA-2669 `_] +* Trader demo integration tests fails with jar not found exception [`CORDA-2668 `_] +* Fix Source ClassLoader for DJVM [`CORDA-2667 `_] +* Issue with simple transfer of ownable asset [`CORDA-2665 `_] +* Fix references to Docker images in docs [`CORDA-2664 `_] +* Add something to docsite the need for a common contracts Jar between OS/ENT and how it should be compiled against OS [`CORDA-2656 `_] +* Create document outlining CorDapp Upgrade guarantees [`CORDA-2655 `_] +* Fix DJVM CLI tool [`CORDA-2654 `_] +* Corda Service needs Thread Context ClassLoader [`CORDA-2653 `_] +* Useless migration error when finance workflow jar is not installed [`CORDA-2651 `_] +* Database connection pools leaking memory on every checkpoint [`CORDA-2646 `_] +* Exception swallowed when querying vault via RPC with bad page spec [`CORDA-2645 `_] +* Applying CordFormation and Cordapp Gradle plugins together includes Jolokia into the Cordapp. [`CORDA-2642 `_] +* Wrong folder ownership while trying to connect to Testnet using RC* docker image [`CORDA-2641 `_] +* Provide a better error message on an incompatible implicit contract upgrade [`CORDA-2633 `_] +* ``uploadAttachment`` via shell can fail with unhelpful message if the result of the command is unsuccessful [`CORDA-2632 `_] +* Provide a better error msg when the notary type is misconfigured on the net params [`CORDA-2629 `_] +* Maybe tone down the level of panic when somebody types their SSH password in incorrectly... [`CORDA-2621 `_] +* Cannot complete transaction that has unknown states in the transaction history [`CORDA-2615 `_] +* Switch off the codepaths that disable the FinalityHandler [`CORDA-2613 `_] +* is our API documentation (what is stable and what isn't) correct? [`CORDA-2610 `_] +* Getting set up guide needs to be updated to reflect Java 8 fun and games [`CORDA-2602 `_] +* Not handle exception when Explorer tries to connect to inaccessible server [`CORDA-2586 `_] +* Errors received from peers can't be distinguished from local errors [`CORDA-2572 `_] +* Add `flow kill` command, deprecate `run killFlow` [`CORDA-2569 `_] +* Hash to signature constraints migration: add a config option that makes hash constraints breakable. [`CORDA-2568 `_] +* Deadlock between database and AppendOnlyPersistentMap [`CORDA-2566 `_] +* Docfix: Document custom cordapp configuration [`CORDA-2560 `_] +* Bootstrapper - option to include contracts to whitelist from signed jars [`CORDA-2554 `_] +* Explicit contract upgrade sample fails upon initiation (ClassNotFoundException) [`CORDA-2550 `_] +* IRS demo app missing demodate endpoint [`CORDA-2535 `_] +* Doc fix: Contract testing tutorial errors [`CORDA-2528 `_] +* Unclear error message when receiving state from node on higher version of signed cordapp [`CORDA-2522 `_] +* Terminating ssh connection to node results in stack trace being thrown to the console [`CORDA-2519 `_] +* Error propagating hash to signature constraints [`CORDA-2515 `_] +* Unable to import trusted attachment [`CORDA-2512 `_] +* Invalid node command line options not always gracefully handled [`CORDA-2506 `_] +* node.conf with rogue line results non-comprehensive error [`CORDA-2505 `_] +* Fix v4's inability to migrate V3 vault data [`CORDA-2487 `_] +* Vault Query fails to process states upon CorDapp Contract upgrade [`CORDA-2486 `_] +* Signature Constraints end-user documentation is limited [`CORDA-2477 `_] +* Docs update: document transition from the whitelist constraint to the sig constraint [`CORDA-2465 `_] +* The ``ContractUpgradeWireTransaction`` does not support the Signature Constraint [`CORDA-2456 `_] +* Intermittent `relation "hibernate_sequence" does not exist` error when using Postgres [`CORDA-2393 `_] +* Implement package namespace ownership [`CORDA-1947 `_] +* Show explicit error message when new version of OS CorDapp contains schema changes [`CORDA-1596 `_] +* Dockerfile improvements and image size reduction [`CORDA-2929 `_] +* Update QPID Proton-J library to latest [`CORDA-2856 `_] +* Not handled excpetion when certificates directory is missing [`CORDA-2786 `_] +* The DJVM cannot sandbox instances of Contract.verify(LedgerTransaction) when testing CorDapps. [`CORDA-2775 `_] +* State machine logs an error prior to deciding to escalate to an error [`CORDA-2757 `_] +* Should Jolokia be included in the built jar files? [`CORDA-2699 `_] +* Transactions created in V3 cannot be verified in V4 if any of the state types were included in "depended upon" CorDapps which were not attached to the transaction [`CORDA-2692 `_] +* Prevent a node re-registering with the doorman if it did already and the node "state" has not been erased [`CORDA-2647 `_] +* The cert hierarchy diagram for C4 is the same as C3.0 but I thought we changed it between C3.1 and 3.2? [`CORDA-2604 `_] +* Windows build fails with `FileSystemException` in `TwoPartyTradeFlowTests` [`CORDA-2363 `_] +* `Cash.generateSpend` cannot be used twice to generate two cash moves in the same tx [`CORDA-2162 `_] +* FlowException thrown by session.receive is not propagated back to a counterparty +* invalid command line args for corda result in 0 exit code +* Windows build fails on TwoPartyTradeFlowTests +* C4 performance below C3, bring it back into parity +* Deserialisation of ContractVerificationException blows up trying to put null into non-null field +* Reference state test (R3T-1918) failing probably due to unconsumed linear state that was referenced. +* Signature constraint: Jarsigner verification allows removal of files from the archive. +* Node explorer bug revealed from within Demobench: serialisation failed error is shown +* Security: Fix vulnerability where an attacker can use CustomSerializers to alter the meaning of serialized data +* Node/RPC is broken after CorDapp upgrade +* RPC client disconnects shouldn't be a warning +* Hibernate logs warning and errors for some conditions we handle + Release notes for Corda 4 ========================= @@ -24,8 +185,6 @@ account for your own schedule planning. by Corda 4. There will shortly be a followup Corda 3.4 release that corrects this error. Interop between Corda 3 and Corda 4 will require that Corda 3 users are on the latest patchlevel release. -.. contents:: - Changes for developers in Corda 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From c11f6c15f71ac47b715e8e1b56ff5159b3730bdc Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Tue, 11 Jun 2019 18:18:53 +0100 Subject: [PATCH 55/75] RELEASE - Final release versoin number --- constants.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index 510f7b24e2..611e68ef43 100644 --- a/constants.properties +++ b/constants.properties @@ -2,7 +2,7 @@ # because some versions here need to be matched by app authors in # their own projects. So don't get fancy with syntax! -cordaVersion=4.1-RC03 +cordaVersion=4.1 gradlePluginsVersion=4.0.42 kotlinVersion=1.2.71 java8MinUpdateVersion=171 From 97eaa7220ca4a14d86385579b84fce6884c95bc8 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Mon, 17 Jun 2019 14:02:45 +0100 Subject: [PATCH 56/75] RELESE - fix release notes ToC --- docs/source/release-notes.rst | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 3535e3eb24..7b2b97e319 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -1,10 +1,17 @@ -.. contents:: +Release notes +------------- + +.. contents:: + :depth: 2 -Release notes for Corda 4.1 -=========================== .. _release_notes_v4_1: + + +Corda 4.1 +========= + It's been a little under 3 1/2 months since the release of Corda 4.0 and all of the brand new features that added to the powerful suite of tools Corda offers. Now, following the release of Corda Enterprise 4.0, we are proud to release Corda 4.1, bringing over 150 fixes and documentation updates to bring additional stability and quality of life improvements to those developing on the Corda platform. @@ -159,11 +166,11 @@ Issues Fixed * RPC client disconnects shouldn't be a warning * Hibernate logs warning and errors for some conditions we handle -Release notes for Corda 4 -========================= - .. _release_notes_v4_0: +Corda 4 +======= + Welcome to the Corda 4 release notes. Please read these carefully to understand what's new in this release and how the changes can help you. Just as prior releases have brought with them commitments to wire and API stability, Corda 4 comes with those same guarantees. States and apps valid in From d7b54ed1f601f5288215041199c9544cba898cfc Mon Sep 17 00:00:00 2001 From: James Brown <33660060+jamesbr3@users.noreply.github.com> Date: Fri, 21 Jun 2019 09:29:43 +0100 Subject: [PATCH 57/75] ENT-3543 Update okhttp dependency (#5208) --- build.gradle | 2 +- .../kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt | 2 +- .../src/main/kotlin/net/corda/testing/http/HttpUtils.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 272205785b..e9fac934ef 100644 --- a/build.gradle +++ b/build.gradle @@ -34,7 +34,7 @@ buildscript { ext.disruptor_version = constants.getProperty("disruptorVersion") ext.metrics_version = constants.getProperty("metricsVersion") ext.metrics_new_relic_version = constants.getProperty("metricsNewRelicVersion") - ext.okhttp_version = '3.5.0' + ext.okhttp_version = '3.14.1' ext.netty_version = '4.1.22.Final' ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion") ext.fileupload_version = '1.3.3' diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index 14bbe399f8..b7dcaa6d12 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -307,7 +307,7 @@ class DriverDSLImpl( while (process.isAlive) try { val response = client.newCall(Request.Builder().url(url).build()).execute() - if (response.isSuccessful && (response.body().string() == "started")) { + if (response.isSuccessful && (response.body()?.string() == "started")) { return WebserverHandle(handle.webAddress, process) } } catch (e: ConnectException) { diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt index 5e87f4e36b..4f4675a0fe 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt @@ -47,7 +47,7 @@ object HttpUtils { private fun makeRequest(request: Request) { val response = client.newCall(request).execute() if (!response.isSuccessful) { - throw IOException("${request.method()} to ${request.url()} returned a ${response.code()}: ${response.body().string()}") + throw IOException("${request.method()} to ${request.url()} returned a ${response.code()}: ${response.body()?.string()}") } } } From 0101cdbd566bdfab53700dc94ee18a840b558944 Mon Sep 17 00:00:00 2001 From: James Brown <33660060+jamesbr3@users.noreply.github.com> Date: Fri, 21 Jun 2019 09:30:40 +0100 Subject: [PATCH 58/75] ENT-3542 remove unused commons-codec dep (#5207) --- node/build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/node/build.gradle b/node/build.gradle index 8357d4b3c9..665cda6b28 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -174,7 +174,6 @@ dependencies { compile group: 'com.ea.agentloader', name: 'ea-agent-loader', version: "${eaagentloader_version}" // BFT-Smart dependencies - compile 'commons-codec:commons-codec:1.10' compile 'com.github.bft-smart:library:master-v1.1-beta-g6215ec8-87' // Java Atomix: RAFT library @@ -221,4 +220,4 @@ jar { publish { name jar.baseName -} \ No newline at end of file +} From 29aa9853627031d7df6fa7e2e42362d479f7902c Mon Sep 17 00:00:00 2001 From: James Brown <33660060+jamesbr3@users.noreply.github.com> Date: Fri, 21 Jun 2019 09:31:18 +0100 Subject: [PATCH 59/75] ENT-3541 remove unused commons-fileupload dep (#5206) --- node-api/build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/node-api/build.gradle b/node-api/build.gradle index 5aee94f5b8..4c96cc4499 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -18,10 +18,6 @@ dependencies { compile "org.apache.activemq:artemis-core-client:${artemis_version}" compile "org.apache.activemq:artemis-commons:${artemis_version}" - // For adding serialisation of file upload streams to RPC - // TODO: Remove this dependency and the code that requires it - compile "commons-fileupload:commons-fileupload:$fileupload_version" - // TypeSafe Config: for simple and human friendly config files. compile "com.typesafe:config:$typesafe_config_version" From 6bef2b52e75bcbb7b009cc90525c1ea8370574d3 Mon Sep 17 00:00:00 2001 From: James Brown <33660060+jamesbr3@users.noreply.github.com> Date: Fri, 21 Jun 2019 09:32:53 +0100 Subject: [PATCH 60/75] ENT-3535 Update Hibernate dependency (#5168) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e9fac934ef..84ff587a2c 100644 --- a/build.gradle +++ b/build.gradle @@ -44,7 +44,7 @@ buildscript { ext.hamkrest_version = '1.4.2.2' ext.jopt_simple_version = '5.0.2' ext.jansi_version = '1.14' - ext.hibernate_version = '5.3.6.Final' + ext.hibernate_version = '5.3.10.Final' ext.h2_version = '1.4.197' // Update docs if renamed or removed. ext.postgresql_version = '42.2.5' ext.rxjava_version = '1.3.8' From e8b55e8f2e6b84a344bbbe55e01951f91d36d849 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Fri, 21 Jun 2019 09:33:37 +0100 Subject: [PATCH 61/75] CORDA-2934 - disable hibernate validator integration with hibernate (#5130) (#5144) (cherry picked from commit 688c74270618b0b4fceed32748b5265de1441a51) --- docs/source/changelog.rst | 6 ++++++ .../nodeapi/internal/persistence/HibernateConfiguration.kt | 1 + 2 files changed, 7 insertions(+) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 34506e237f..5806b1669c 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -9,6 +9,12 @@ Unreleased * Fix a bug in Corda 4.0 that combined commands in ``TransactionBuilder`` if they only differed by the signers list. The behaviour is now consistent with prior Corda releases. +* Disabled the default loading of ``hibernate-validator`` as a plugin by hibernate when a CorDapp depends on it. This change will in turn fix the + (https://github.com/corda/corda/issues/4444) issue, because nodes will no longer need to add ``hibernate-validator`` to the ``\libs`` folder. + For nodes that already did that, it can be safely removed when the latest Corda is installed. + One thing to keep in mind is that if any CorDapp relied on hibernate-validator to validate Querayable JPA Entities via annotations, that will no longer happen. + That was a bad practice anyway, because the ``ContractState`` should be validated in the Contract verify method. + .. _changelog_v4.0: Version 4.0 diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt index 637848d155..fa5cfcbb24 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt @@ -89,6 +89,7 @@ class HibernateConfiguration( val config = Configuration(metadataSources).setProperty("hibernate.connection.provider_class", NodeDatabaseConnectionProvider::class.java.name) .setProperty("hibernate.format_sql", "true") .setProperty("hibernate.hbm2ddl.auto", hbm2dll) + .setProperty("javax.persistence.validation.mode", "none") .setProperty("hibernate.connection.isolation", databaseConfig.transactionIsolationLevel.jdbcValue.toString()) schemas.forEach { schema -> From 3ceb242ee148fed663964a12030e39c907872ab2 Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Fri, 21 Jun 2019 09:34:15 +0100 Subject: [PATCH 62/75] CORDA-2935 - Align timeouts for CRL retrieval and TLS handshake (#5126) --- .../internal/protonwrapper/netty/SSLHelper.kt | 10 ++++- .../CertificateRevocationListNodeTests.kt | 39 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt index 75d623d278..63128f3332 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelper.kt @@ -19,6 +19,8 @@ import java.util.* import javax.net.ssl.* private const val HOSTNAME_FORMAT = "%s.corda.net" +private const val SSL_HANDSHAKE_TIMEOUT_PROP_NAME = "corda.netty.sslHelper.handshakeTimeout" +private const val DEFAULT_SSL_TIMEOUT = 20000 // Aligned with sun.security.provider.certpath.URICertStore.DEFAULT_CRL_CONNECT_TIMEOUT internal class LoggingTrustManagerWrapper(val wrapped: X509ExtendedTrustManager) : X509ExtendedTrustManager() { companion object { @@ -123,7 +125,9 @@ internal fun createClientSslHelper(target: NetworkHostAndPort, sslParameters.serverNames = listOf(SNIHostName(x500toHostName(expectedRemoteLegalNames.single()))) sslEngine.sslParameters = sslParameters } - return SslHandler(sslEngine) + val sslHandler = SslHandler(sslEngine) + sslHandler.handshakeTimeoutMillis = Integer.getInteger(SSL_HANDSHAKE_TIMEOUT_PROP_NAME, DEFAULT_SSL_TIMEOUT).toLong() + return sslHandler } internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory, @@ -138,7 +142,9 @@ internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory, sslEngine.enabledProtocols = ArtemisTcpTransport.TLS_VERSIONS.toTypedArray() sslEngine.enabledCipherSuites = ArtemisTcpTransport.CIPHER_SUITES.toTypedArray() sslEngine.enableSessionCreation = true - return SslHandler(sslEngine) + val sslHandler = SslHandler(sslEngine) + sslHandler.handshakeTimeoutMillis = Integer.getInteger(SSL_HANDSHAKE_TIMEOUT_PROP_NAME, DEFAULT_SSL_TIMEOUT).toLong() + return sslHandler } internal fun initialiseTrustStoreAndEnableCrlChecking(trustStore: CertificateStore, crlCheckSoftFail: Boolean): ManagerFactoryParameters { diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt index 2b760c8e93..b9ad2fd99b 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt @@ -83,6 +83,9 @@ class CertificateRevocationListNodeTests { private abstract class AbstractNodeConfiguration : NodeConfiguration companion object { + + const val FORBIDDEN_CRL = "forbidden.crl" + fun createRevocationList(clrServer: CrlServer, signatureAlgorithm: String, caCertificate: X509Certificate, caPrivateKey: PrivateKey, endpoint: String, @@ -493,6 +496,13 @@ class CertificateRevocationListNodeTests { .build() } + @GET + @Path(FORBIDDEN_CRL) + @Produces("application/pkcs7-crl") + fun getNodeSlowCRL(): Response { + return Response.status(Response.Status.FORBIDDEN).build() + } + @GET @Path("intermediate.crl") @Produces("application/pkcs7-crl") @@ -588,4 +598,33 @@ class CertificateRevocationListNodeTests { ) }.withMessage("Unknown signature type requested: EC") } + + @Test + fun `AMPQ Client to Server connection succeeds when CRL retrieval is forbidden and soft fail is enabled`() { + val crlCheckSoftFail = true + val forbiddenUrl = "http://${server.hostAndPort}/crl/$FORBIDDEN_CRL" + val (amqpServer, _) = createServer( + serverPort, + crlCheckSoftFail = crlCheckSoftFail, + nodeCrlDistPoint = forbiddenUrl, + tlsCrlDistPoint = forbiddenUrl) + amqpServer.use { + amqpServer.start() + amqpServer.onReceive.subscribe { + it.complete(true) + } + val (amqpClient, _) = createClient( + serverPort, + crlCheckSoftFail, + nodeCrlDistPoint = forbiddenUrl, + tlsCrlDistPoint = forbiddenUrl) + amqpClient.use { + val serverConnected = amqpServer.onConnection.toFuture() + amqpClient.onConnection.toFuture() + amqpClient.start() + val serverConnect = serverConnected.get() + assertEquals(true, serverConnect.connected) + } + } + } } From 27707ad3cb18c9f5b178aac27c560b29e7e2973b Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Fri, 21 Jun 2019 09:34:52 +0100 Subject: [PATCH 63/75] CORDA-2511 - Upgrade notes for C4 need to include required minimum previous Corda version (#5039) (#5124) * CORDA-2511 doc fix: upgrade notes for C4 need to include guidance to upgrade from C3.2 (minimum) to C4 (cherry picked from commit ab8d4a312aff2ff4c1ddaa69f9306024c039b3fc) --- docs/source/node-upgrade-notes.rst | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/source/node-upgrade-notes.rst b/docs/source/node-upgrade-notes.rst index c552bb1e2d..d6c0da05b4 100644 --- a/docs/source/node-upgrade-notes.rst +++ b/docs/source/node-upgrade-notes.rst @@ -38,7 +38,16 @@ user guide to learn how to make backups. We provide some :ref:`backup recommendations ` if you'd like more detail. -Step 3. Replace ``corda.jar`` with the new version +Step 3. Upgrade the node database to Corda 3.2 or later +------------------------------------------------------- + +Ensure your node is running Corda 3.2 or later. +Corda 3.2 required a database table name change and column type change in PostgreSQL. +These changes need to be applied to the database before upgrading to Corda 4.0. +Refer to `Corda 3.2 release notes `_ +for further information. + +Step 4. Replace ``corda.jar`` with the new version -------------------------------------------------- Download the latest version of Corda from `our Artifactory site `_. @@ -47,13 +56,13 @@ node requires. .. important:: Corda 4 requires Java |java_version| or any higher Java 8 patchlevel. Java 9+ is not currently supported. -Step 4. Start up the node +Step 5. Start up the node ------------------------- Start the node in the usual manner you have selected. The node will perform any automatic data migrations required, which may take some time. If the migration process is interrupted it can be continued simply by starting the node again, without harm. -Step 5. Undrain the node +Step 6. Undrain the node ------------------------ You may now do any checks that you wish to perform, read the logs, and so on. When you are ready, use this command at the shell: From 571386570278db97e584e7914122e30a8fddc6e8 Mon Sep 17 00:00:00 2001 From: Katelyn Date: Fri, 21 Jun 2019 22:30:36 +0100 Subject: [PATCH 64/75] DOCS - Fix broken link --- docs/source/release-notes.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 7b2b97e319..df4be7cca6 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -4,11 +4,8 @@ Release notes .. contents:: :depth: 2 - .. _release_notes_v4_1: - - Corda 4.1 ========= @@ -16,7 +13,7 @@ It's been a little under 3 1/2 months since the release of Corda 4.0 and all of of tools Corda offers. Now, following the release of Corda Enterprise 4.0, we are proud to release Corda 4.1, bringing over 150 fixes and documentation updates to bring additional stability and quality of life improvements to those developing on the Corda platform. -Information on Corda Enterprise 4.0 can be found `here _ and +Information on Corda Enterprise 4.0 can be found `here `_ and `here `_. (It's worth noting that normally this document would have started with a comment about whether or not you'd been recently domiciled under some solidified mineral material regarding the release of Corda Enterprise 4.0. Alas, we made that joke when we shipped the first release of Corda after Enterprise 3.0 shipped, so the thunder has been stolen and repeating ourselves would be terribly gauche.) From 6c0519795468e5f15c2af7cace623096a52a9627 Mon Sep 17 00:00:00 2001 From: Stefano Franz Date: Wed, 26 Jun 2019 17:53:32 +0000 Subject: [PATCH 65/75] NOTICK - fix network builder for v4 (#5205) make builder more parallel by making docker node building async --- .../net/corda/bootstrapper/NetworkBuilder.kt | 58 ++++++++++++------- .../corda/bootstrapper/nodes/NodeBuilder.kt | 34 ++++++++--- .../src/main/resources/node-Dockerfile | 3 +- .../src/main/resources/notary-Dockerfile | 2 +- 4 files changed, 65 insertions(+), 32 deletions(-) diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/NetworkBuilder.kt b/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/NetworkBuilder.kt index 054a058c18..8cec323eb2 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/NetworkBuilder.kt +++ b/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/NetworkBuilder.kt @@ -5,8 +5,10 @@ import net.corda.bootstrapper.context.Context import net.corda.bootstrapper.nodes.* import net.corda.bootstrapper.notaries.NotaryCopier import net.corda.bootstrapper.notaries.NotaryFinder +import net.corda.node.utilities.NamedThreadFactory import java.io.File import java.util.concurrent.CompletableFuture +import java.util.concurrent.Executors interface NetworkBuilder { @@ -31,12 +33,10 @@ interface NetworkBuilder { fun onNodeStartBuild(callback: (FoundNode) -> Unit): NetworkBuilder fun onNodePushStart(callback: (BuiltNode) -> Unit): NetworkBuilder fun onNodeInstancesRequested(callback: (List) -> Unit): NetworkBuilder - } private class NetworkBuilderImpl : NetworkBuilder { - @Volatile private var onNodeLocatedCallback: ((FoundNode) -> Unit) = {} @Volatile @@ -76,7 +76,6 @@ private class NetworkBuilderImpl : NetworkBuilder { return this } - override fun onNodeStartBuild(callback: (FoundNode) -> Unit): NetworkBuilder { this.onNodeBuildStartCallback = callback return this; @@ -127,8 +126,10 @@ private class NetworkBuilderImpl : NetworkBuilder { return this; } - override fun build(): CompletableFuture, Context>> { + + val executor = Executors.newCachedThreadPool(NamedThreadFactory("network-builder")) + val cacheDir = File(workingDir, cacheDirName) val baseDir = workingDir!! val context = Context(networkName, backendType, backendOptions) @@ -163,12 +164,21 @@ private class NetworkBuilderImpl : NetworkBuilder { val notariesFuture = notaryDiscoveryFuture.thenCompose { copiedNotaries -> copiedNotaries .map { copiedNotary -> - nodeBuilder.buildNode(copiedNotary).also(onNodeBuiltCallback) - }.map { builtNotary -> - onNodePushStartCallback(builtNotary) - nodePusher.pushNode(builtNotary).thenApply { it.also(onNodePushedCallback) } + nodeBuilder.buildNode(copiedNotary).thenAlsoAsync { + onNodeBuildStartCallback.invoke(it) + } + }.map { builtNotaryFuture -> + builtNotaryFuture.thenComposeAsync { builtNotary -> + onNodeBuiltCallback(builtNotary) + onNodePushStartCallback(builtNotary) + nodePusher.pushNode(builtNotary).thenAlsoAsync { pushedNotary -> + onNodePushedCallback(pushedNotary) + } + } }.map { pushedNotary -> - pushedNotary.thenApplyAsync { nodeInstantiator.createInstanceRequest(it).also { onNodeInstanceRequestedCallback.invoke(listOf(it)) } } + pushedNotary.thenApplyAsync { + nodeInstantiator.createInstanceRequest(it).also { onNodeInstanceRequestedCallback(listOf(it)) } + } }.map { instanceRequest -> instanceRequest.thenComposeAsync { request -> nodeInstantiator.instantiateNotaryInstance(request).thenApply { it.also(onNodeInstanceCallback) } @@ -185,17 +195,16 @@ private class NetworkBuilderImpl : NetworkBuilder { } }.map { copiedNode: CopiedNode -> onNodeBuildStartCallback.invoke(copiedNode) - nodeBuilder.buildNode(copiedNode).let { - onNodeBuiltCallback.invoke(it) - it + nodeBuilder.buildNode(copiedNode) + }.map { builtNodeFuture -> + builtNodeFuture.thenComposeAsync { builtNode -> + onNodeBuiltCallback.invoke(builtNode) + nodePusher.pushNode(builtNode).thenAlsoAsync { pushedNode -> + onNodePushedCallback.invoke(pushedNode) + } } - }.map { builtNode -> - nodePusher.pushNode(builtNode).thenApplyAsync { - onNodePushedCallback.invoke(it) - it - } - }.map { pushedNode -> - pushedNode.thenApplyAsync { + }.map { pushedNodeFuture -> + pushedNodeFuture.thenApplyAsync { nodeInstantiator.createInstanceRequests(it, nodeCount).also(onNodeInstanceRequestedCallback) } @@ -213,10 +222,10 @@ private class NetworkBuilderImpl : NetworkBuilder { }.toSingleFuture() }.thenCompose { it }.thenApplyAsync { it.flatten() } - return notariesFuture.thenCombineAsync(nodesFuture, { _, nodeInstances -> + return notariesFuture.thenCombineAsync(nodesFuture) { _, nodeInstances -> context.networkInitiated = true nodeInstances to context - }) + } } } @@ -225,3 +234,10 @@ fun List>.toSingleFuture(): CompletableFuture> this.map { it.getNow(null) } } } + +fun CompletableFuture.thenAlsoAsync(consumer: (T) -> Unit): CompletableFuture { + return this.thenApplyAsync { + consumer(it) + it + } +} diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeBuilder.kt b/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeBuilder.kt index 2c4cfc157d..190da5da6f 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeBuilder.kt +++ b/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeBuilder.kt @@ -1,5 +1,7 @@ package net.corda.bootstrapper.nodes +import com.github.dockerjava.api.model.BuildResponseItem +import com.github.dockerjava.core.async.ResultCallbackTemplate import com.github.dockerjava.core.command.BuildImageResultCallback import com.typesafe.config.Config import com.typesafe.config.ConfigFactory @@ -11,6 +13,7 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.parseAsNodeConfiguration import org.slf4j.LoggerFactory import java.io.File +import java.util.concurrent.CompletableFuture open class NodeBuilder { @@ -18,7 +21,10 @@ open class NodeBuilder { val LOG = LoggerFactory.getLogger(NodeBuilder::class.java) } - fun buildNode(copiedNode: CopiedNode): BuiltNode { + fun buildNode(copiedNode: CopiedNode): CompletableFuture { + + val future: CompletableFuture = CompletableFuture() + val localDockerClient = DockerUtils.createLocalDockerClient() val copiedNodeConfig = copiedNode.copiedNodeConfig val nodeDir = copiedNodeConfig.parentFile @@ -27,15 +33,27 @@ open class NodeBuilder { } val nodeConfig = ConfigFactory.parseFile(copiedNodeConfig) LOG.info("starting to build docker image for: $nodeDir") - val nodeImageId = localDockerClient.buildImageCmd() + localDockerClient.buildImageCmd() .withDockerfile(File(nodeDir, "Dockerfile")) - .withBaseDirectory(nodeDir) - .exec(BuildImageResultCallback()).awaitImageId() - LOG.info("finished building docker image for: $nodeDir with id: $nodeImageId") - val config = nodeConfig.parseAsNodeConfigWithFallback(ConfigFactory.parseFile(copiedNode.configFile)).value() - return copiedNode.builtNode(config, nodeImageId) - } + .withBaseDirectory(nodeDir).exec(object : ResultCallbackTemplate() { + var result: BuildResponseItem? = null + override fun onNext(`object`: BuildResponseItem?) { + this.result = `object` + } + override fun onError(throwable: Throwable?) { + future.completeExceptionally(throwable) + } + + override fun onComplete() { + super.onComplete() + LOG.info("finished building docker image for: $nodeDir with id: ${result?.imageId}") + val config = nodeConfig.parseAsNodeConfigWithFallback(ConfigFactory.parseFile(copiedNode.configFile)).value() + future.complete(copiedNode.builtNode(config, result?.imageId!!)) + } + }) + return future + } } fun Config.parseAsNodeConfigWithFallback(preCopyConfig: Config): Validated { diff --git a/tools/network-bootstrapper/src/main/resources/node-Dockerfile b/tools/network-bootstrapper/src/main/resources/node-Dockerfile index 3ef62097b2..55dee2d520 100644 --- a/tools/network-bootstrapper/src/main/resources/node-Dockerfile +++ b/tools/network-bootstrapper/src/main/resources/node-Dockerfile @@ -1,5 +1,4 @@ -# Base image from (http://phusion.github.io/baseimage-docker) -FROM openjdk:8u151-jre-alpine +FROM openjdk:8u212-jre-alpine RUN apk upgrade --update && \ apk add --update --no-cache bash iputils && \ diff --git a/tools/network-bootstrapper/src/main/resources/notary-Dockerfile b/tools/network-bootstrapper/src/main/resources/notary-Dockerfile index d8a7b8a0a7..61db0bf801 100644 --- a/tools/network-bootstrapper/src/main/resources/notary-Dockerfile +++ b/tools/network-bootstrapper/src/main/resources/notary-Dockerfile @@ -1,5 +1,5 @@ # Base image from (http://phusion.github.io/baseimage-docker) -FROM openjdk:8u151-jre-alpine +FROM openjdk:8u212-jre-alpine RUN apk upgrade --update && \ apk add --update --no-cache bash iputils && \ From c6262b6ff9296c30c6302810a8d89c7aa313adbd Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Tue, 9 Jul 2019 12:25:20 +0100 Subject: [PATCH 66/75] Backport of CORDA-2998 fix network builder (#5265) (#5270) * CORDA-2998 fix network builder (#5265) CORDA-2998 fix network builder (cherry picked from commit f89008c070cd01a9dabf7dd94234fbff3de7bca6) * Network-builder - update to https://hub.docker.com/r/corda/corda-zulu-4.1 * Network-builder - update docs to correct 4.1 docker image version. --- build.gradle | 3 +- docs/source/network-builder.rst | 82 ++++++++++-------- settings.gradle | 2 +- .../corda/bootstrapper/nodes/NodeFinder.kt | 32 ------- .../bootstrapper/notaries/NotaryFinder.kt | 26 ------ .../src/main/resources/node-Dockerfile | 36 -------- .../src/main/resources/notary-Dockerfile | 39 --------- .../build.gradle | 30 +++++-- .../net/corda/networkbuilder}/GuiUtils.java | 5 +- .../net/corda/networkbuilder}/Constants.kt | 16 ++-- .../kotlin/net/corda/networkbuilder}/Main.kt | 18 ++-- .../corda/networkbuilder}/NetworkBuilder.kt | 34 ++++---- .../networkbuilder}/backends/AzureBackend.kt | 24 +++-- .../corda/networkbuilder}/backends/Backend.kt | 17 ++-- .../networkbuilder}/backends/DockerBackend.kt | 16 ++-- .../cli/CommandLineInterface.kt | 24 +++-- .../networkbuilder}/cli/CommandParsers.kt | 6 +- .../containers/instance/InstanceInfo.kt | 2 +- .../containers/instance/Instantiator.kt | 6 +- .../instance/azure/AzureInstantiator.kt | 22 +++-- .../instance/docker/DockerInstantiator.kt | 28 +++--- .../containers/push/ContainerPusher.kt | 2 +- .../push/azure/AzureContainerPusher.kt | 18 ++-- .../push/azure/AzureRegistryLocator.kt | 24 ++--- .../push/docker/DockerContainerPusher.kt | 7 +- .../corda/networkbuilder}/context/Context.kt | 14 ++- .../networkbuilder}/docker/DockerUtils.kt | 4 +- .../networkbuilder}/gui/BootstrapperView.kt | 40 +++++---- .../net/corda/networkbuilder}/gui/Gui.kt | 2 +- .../corda/networkbuilder}/nodes/BuiltNode.kt | 3 +- .../corda/networkbuilder}/nodes/CopiedNode.kt | 8 +- .../corda/networkbuilder}/nodes/FoundNode.kt | 18 ++-- .../corda/networkbuilder}/nodes/NodeAdder.kt | 6 +- .../networkbuilder}/nodes/NodeBuilder.kt | 8 +- .../corda/networkbuilder}/nodes/NodeCopier.kt | 26 ++++-- .../corda/networkbuilder/nodes/NodeFinder.kt | 38 ++++++++ .../networkbuilder}/nodes/NodeInstance.kt | 6 +- .../nodes/NodeInstanceRequest.kt | 7 +- .../networkbuilder}/nodes/NodeInstantiator.kt | 14 ++- .../corda/networkbuilder}/nodes/NodePusher.kt | 7 +- .../corda/networkbuilder}/nodes/PushedNode.kt | 4 +- .../networkbuilder}/notaries/CopiedNotary.kt | 7 +- .../networkbuilder}/notaries/NotaryCopier.kt | 24 ++--- .../serialization/SerializationHelper.kt | 3 +- .../corda/networkbuilder}/volumes/Volume.kt | 10 +-- .../volumes/azure/AzureSmbVolume.kt | 23 ++--- .../volumes/docker/LocalVolume.kt | 9 +- .../src/main/resources/node-Dockerfile | 21 +++++ .../src/main/resources/node_info_watcher.sh | 0 .../src/main/resources/node_killer.sh | 0 .../src/main/resources/notary-Dockerfile | 22 +++++ .../src/main/resources/rpc-settings.conf | 0 .../src/main/resources/run-corda-node.sh | 2 +- .../src/main/resources/run-corda-notary.sh | 2 +- .../src/main/resources/ssh.conf | 0 .../src/main/resources/views/bootstrapper.css | 0 .../src/main/resources/views/cordalogo.png | Bin .../src/main/resources/views/mainPane.fxml | 0 58 files changed, 382 insertions(+), 465 deletions(-) delete mode 100644 tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeFinder.kt delete mode 100644 tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/NotaryFinder.kt delete mode 100644 tools/network-bootstrapper/src/main/resources/node-Dockerfile delete mode 100644 tools/network-bootstrapper/src/main/resources/notary-Dockerfile rename tools/{network-bootstrapper => network-builder}/build.gradle (76%) rename tools/{network-bootstrapper/src/main/java/net/corda/bootstrapper => network-builder/src/main/java/net/corda/networkbuilder}/GuiUtils.java (98%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/Constants.kt (85%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/Main.kt (74%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/NetworkBuilder.kt (91%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/backends/AzureBackend.kt (77%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/backends/Backend.kt (68%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/backends/DockerBackend.kt (67%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/cli/CommandLineInterface.kt (84%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/cli/CommandParsers.kt (93%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/containers/instance/InstanceInfo.kt (82%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/containers/instance/Instantiator.kt (75%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/containers/instance/azure/AzureInstantiator.kt (85%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/containers/instance/docker/DockerInstantiator.kt (85%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/containers/push/ContainerPusher.kt (85%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/containers/push/azure/AzureContainerPusher.kt (83%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/containers/push/azure/AzureRegistryLocator.kt (84%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/containers/push/docker/DockerContainerPusher.kt (74%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/context/Context.kt (92%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/docker/DockerUtils.kt (96%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/gui/BootstrapperView.kt (93%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/gui/Gui.kt (86%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/BuiltNode.kt (95%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/CopiedNode.kt (92%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/FoundNode.kt (84%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/NodeAdder.kt (87%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/NodeBuilder.kt (87%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/NodeCopier.kt (81%) create mode 100644 tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeFinder.kt rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/NodeInstance.kt (94%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/NodeInstanceRequest.kt (83%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/NodeInstantiator.kt (93%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/NodePusher.kt (79%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/nodes/PushedNode.kt (95%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/notaries/CopiedNotary.kt (79%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/notaries/NotaryCopier.kt (78%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/serialization/SerializationHelper.kt (97%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/volumes/Volume.kt (92%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/volumes/azure/AzureSmbVolume.kt (87%) rename tools/{network-bootstrapper/src/main/kotlin/net/corda/bootstrapper => network-builder/src/main/kotlin/net/corda/networkbuilder}/volumes/docker/LocalVolume.kt (86%) create mode 100644 tools/network-builder/src/main/resources/node-Dockerfile rename tools/{network-bootstrapper => network-builder}/src/main/resources/node_info_watcher.sh (100%) mode change 100755 => 100644 rename tools/{network-bootstrapper => network-builder}/src/main/resources/node_killer.sh (100%) mode change 100755 => 100644 create mode 100644 tools/network-builder/src/main/resources/notary-Dockerfile rename tools/{network-bootstrapper => network-builder}/src/main/resources/rpc-settings.conf (100%) rename tools/{network-bootstrapper => network-builder}/src/main/resources/run-corda-node.sh (90%) mode change 100755 => 100644 rename tools/{network-bootstrapper => network-builder}/src/main/resources/run-corda-notary.sh (88%) mode change 100755 => 100644 rename tools/{network-bootstrapper => network-builder}/src/main/resources/ssh.conf (100%) rename tools/{network-bootstrapper => network-builder}/src/main/resources/views/bootstrapper.css (100%) rename tools/{network-bootstrapper => network-builder}/src/main/resources/views/cordalogo.png (100%) rename tools/{network-bootstrapper => network-builder}/src/main/resources/views/mainPane.fxml (100%) diff --git a/build.gradle b/build.gradle index 84ff587a2c..858e887321 100644 --- a/build.gradle +++ b/build.gradle @@ -381,7 +381,8 @@ bintrayConfig { 'corda-tools-cliutils', 'corda-common-configuration-parsing', 'corda-common-validation', - 'corda-common-logging' + 'corda-common-logging', + 'corda-tools-network-builder' ] license { name = 'Apache-2.0' diff --git a/docs/source/network-builder.rst b/docs/source/network-builder.rst index 2a498eb142..df8e4d175e 100644 --- a/docs/source/network-builder.rst +++ b/docs/source/network-builder.rst @@ -8,11 +8,15 @@ containers to abstract the complexity of managing a distributed network away fro .. image:: _static/images/network-builder-v4.png -The network you build will either be made up of local ``docker`` nodes *or* of nodes spread across Azure -containers. More backends may be added in future. The tool is open source, so contributions to add more +The network you build will either be made up of local ``Docker`` nodes *or* of nodes spread across Azure +containers. +For each node a separate Docker image is built based on `corda/corda-zulu-|corda_version| `_. +Unlike the official image, a `node.conf` file and CorDapps are embedded into the image +(they are not externally provided to the running container via volumes/mount points). +More backends may be added in future. The tool is open source, so contributions to add more destinations for the containers are welcome! -`Download the Corda Network Builder `_. +`Download the Corda Network Builder `_. .. _pre-requisites: @@ -41,9 +45,9 @@ the following layout: An easy way to build a valid set of nodes is by running ``deployNodes``. In this document, we will be using the output of running ``deployNodes`` for the `Example CorDapp `_: -1. ``git clone https://github.com/corda/cordapp-example`` -2. ``cd cordapp-example`` -3. ``./gradlew clean deployNodes`` +1. ``git clone https://github.com/corda/samples`` +2. ``cd samples/cordapp-example`` +3. ``./gradlew clean workflows-java:deployNodes`` Building a network via the command line --------------------------------------- @@ -54,24 +58,27 @@ Starting the nodes Quickstart Local Docker ~~~~~~~~~~~~~~~~~~~~~~~ -1. ``cd kotlin-source/build/nodes`` -2. ``java -jar -d .`` +1. ``cd workflows-java/build/nodes`` +2. ``java -jar -d .`` If you run ``docker ps`` to see the running containers, the following output should be displayed: .. sourcecode:: shell - CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES - 406868b4ba69 node-partyc:corda-network "/run-corda.sh" 17 seconds ago Up 16 seconds 0.0.0.0:32902->10003/tcp, 0.0.0.0:32895->10005/tcp, 0.0.0.0:32898->10020/tcp, 0.0.0.0:32900->12222/tcp partyc0 - 4546a2fa8de7 node-partyb:corda-network "/run-corda.sh" 17 seconds ago Up 17 seconds 0.0.0.0:32896->10003/tcp, 0.0.0.0:32899->10005/tcp, 0.0.0.0:32901->10020/tcp, 0.0.0.0:32903->12222/tcp partyb0 - c8c44c515bdb node-partya:corda-network "/run-corda.sh" 17 seconds ago Up 17 seconds 0.0.0.0:32894->10003/tcp, 0.0.0.0:32897->10005/tcp, 0.0.0.0:32892->10020/tcp, 0.0.0.0:32893->12222/tcp partya0 - cf7ab689f493 node-notary:corda-network "/run-corda.sh" 30 seconds ago Up 31 seconds 0.0.0.0:32888->10003/tcp, 0.0.0.0:32889->10005/tcp, 0.0.0.0:32890->10020/tcp, 0.0.0.0:32891->12222/tcp notary0 + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + 406868b4ba69 node-partyc:corda-network "run-corda" 17 seconds ago Up 16 seconds 0.0.0.0:32902->10003/tcp, 0.0.0.0:32895->10005/tcp, 0.0.0.0:32898->10020/tcp, 0.0.0.0:32900->12222/tcp partyc0 + 4546a2fa8de7 node-partyb:corda-network "run-corda" 17 seconds ago Up 17 seconds 0.0.0.0:32896->10003/tcp, 0.0.0.0:32899->10005/tcp, 0.0.0.0:32901->10020/tcp, 0.0.0.0:32903->12222/tcp partyb0 + c8c44c515bdb node-partya:corda-network "run-corda" 17 seconds ago Up 17 seconds 0.0.0.0:32894->10003/tcp, 0.0.0.0:32897->10005/tcp, 0.0.0.0:32892->10020/tcp, 0.0.0.0:32893->12222/tcp partya0 + cf7ab689f493 node-notary:corda-network "run-corda" 30 seconds ago Up 31 seconds 0.0.0.0:32888->10003/tcp, 0.0.0.0:32889->10005/tcp, 0.0.0.0:32890->10020/tcp, 0.0.0.0:32891->12222/tcp notary0 + +Depending on you machine performance, even after all containers are reported as running, +the underlying Corda nodes may be still starting and SSHing to a node may be not available immediately. Quickstart Remote Azure ~~~~~~~~~~~~~~~~~~~~~~~ 1. ``cd kotlin-source/build/nodes`` -2. ``java -jar -b AZURE -d .`` +2. ``java -jar -b AZURE -d .`` .. note:: The Azure configuration is handled by the az-cli utility. See the :ref:`pre-requisites`. @@ -95,21 +102,26 @@ You can interact with the nodes by SSHing into them on the port that is mapped t >>> run networkMapSnapshot [ - { "addresses" : [ "partya0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyA, L=London, C=GB" ], "platformVersion" : 3, "serial" : 1532701330613 }, - { "addresses" : [ "notary0:10020" ], "legalIdentitiesAndCerts" : [ "O=Notary, L=London, C=GB" ], "platformVersion" : 3, "serial" : 1532701305115 }, - { "addresses" : [ "partyc0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyC, L=Paris, C=FR" ], "platformVersion" : 3, "serial" : 1532701331608 }, - { "addresses" : [ "partyb0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyB, L=New York, C=US" ], "platformVersion" : 3, "serial" : 1532701330118 } + { "addresses" : [ "partya0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyA, L=London, C=GB" ], "platformVersion" : |platform_version|, "serial" : 1532701330613 }, + { "addresses" : [ "notary0:10020" ], "legalIdentitiesAndCerts" : [ "O=Notary, L=London, C=GB" ], "platformVersion" : |platform_version|, "serial" : 1532701305115 }, + { "addresses" : [ "partyc0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyC, L=Paris, C=FR" ], "platformVersion" : |platform_version|, "serial" : 1532701331608 }, + { "addresses" : [ "partyb0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyB, L=New York, C=US" ], "platformVersion" : |platform_version|, "serial" : 1532701330118 } ] >>> +You can also run a flow from cordapp-example: ``flow start com.example.flow.ExampleFlow$Initiator iouValue: 20, otherParty: "PartyB"`` + +To verify it, connect into the ``partyb0`` node and run ``run vaultQuery contractStateType: "com.example.state.IOUState"``. +The ``partyb0`` vault should contain ``IOUState``. + Adding additional nodes ^^^^^^^^^^^^^^^^^^^^^^^ It is possible to add additional nodes to the network by reusing the nodes you built earlier. For example, to add a node by reusing the existing ``PartyA`` node, you would run: -``java -jar --add "PartyA=O=PartyZ,L=London,C=GB"`` +``java -jar --add "PartyA=O=PartyZ,L=London,C=GB"`` To confirm the node has been started correctly, run the following in the previously connected SSH session: @@ -117,18 +129,18 @@ To confirm the node has been started correctly, run the following in the previou Tue Jul 17 15:47:14 GMT 2018>>> run networkMapSnapshot [ - { "addresses" : [ "partya0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyA, L=London, C=GB" ], "platformVersion" : 3, "serial" : 1532701330613 }, - { "addresses" : [ "notary0:10020" ], "legalIdentitiesAndCerts" : [ "O=Notary, L=London, C=GB" ], "platformVersion" : 3, "serial" : 1532701305115 }, - { "addresses" : [ "partyc0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyC, L=Paris, C=FR" ], "platformVersion" : 3, "serial" : 1532701331608 }, - { "addresses" : [ "partyb0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyB, L=New York, C=US" ], "platformVersion" : 3, "serial" : 1532701330118 }, - { "addresses" : [ "partya1:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyZ, L=London, C=GB" ], "platformVersion" : 3, "serial" : 1532701630861 } + { "addresses" : [ "partya0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyA, L=London, C=GB" ], "platformVersion" : |platform_version|, "serial" : 1532701330613 }, + { "addresses" : [ "notary0:10020" ], "legalIdentitiesAndCerts" : [ "O=Notary, L=London, C=GB" ], "platformVersion" : |platform_version|, "serial" : 1532701305115 }, + { "addresses" : [ "partyc0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyC, L=Paris, C=FR" ], "platformVersion" : |platform_version|, "serial" : 1532701331608 }, + { "addresses" : [ "partyb0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyB, L=New York, C=US" ], "platformVersion" : |platform_version|, "serial" : 1532701330118 }, + { "addresses" : [ "partya1:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyZ, L=London, C=GB" ], "platformVersion" : |platform_version|, "serial" : 1532701630861 } ] Building a network in Graphical User Mode ----------------------------------------- The Corda Network Builder also provides a GUI for when automated interactions are not required. To launch it, run -``java -jar -g``. +``java -jar -g``. Starting the nodes ^^^^^^^^^^^^^^^^^^ @@ -145,11 +157,11 @@ see the running containers, the following output should be displayed: .. sourcecode:: shell - CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES - 406868b4ba69 node-partyc:corda-network "/run-corda.sh" 17 seconds ago Up 16 seconds 0.0.0.0:32902->10003/tcp, 0.0.0.0:32895->10005/tcp, 0.0.0.0:32898->10020/tcp, 0.0.0.0:32900->12222/tcp partyc0 - 4546a2fa8de7 node-partyb:corda-network "/run-corda.sh" 17 seconds ago Up 17 seconds 0.0.0.0:32896->10003/tcp, 0.0.0.0:32899->10005/tcp, 0.0.0.0:32901->10020/tcp, 0.0.0.0:32903->12222/tcp partyb0 - c8c44c515bdb node-partya:corda-network "/run-corda.sh" 17 seconds ago Up 17 seconds 0.0.0.0:32894->10003/tcp, 0.0.0.0:32897->10005/tcp, 0.0.0.0:32892->10020/tcp, 0.0.0.0:32893->12222/tcp partya0 - cf7ab689f493 node-notary:corda-network "/run-corda.sh" 30 seconds ago Up 31 seconds 0.0.0.0:32888->10003/tcp, 0.0.0.0:32889->10005/tcp, 0.0.0.0:32890->10020/tcp, 0.0.0.0:32891->12222/tcp notary0 + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + 406868b4ba69 node-partyc:corda-network "run-corda" 17 seconds ago Up 16 seconds 0.0.0.0:32902->10003/tcp, 0.0.0.0:32895->10005/tcp, 0.0.0.0:32898->10020/tcp, 0.0.0.0:32900->12222/tcp partyc0 + 4546a2fa8de7 node-partyb:corda-network "run-corda" 17 seconds ago Up 17 seconds 0.0.0.0:32896->10003/tcp, 0.0.0.0:32899->10005/tcp, 0.0.0.0:32901->10020/tcp, 0.0.0.0:32903->12222/tcp partyb0 + c8c44c515bdb node-partya:corda-network "run-corda" 17 seconds ago Up 17 seconds 0.0.0.0:32894->10003/tcp, 0.0.0.0:32897->10005/tcp, 0.0.0.0:32892->10020/tcp, 0.0.0.0:32893->12222/tcp partya0 + cf7ab689f493 node-notary:corda-network "run-corda" 30 seconds ago Up 31 seconds 0.0.0.0:32888->10003/tcp, 0.0.0.0:32889->10005/tcp, 0.0.0.0:32890->10020/tcp, 0.0.0.0:32891->12222/tcp notary0 Interacting with the nodes ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -173,11 +185,11 @@ node has been started correctly, run the following in the previously connected S Tue Jul 17 15:47:14 GMT 2018>>> run networkMapSnapshot [ - { "addresses" : [ "partya0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyA, L=London, C=GB" ], "platformVersion" : 3, "serial" : 1532701330613 }, - { "addresses" : [ "notary0:10020" ], "legalIdentitiesAndCerts" : [ "O=Notary, L=London, C=GB" ], "platformVersion" : 3, "serial" : 1532701305115 }, - { "addresses" : [ "partyc0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyC, L=Paris, C=FR" ], "platformVersion" : 3, "serial" : 1532701331608 }, - { "addresses" : [ "partyb0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyB, L=New York, C=US" ], "platformVersion" : 3, "serial" : 1532701330118 }, - { "addresses" : [ "partya1:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyZ, L=London, C=GB" ], "platformVersion" : 3, "serial" : 1532701630861 } + { "addresses" : [ "partya0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyA, L=London, C=GB" ], "platformVersion" : |platform_version|, "serial" : 1532701330613 }, + { "addresses" : [ "notary0:10020" ], "legalIdentitiesAndCerts" : [ "O=Notary, L=London, C=GB" ], "platformVersion" : |platform_version|, "serial" : 1532701305115 }, + { "addresses" : [ "partyc0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyC, L=Paris, C=FR" ], "platformVersion" : |platform_version|, "serial" : 1532701331608 }, + { "addresses" : [ "partyb0:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyB, L=New York, C=US" ], "platformVersion" : |platform_version|, "serial" : 1532701330118 }, + { "addresses" : [ "partya1:10020" ], "legalIdentitiesAndCerts" : [ "O=PartyZ, L=London, C=GB" ], "platformVersion" : |platform_version|, "serial" : 1532701630861 } ] Shutting down the nodes diff --git a/settings.gradle b/settings.gradle index 5cf9069aa3..13f5d02953 100644 --- a/settings.gradle +++ b/settings.gradle @@ -41,7 +41,7 @@ include 'tools:bootstrapper' include 'tools:blobinspector' include 'tools:shell' include 'tools:shell-cli' -include 'tools:network-bootstrapper' +include 'tools:network-builder' include 'tools:cliutils' include 'tools:worldmap' include 'example-code' diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeFinder.kt b/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeFinder.kt deleted file mode 100644 index 1f4cd1adbd..0000000000 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeFinder.kt +++ /dev/null @@ -1,32 +0,0 @@ -package net.corda.bootstrapper.nodes - -import com.typesafe.config.ConfigException -import com.typesafe.config.ConfigFactory -import net.corda.bootstrapper.Constants -import net.corda.core.utilities.contextLogger -import java.io.File - -class NodeFinder(private val scratchDir: File) { - - fun findNodes(): List { - return scratchDir.walkBottomUp().filter { it.name == "node.conf" && !it.absolutePath.contains(Constants.BOOTSTRAPPER_DIR_NAME) }.map { - try { - ConfigFactory.parseFile(it) to it - } catch (e: ConfigException) { - null - } - }.filterNotNull() - .filter { !it.first.hasPath("notary") } - .map { (_, nodeConfigFile) -> - LOG.info("We've found a node with name: ${nodeConfigFile.parentFile.name}") - FoundNode(nodeConfigFile, nodeConfigFile.parentFile) - }.toList() - - } - - companion object { - val LOG = contextLogger() - } - -} - diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/NotaryFinder.kt b/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/NotaryFinder.kt deleted file mode 100644 index 2801141031..0000000000 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/NotaryFinder.kt +++ /dev/null @@ -1,26 +0,0 @@ -package net.corda.bootstrapper.notaries - -import com.typesafe.config.ConfigException -import com.typesafe.config.ConfigFactory -import net.corda.bootstrapper.Constants -import net.corda.bootstrapper.nodes.FoundNode -import java.io.File - -class NotaryFinder(private val dirToSearch: File) { - - fun findNotaries(): List { - return dirToSearch.walkBottomUp().filter { it.name == "node.conf" && !it.absolutePath.contains(Constants.BOOTSTRAPPER_DIR_NAME) } - .map { - try { - ConfigFactory.parseFile(it) to it - } catch (e: ConfigException) { - null - } - }.filterNotNull() - .filter { it.first.hasPath("notary") } - .map { (_, nodeConfigFile) -> - FoundNode(nodeConfigFile) - }.toList() - } -} - diff --git a/tools/network-bootstrapper/src/main/resources/node-Dockerfile b/tools/network-bootstrapper/src/main/resources/node-Dockerfile deleted file mode 100644 index 55dee2d520..0000000000 --- a/tools/network-bootstrapper/src/main/resources/node-Dockerfile +++ /dev/null @@ -1,36 +0,0 @@ -FROM openjdk:8u212-jre-alpine - -RUN apk upgrade --update && \ - apk add --update --no-cache bash iputils && \ - rm -rf /var/cache/apk/* && \ - # Add user to run the app && \ - addgroup corda && \ - adduser -G corda -D -s /bin/bash corda && \ - # Create /opt/corda directory && \ - mkdir -p /opt/corda/plugins && \ - mkdir -p /opt/corda/logs && \ - mkdir -p /opt/corda/additional-node-infos && \ - mkdir -p /opt/node-setup - -# Copy corda files -ADD --chown=corda:corda corda.jar /opt/corda/corda.jar -ADD --chown=corda:corda node.conf /opt/corda/node.conf -ADD --chown=corda:corda cordapps/ /opt/corda/cordapps - -# Copy node info watcher script -ADD --chown=corda:corda node_info_watcher.sh /opt/corda/ - -COPY run-corda.sh /run-corda.sh - -RUN chmod +x /run-corda.sh && \ - chmod +x /opt/corda/node_info_watcher.sh && \ - sync && \ - chown -R corda:corda /opt/corda - -# Working directory for Corda -WORKDIR /opt/corda -ENV HOME=/opt/corda -USER corda - -# Start it -CMD ["/run-corda.sh"] \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/resources/notary-Dockerfile b/tools/network-bootstrapper/src/main/resources/notary-Dockerfile deleted file mode 100644 index 61db0bf801..0000000000 --- a/tools/network-bootstrapper/src/main/resources/notary-Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -# Base image from (http://phusion.github.io/baseimage-docker) -FROM openjdk:8u212-jre-alpine - -RUN apk upgrade --update && \ - apk add --update --no-cache bash iputils && \ - rm -rf /var/cache/apk/* && \ - # Add user to run the app && \ - addgroup corda && \ - adduser -G corda -D -s /bin/bash corda && \ - # Create /opt/corda directory && \ - mkdir -p /opt/corda/plugins && \ - mkdir -p /opt/corda/logs && \ - mkdir -p /opt/corda/additional-node-infos && \ - mkdir -p /opt/node-setup - -# Copy corda files -ADD --chown=corda:corda corda.jar /opt/corda/corda.jar -ADD --chown=corda:corda node.conf /opt/corda/node.conf -ADD --chown=corda:corda cordapps/ /opt/corda/cordapps -ADD --chown=corda:corda certificates/ /opt/corda/certificates -#ADD --chown=corda:corda nodeInfo-* /opt/corda/ - -# Copy node info watcher script -ADD --chown=corda:corda node_info_watcher.sh /opt/corda/ - -COPY run-corda.sh /run-corda.sh - -RUN chmod +x /run-corda.sh && \ - chmod +x /opt/corda/node_info_watcher.sh && \ - sync && \ - chown -R corda:corda /opt/corda - -# Working directory for Corda -WORKDIR /opt/corda -ENV HOME=/opt/corda -USER corda - -# Start it -CMD ["/run-corda.sh"] \ No newline at end of file diff --git a/tools/network-bootstrapper/build.gradle b/tools/network-builder/build.gradle similarity index 76% rename from tools/network-bootstrapper/build.gradle rename to tools/network-builder/build.gradle index 316be6e723..0e68bf0dc5 100644 --- a/tools/network-bootstrapper/build.gradle +++ b/tools/network-builder/build.gradle @@ -8,9 +8,11 @@ apply plugin: 'idea' apply plugin: 'java' apply plugin: 'application' // We need to set mainClassName before applying the shadow plugin. -mainClassName = 'net.corda.bootstrapper.Main' +mainClassName = 'net.corda.networkbuilder.Main' apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'com.jfrog.artifactory' configurations { compile { @@ -50,14 +52,32 @@ tasks.withType(JavaCompile) { options.compilerArgs << '-proc:none' } -jar.enabled = false +processResources { + from file("$rootDir/config/dev/log4j2.xml") +} shadowJar { - baseName = 'network-bootstrapper' + baseName = 'network-builder' classifier = null version = null zip64 true } -task buildNetworkBootstrapper(dependsOn: shadowJar) -assemble.dependsOn buildNetworkBootstrapper +task buildNetworkBuilder(dependsOn: shadowJar) +assemble.dependsOn buildNetworkBuilder + +artifacts { + publish shadowJar { + classifier = "" + } +} + +jar { + classifier "ignore" + enabled = false +} + +publish { + disableDefaultJar = true + name 'corda-tools-network-builder' +} diff --git a/tools/network-bootstrapper/src/main/java/net/corda/bootstrapper/GuiUtils.java b/tools/network-builder/src/main/java/net/corda/networkbuilder/GuiUtils.java similarity index 98% rename from tools/network-bootstrapper/src/main/java/net/corda/bootstrapper/GuiUtils.java rename to tools/network-builder/src/main/java/net/corda/networkbuilder/GuiUtils.java index 97bbc243aa..bf84176a1f 100644 --- a/tools/network-bootstrapper/src/main/java/net/corda/bootstrapper/GuiUtils.java +++ b/tools/network-builder/src/main/java/net/corda/networkbuilder/GuiUtils.java @@ -1,4 +1,4 @@ -package net.corda.bootstrapper; +package net.corda.networkbuilder; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; @@ -56,5 +56,4 @@ public class GuiUtils { } catch (InterruptedException e) { } } - -} +} \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/Constants.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/Constants.kt similarity index 85% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/Constants.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/Constants.kt index d6e907d1b5..6fb3e962f3 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/Constants.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/Constants.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper +package net.corda.networkbuilder import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonParser @@ -12,12 +12,12 @@ import com.microsoft.azure.management.resources.fluentcore.arm.Region class Constants { companion object { - val NODE_P2P_PORT = 10020 - val NODE_SSHD_PORT = 12222 - val NODE_RPC_PORT = 10003 - val NODE_RPC_ADMIN_PORT = 10005 + const val NODE_P2P_PORT = 10020 + const val NODE_SSHD_PORT = 12222 + const val NODE_RPC_PORT = 10003 + const val NODE_RPC_ADMIN_PORT = 10005 - val BOOTSTRAPPER_DIR_NAME = ".bootstrapper" + const val BOOTSTRAPPER_DIR_NAME = ".bootstrapper" fun getContextMapper(): ObjectMapper { val objectMapper = ObjectMapper(YAMLFactory()).registerKotlinModule() @@ -39,12 +39,10 @@ class Constants { val ALPHA_NUMERIC_ONLY_REGEX = "[^\\p{IsAlphabetic}\\p{IsDigit}]".toRegex() val ALPHA_NUMERIC_DOT_AND_UNDERSCORE_ONLY_REGEX = "[^\\p{IsAlphabetic}\\p{IsDigit}._]".toRegex() - val REGION_ARG_NAME = "REGION" + const val REGION_ARG_NAME = "REGION" fun ResourceGroup.restFriendlyName(): String { return this.name().replace(ALPHA_NUMERIC_ONLY_REGEX, "").toLowerCase() } } - - } \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/Main.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/Main.kt similarity index 74% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/Main.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/Main.kt index 341e2bbfd9..1285f7f185 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/Main.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/Main.kt @@ -1,16 +1,16 @@ @file:JvmName("Main") -package net.corda.bootstrapper +package net.corda.networkbuilder import javafx.application.Application -import net.corda.bootstrapper.backends.Backend -import net.corda.bootstrapper.backends.Backend.BackendType.AZURE -import net.corda.bootstrapper.cli.AzureParser -import net.corda.bootstrapper.cli.CliParser -import net.corda.bootstrapper.cli.CommandLineInterface -import net.corda.bootstrapper.docker.DockerUtils -import net.corda.bootstrapper.gui.Gui -import net.corda.bootstrapper.serialization.SerializationEngine +import net.corda.networkbuilder.backends.Backend +import net.corda.networkbuilder.backends.Backend.BackendType.AZURE +import net.corda.networkbuilder.cli.AzureParser +import net.corda.networkbuilder.cli.CliParser +import net.corda.networkbuilder.cli.CommandLineInterface +import net.corda.networkbuilder.docker.DockerUtils +import net.corda.networkbuilder.gui.Gui +import net.corda.networkbuilder.serialization.SerializationEngine import picocli.CommandLine import javax.ws.rs.ProcessingException import kotlin.system.exitProcess diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/NetworkBuilder.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/NetworkBuilder.kt similarity index 91% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/NetworkBuilder.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/NetworkBuilder.kt index 8cec323eb2..c46a5be131 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/NetworkBuilder.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/NetworkBuilder.kt @@ -1,14 +1,11 @@ -package net.corda.bootstrapper +package net.corda.networkbuilder -import net.corda.bootstrapper.backends.Backend -import net.corda.bootstrapper.context.Context -import net.corda.bootstrapper.nodes.* -import net.corda.bootstrapper.notaries.NotaryCopier -import net.corda.bootstrapper.notaries.NotaryFinder -import net.corda.node.utilities.NamedThreadFactory +import net.corda.networkbuilder.backends.Backend +import net.corda.networkbuilder.context.Context +import net.corda.networkbuilder.nodes.* +import net.corda.networkbuilder.notaries.NotaryCopier import java.io.File import java.util.concurrent.CompletableFuture -import java.util.concurrent.Executors interface NetworkBuilder { @@ -23,8 +20,8 @@ interface NetworkBuilder { fun onNodeBuild(callback: (BuiltNode) -> Unit): NetworkBuilder fun onNodePushed(callback: (PushedNode) -> Unit): NetworkBuilder fun onNodeInstance(callback: (NodeInstance) -> Unit): NetworkBuilder - - fun withNetworkName(networtName: String): NetworkBuilder + /** Sets network name */ + fun withNetworkName(networkName: String): NetworkBuilder fun withBasedir(baseDir: File): NetworkBuilder fun withBackend(backendType: Backend.BackendType): NetworkBuilder fun withBackendOptions(options: Map): NetworkBuilder @@ -78,7 +75,7 @@ private class NetworkBuilderImpl : NetworkBuilder { override fun onNodeStartBuild(callback: (FoundNode) -> Unit): NetworkBuilder { this.onNodeBuildStartCallback = callback - return this; + return this } override fun onNodeBuild(callback: (BuiltNode) -> Unit): NetworkBuilder { @@ -97,12 +94,12 @@ private class NetworkBuilderImpl : NetworkBuilder { } override fun onNodeInstance(callback: (NodeInstance) -> Unit): NetworkBuilder { - this.onNodeInstanceCallback = callback; + this.onNodeInstanceCallback = callback return this } - override fun withNetworkName(networtName: String): NetworkBuilder { - this.networkName = networtName + override fun withNetworkName(networkName: String): NetworkBuilder { + this.networkName = networkName return this } @@ -122,21 +119,19 @@ private class NetworkBuilderImpl : NetworkBuilder { } override fun onNodePushStart(callback: (BuiltNode) -> Unit): NetworkBuilder { - this.onNodePushStartCallback = callback; - return this; + this.onNodePushStartCallback = callback + return this } override fun build(): CompletableFuture, Context>> { - val executor = Executors.newCachedThreadPool(NamedThreadFactory("network-builder")) - val cacheDir = File(workingDir, cacheDirName) val baseDir = workingDir!! val context = Context(networkName, backendType, backendOptions) if (cacheDir.exists()) cacheDir.deleteRecursively() val (containerPusher, instantiator, volume) = Backend.fromContext(context, cacheDir) val nodeFinder = NodeFinder(baseDir) - val notaryFinder = NotaryFinder(baseDir) + val notaryFinder = NodeFinder(baseDir) val notaryCopier = NotaryCopier(cacheDir) val nodeInstantiator = NodeInstantiator(instantiator, context) @@ -235,6 +230,7 @@ fun List>.toSingleFuture(): CompletableFuture> } } +/** Asynchronously runs `consumer` on the receiver CompletableFuture.*/ fun CompletableFuture.thenAlsoAsync(consumer: (T) -> Unit): CompletableFuture { return this.thenApplyAsync { consumer(it) diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/backends/AzureBackend.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/backends/AzureBackend.kt similarity index 77% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/backends/AzureBackend.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/backends/AzureBackend.kt index 93a9890d46..166b889be3 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/backends/AzureBackend.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/backends/AzureBackend.kt @@ -1,15 +1,15 @@ -package net.corda.bootstrapper.backends +package net.corda.networkbuilder.backends import com.microsoft.azure.CloudException import com.microsoft.azure.credentials.AzureCliCredentials import com.microsoft.azure.management.Azure import com.microsoft.rest.LogLevel -import net.corda.bootstrapper.Constants -import net.corda.bootstrapper.containers.instance.azure.AzureInstantiator -import net.corda.bootstrapper.containers.push.azure.AzureContainerPusher -import net.corda.bootstrapper.containers.push.azure.RegistryLocator -import net.corda.bootstrapper.context.Context -import net.corda.bootstrapper.volumes.azure.AzureSmbVolume +import net.corda.networkbuilder.Constants +import net.corda.networkbuilder.containers.instance.azure.AzureInstantiator +import net.corda.networkbuilder.containers.push.azure.AzureContainerPusher +import net.corda.networkbuilder.containers.push.azure.RegistryLocator +import net.corda.networkbuilder.context.Context +import net.corda.networkbuilder.volumes.azure.AzureSmbVolume import org.slf4j.LoggerFactory import java.util.concurrent.CompletableFuture @@ -49,14 +49,12 @@ data class AzureBackend(override val containerPusher: AzureContainerPusher, RegistryLocator(azure, resourceGroup) } val containerPusherFuture = registryLocatorFuture.thenApplyAsync { - AzureContainerPusher(azure, it.registry) + AzureContainerPusher(it.registry) } val azureNetworkStore = CompletableFuture.supplyAsync { AzureSmbVolume(azure, resourceGroup) } - val azureInstantiatorFuture = azureNetworkStore.thenCombine(registryLocatorFuture, - { azureVolume, registryLocator -> - AzureInstantiator(azure, registryLocator.registry, azureVolume, resourceGroup) - } - ) + val azureInstantiatorFuture = azureNetworkStore.thenCombine(registryLocatorFuture) { azureVolume, registryLocator -> + AzureInstantiator(azure, registryLocator.registry, azureVolume, resourceGroup) + } return AzureBackend(containerPusherFuture.get(), azureInstantiatorFuture.get(), azureNetworkStore.get()) } } diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/backends/Backend.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/backends/Backend.kt similarity index 68% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/backends/Backend.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/backends/Backend.kt index 2737411593..749dc4af7c 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/backends/Backend.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/backends/Backend.kt @@ -1,11 +1,11 @@ -package net.corda.bootstrapper.backends +package net.corda.networkbuilder.backends -import net.corda.bootstrapper.backends.Backend.BackendType.AZURE -import net.corda.bootstrapper.backends.Backend.BackendType.LOCAL_DOCKER -import net.corda.bootstrapper.containers.instance.Instantiator -import net.corda.bootstrapper.containers.push.ContainerPusher -import net.corda.bootstrapper.context.Context -import net.corda.bootstrapper.volumes.Volume +import net.corda.networkbuilder.backends.Backend.BackendType.AZURE +import net.corda.networkbuilder.backends.Backend.BackendType.LOCAL_DOCKER +import net.corda.networkbuilder.containers.instance.Instantiator +import net.corda.networkbuilder.containers.push.ContainerPusher +import net.corda.networkbuilder.context.Context +import net.corda.networkbuilder.volumes.Volume import java.io.File interface Backend { @@ -26,12 +26,9 @@ interface Backend { AZURE("Azure Containers"), LOCAL_DOCKER("Local Docker"); - override fun toString(): String { return this.displayName } - - } operator fun component1(): ContainerPusher { diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/backends/DockerBackend.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/backends/DockerBackend.kt similarity index 67% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/backends/DockerBackend.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/backends/DockerBackend.kt index fab1c3a24f..484e22f5f0 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/backends/DockerBackend.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/backends/DockerBackend.kt @@ -1,16 +1,15 @@ -package net.corda.bootstrapper.backends +package net.corda.networkbuilder.backends -import net.corda.bootstrapper.containers.instance.docker.DockerInstantiator -import net.corda.bootstrapper.containers.push.docker.DockerContainerPusher -import net.corda.bootstrapper.context.Context -import net.corda.bootstrapper.volumes.docker.LocalVolume +import net.corda.networkbuilder.containers.instance.docker.DockerInstantiator +import net.corda.networkbuilder.containers.push.docker.DockerContainerPusher +import net.corda.networkbuilder.context.Context +import net.corda.networkbuilder.volumes.docker.LocalVolume import java.io.File class DockerBackend(override val containerPusher: DockerContainerPusher, override val instantiator: DockerInstantiator, override val volume: LocalVolume) : Backend { - companion object { fun fromContext(context: Context, baseDir: File): DockerBackend { val dockerContainerPusher = DockerContainerPusher() @@ -19,7 +18,4 @@ class DockerBackend(override val containerPusher: DockerContainerPusher, return DockerBackend(dockerContainerPusher, dockerInstantiator, localVolume) } } - -} - - +} \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/cli/CommandLineInterface.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/cli/CommandLineInterface.kt similarity index 84% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/cli/CommandLineInterface.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/cli/CommandLineInterface.kt index 5a385dbd6d..561070b083 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/cli/CommandLineInterface.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/cli/CommandLineInterface.kt @@ -1,13 +1,13 @@ -package net.corda.bootstrapper.cli +package net.corda.networkbuilder.cli import com.fasterxml.jackson.databind.ObjectMapper -import net.corda.bootstrapper.Constants -import net.corda.bootstrapper.NetworkBuilder -import net.corda.bootstrapper.backends.Backend -import net.corda.bootstrapper.context.Context -import net.corda.bootstrapper.nodes.NodeAdder -import net.corda.bootstrapper.nodes.NodeInstantiator -import net.corda.bootstrapper.toSingleFuture +import net.corda.networkbuilder.Constants +import net.corda.networkbuilder.NetworkBuilder +import net.corda.networkbuilder.backends.Backend +import net.corda.networkbuilder.context.Context +import net.corda.networkbuilder.nodes.NodeAdder +import net.corda.networkbuilder.nodes.NodeInstantiator +import net.corda.networkbuilder.toSingleFuture import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.getOrThrow import java.io.File @@ -44,13 +44,12 @@ class CommandLineInterface { }.toSingleFuture().getOrThrow() persistContext(contextFile, objectMapper, context) } - } private fun setupContextFromExisting(contextFile: File, objectMapper: ObjectMapper): Context { - return contextFile.let { - if (it.exists()) { - it.inputStream().use { + return contextFile.let { file -> + if (file.exists()) { + file.inputStream().use { objectMapper.readValue(it, Context::class.java) } } else { @@ -59,7 +58,6 @@ class CommandLineInterface { } } - private fun persistContext(contextFile: File, objectMapper: ObjectMapper, context: Context?) { contextFile.outputStream().use { objectMapper.writeValue(it, context) diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/cli/CommandParsers.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/cli/CommandParsers.kt similarity index 93% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/cli/CommandParsers.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/cli/CommandParsers.kt index fce00d51ba..b6277b5b69 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/cli/CommandParsers.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/cli/CommandParsers.kt @@ -1,8 +1,8 @@ -package net.corda.bootstrapper.cli +package net.corda.networkbuilder.cli import com.microsoft.azure.management.resources.fluentcore.arm.Region -import net.corda.bootstrapper.Constants -import net.corda.bootstrapper.backends.Backend +import net.corda.networkbuilder.Constants +import net.corda.networkbuilder.backends.Backend import picocli.CommandLine import picocli.CommandLine.Option import java.io.File diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/InstanceInfo.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/InstanceInfo.kt similarity index 82% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/InstanceInfo.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/InstanceInfo.kt index 0a9c863e0c..9120207a41 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/InstanceInfo.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/InstanceInfo.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.containers.instance +package net.corda.networkbuilder.containers.instance data class InstanceInfo(val groupId: String, val instanceName: String, diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/Instantiator.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/Instantiator.kt similarity index 75% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/Instantiator.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/Instantiator.kt index 1f7c963115..faff36a9ed 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/Instantiator.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/Instantiator.kt @@ -1,17 +1,15 @@ -package net.corda.bootstrapper.containers.instance +package net.corda.networkbuilder.containers.instance import java.util.concurrent.CompletableFuture - interface Instantiator { fun instantiateContainer(imageId: String, portsToOpen: List, instanceName: String, env: Map? = null): CompletableFuture>> - companion object { - val ADDITIONAL_NODE_INFOS_PATH = "/opt/corda/additional-node-infos" + const val ADDITIONAL_NODE_INFOS_PATH = "/opt/corda/additional-node-infos" } fun getExpectedFQDN(instanceName: String): String diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/azure/AzureInstantiator.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/azure/AzureInstantiator.kt similarity index 85% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/azure/AzureInstantiator.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/azure/AzureInstantiator.kt index f537a5420a..4b6b80d004 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/azure/AzureInstantiator.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/azure/AzureInstantiator.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.containers.instance.azure +package net.corda.networkbuilder.containers.instance.azure import com.microsoft.azure.management.Azure import com.microsoft.azure.management.containerinstance.ContainerGroup @@ -6,11 +6,11 @@ import com.microsoft.azure.management.containerinstance.ContainerGroupRestartPol import com.microsoft.azure.management.containerregistry.Registry import com.microsoft.azure.management.resources.ResourceGroup import com.microsoft.rest.ServiceCallback -import net.corda.bootstrapper.Constants.Companion.restFriendlyName -import net.corda.bootstrapper.containers.instance.Instantiator -import net.corda.bootstrapper.containers.instance.Instantiator.Companion.ADDITIONAL_NODE_INFOS_PATH -import net.corda.bootstrapper.containers.push.azure.RegistryLocator.Companion.parseCredentials -import net.corda.bootstrapper.volumes.azure.AzureSmbVolume +import net.corda.networkbuilder.Constants.Companion.restFriendlyName +import net.corda.networkbuilder.containers.instance.Instantiator +import net.corda.networkbuilder.containers.instance.Instantiator.Companion.ADDITIONAL_NODE_INFOS_PATH +import net.corda.networkbuilder.containers.push.azure.RegistryLocator.Companion.parseCredentials +import net.corda.networkbuilder.volumes.azure.AzureSmbVolume import org.slf4j.LoggerFactory import java.util.concurrent.CompletableFuture @@ -28,9 +28,9 @@ class AzureInstantiator(private val azure: Azure, LOG.info("Starting instantiation of container: $instanceName using $imageId") val registryAddress = registry.loginServerUrl() - val (username, password) = registry.parseCredentials(); + val (username, password) = registry.parseCredentials() val mountName = "node-setup" - val future = CompletableFuture>>().also { + return CompletableFuture>>().also { azure.containerGroups().define(buildIdent(instanceName)) .withRegion(resourceGroup.region()) .withExistingResourceGroup(resourceGroup) @@ -56,11 +56,10 @@ class AzureInstantiator(private val azure: Azure, override fun success(result: ContainerGroup) { val fqdn = result.fqdn() LOG.info("Completed instantiation: $instanceName is running at $fqdn with port(s) $portsToOpen exposed") - it.complete(result.fqdn() to portsToOpen.map { it to it }.toMap()) + it.complete(result.fqdn() to portsToOpen.map { port -> port to port }.toMap()) } }) } - return future } private fun buildIdent(instanceName: String) = "$instanceName-${resourceGroup.restFriendlyName()}" @@ -75,11 +74,10 @@ class AzureInstantiator(private val azure: Azure, LOG.info("Found an existing instance of: $containerName destroying ContainerGroup") azure.containerGroups().deleteByResourceGroup(resourceGroup.name(), containerName) } - return existingContainer; + return existingContainer } companion object { val LOG = LoggerFactory.getLogger(AzureInstantiator::class.java) } - } \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/docker/DockerInstantiator.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/docker/DockerInstantiator.kt similarity index 85% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/docker/DockerInstantiator.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/docker/DockerInstantiator.kt index eaf0bf6882..6b3fee03a8 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/instance/docker/DockerInstantiator.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/instance/docker/DockerInstantiator.kt @@ -1,19 +1,18 @@ -package net.corda.bootstrapper.containers.instance.docker +package net.corda.networkbuilder.containers.instance.docker import com.github.dockerjava.api.model.* -import net.corda.bootstrapper.Constants -import net.corda.bootstrapper.containers.instance.Instantiator -import net.corda.bootstrapper.context.Context -import net.corda.bootstrapper.docker.DockerUtils -import net.corda.bootstrapper.volumes.docker.LocalVolume +import net.corda.networkbuilder.Constants +import net.corda.networkbuilder.containers.instance.Instantiator +import net.corda.networkbuilder.context.Context +import net.corda.networkbuilder.docker.DockerUtils +import net.corda.networkbuilder.volumes.docker.LocalVolume import org.slf4j.LoggerFactory import java.util.concurrent.CompletableFuture - class DockerInstantiator(private val volume: LocalVolume, private val context: Context) : Instantiator { - val networkId = setupNetwork(); + private val networkId = setupNetwork() override fun instantiateContainer(imageId: String, portsToOpen: List, @@ -43,7 +42,9 @@ class DockerInstantiator(private val volume: LocalVolume, } LOG.info("starting local docker instance of: $imageId with name $instanceName and env: $env") - val ports = (portsToOpen + Constants.NODE_RPC_ADMIN_PORT).map { ExposedPort.tcp(it) }.map { PortBinding(null, it) }.let { Ports(*it.toTypedArray()) } + val ports = (portsToOpen + Constants.NODE_RPC_ADMIN_PORT).map { ExposedPort.tcp(it) } + .map { PortBinding(null, it) } + .let { Ports(*it.toTypedArray()) } val createCmd = localClient.createContainerCmd(imageId) .withName(instanceName) .withVolumes(nodeInfosVolume) @@ -56,25 +57,20 @@ class DockerInstantiator(private val volume: LocalVolume, localClient.startContainerCmd(createCmd.id).exec() val foundContainer = localClient.listContainersCmd().exec() - .filter { it.id == (createCmd.id) } - .firstOrNull() + .firstOrNull { it.id == createCmd.id } val portMappings = foundContainer?.ports?.map { (it.privatePort ?: 0) to (it.publicPort ?: 0) }?.toMap()?.toMap() ?: portsToOpen.map { it to it }.toMap() - - return CompletableFuture.completedFuture(("localhost") to portMappings) } private fun buildDockerEnv(env: Map?) = (env ?: emptyMap()).entries.map { (key, value) -> "$key=$value" }.toList() - override fun getExpectedFQDN(instanceName: String): String { - return instanceName - } + override fun getExpectedFQDN(instanceName: String): String = instanceName private fun setupNetwork(): String { val createLocalDockerClient = DockerUtils.createLocalDockerClient() diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/ContainerPusher.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/ContainerPusher.kt similarity index 85% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/ContainerPusher.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/ContainerPusher.kt index 13714586ac..b2b3dd231d 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/ContainerPusher.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/ContainerPusher.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.containers.push +package net.corda.networkbuilder.containers.push import java.util.concurrent.CompletableFuture diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/azure/AzureContainerPusher.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/azure/AzureContainerPusher.kt similarity index 83% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/azure/AzureContainerPusher.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/azure/AzureContainerPusher.kt index 2c57bb1a2e..940c92cc51 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/azure/AzureContainerPusher.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/azure/AzureContainerPusher.kt @@ -1,25 +1,21 @@ -package net.corda.bootstrapper.containers.push.azure +package net.corda.networkbuilder.containers.push.azure import com.github.dockerjava.api.async.ResultCallback import com.github.dockerjava.api.model.PushResponseItem -import com.microsoft.azure.management.Azure import com.microsoft.azure.management.containerregistry.Registry -import net.corda.bootstrapper.containers.push.ContainerPusher -import net.corda.bootstrapper.containers.push.azure.RegistryLocator.Companion.parseCredentials -import net.corda.bootstrapper.docker.DockerUtils +import net.corda.networkbuilder.containers.push.ContainerPusher +import net.corda.networkbuilder.containers.push.azure.RegistryLocator.Companion.parseCredentials +import net.corda.networkbuilder.docker.DockerUtils import org.slf4j.LoggerFactory import java.io.Closeable import java.util.concurrent.CompletableFuture - -class AzureContainerPusher(private val azure: Azure, private val azureRegistry: Registry) : ContainerPusher { - +class AzureContainerPusher(private val azureRegistry: Registry) : ContainerPusher { override fun pushContainerToImageRepository(localImageId: String, remoteImageName: String, networkName: String): CompletableFuture { - val (registryUser, registryPassword) = azureRegistry.parseCredentials() val dockerClient = DockerUtils.createDockerClient( azureRegistry.loginServerUrl(), @@ -57,6 +53,4 @@ class AzureContainerPusher(private val azure: Azure, private val azureRegistry: companion object { val LOG = LoggerFactory.getLogger(AzureContainerPusher::class.java) } - -} - +} \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/azure/AzureRegistryLocator.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/azure/AzureRegistryLocator.kt similarity index 84% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/azure/AzureRegistryLocator.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/azure/AzureRegistryLocator.kt index f9ee36c259..c55f1f148f 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/azure/AzureRegistryLocator.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/azure/AzureRegistryLocator.kt @@ -1,37 +1,34 @@ -package net.corda.bootstrapper.containers.push.azure +package net.corda.networkbuilder.containers.push.azure import com.microsoft.azure.management.Azure import com.microsoft.azure.management.containerregistry.AccessKeyType import com.microsoft.azure.management.containerregistry.Registry import com.microsoft.azure.management.resources.ResourceGroup -import net.corda.bootstrapper.Constants.Companion.restFriendlyName -import net.corda.bootstrapper.containers.instance.azure.AzureInstantiator +import net.corda.networkbuilder.Constants.Companion.restFriendlyName +import net.corda.networkbuilder.containers.instance.azure.AzureInstantiator import org.slf4j.LoggerFactory class RegistryLocator(private val azure: Azure, private val resourceGroup: ResourceGroup) { - val registry: Registry = locateRegistry() - private fun locateRegistry(): Registry { LOG.info("Attempting to find existing registry with name: ${resourceGroup.restFriendlyName()}") val found = azure.containerRegistries().getByResourceGroup(resourceGroup.name(), resourceGroup.restFriendlyName()) - if (found == null) { + return if (found == null) { LOG.info("Did not find existing container registry - creating new registry with name ${resourceGroup.restFriendlyName()}") - return azure.containerRegistries() + azure.containerRegistries() .define(resourceGroup.restFriendlyName()) .withRegion(resourceGroup.region().name()) .withExistingResourceGroup(resourceGroup) .withBasicSku() .withRegistryNameAsAdminUser() .create() - } else { LOG.info("found existing registry with name: ${resourceGroup.restFriendlyName()} reusing") - return found + found } } @@ -44,12 +41,5 @@ class RegistryLocator(private val azure: Azure, } val LOG = LoggerFactory.getLogger(AzureInstantiator::class.java) - } - - -} - - - - +} \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/docker/DockerContainerPusher.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/docker/DockerContainerPusher.kt similarity index 74% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/docker/DockerContainerPusher.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/docker/DockerContainerPusher.kt index 8c53e6edae..ae5de5aa17 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/containers/push/docker/DockerContainerPusher.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/containers/push/docker/DockerContainerPusher.kt @@ -1,12 +1,11 @@ -package net.corda.bootstrapper.containers.push.docker +package net.corda.networkbuilder.containers.push.docker -import net.corda.bootstrapper.containers.push.ContainerPusher -import net.corda.bootstrapper.docker.DockerUtils +import net.corda.networkbuilder.containers.push.ContainerPusher +import net.corda.networkbuilder.docker.DockerUtils import java.util.concurrent.CompletableFuture class DockerContainerPusher : ContainerPusher { - override fun pushContainerToImageRepository(localImageId: String, remoteImageName: String, networkName: String): CompletableFuture { val dockerClient = DockerUtils.createLocalDockerClient() dockerClient.tagImageCmd(localImageId, remoteImageName, networkName).withForce().exec() diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/context/Context.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/context/Context.kt similarity index 92% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/context/Context.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/context/Context.kt index ab689eb47f..30046f2dfc 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/context/Context.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/context/Context.kt @@ -1,15 +1,14 @@ -package net.corda.bootstrapper.context +package net.corda.networkbuilder.context -import net.corda.bootstrapper.Constants -import net.corda.bootstrapper.backends.Backend -import net.corda.bootstrapper.nodes.NodeInstanceRequest +import net.corda.networkbuilder.Constants +import net.corda.networkbuilder.backends.Backend +import net.corda.networkbuilder.nodes.NodeInstanceRequest import net.corda.core.identity.CordaX500Name import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet import java.util.concurrent.ConcurrentHashMap class Context(val networkName: String, val backendType: Backend.BackendType, backendOptions: Map = emptyMap()) { - @Volatile var safeNetworkName: String = networkName.replace(Constants.ALPHA_NUMERIC_ONLY_REGEX, "").toLowerCase() @@ -30,7 +29,6 @@ class Context(val networkName: String, val backendType: Backend.BackendType, bac registerNode(request.name, request) } - data class PersistableNodeInstance( val groupName: String, val groupX500: CordaX500Name?, @@ -43,7 +41,6 @@ class Context(val networkName: String, val backendType: Backend.BackendType, bac val rpcUser: String, val rpcPassword: String) - companion object { fun fromInstanceRequest(nodeInstanceRequest: NodeInstanceRequest): PersistableNodeInstance { return PersistableNodeInstance( @@ -58,11 +55,10 @@ class Context(val networkName: String, val backendType: Backend.BackendType, bac "", "" ) - } } private fun NodeInstanceRequest.toPersistable(): PersistableNodeInstance { return fromInstanceRequest(this) } -} +} \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/docker/DockerUtils.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/docker/DockerUtils.kt similarity index 96% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/docker/DockerUtils.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/docker/DockerUtils.kt index f4c5680b75..342291a863 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/docker/DockerUtils.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/docker/DockerUtils.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.docker +package net.corda.networkbuilder.docker import com.github.dockerjava.api.DockerClient import com.github.dockerjava.core.DefaultDockerClientConfig @@ -30,6 +30,4 @@ object DockerUtils { .withRegistryPassword(password) .build() } - - } \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/gui/BootstrapperView.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/gui/BootstrapperView.kt similarity index 93% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/gui/BootstrapperView.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/gui/BootstrapperView.kt index 750185e254..36f851494b 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/gui/BootstrapperView.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/gui/BootstrapperView.kt @@ -1,6 +1,7 @@ -package net.corda.bootstrapper.gui +package net.corda.networkbuilder.gui import com.microsoft.azure.management.resources.fluentcore.arm.Region +import javafx.beans.binding.Bindings import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty import javafx.collections.ObservableListBase @@ -13,14 +14,13 @@ import javafx.scene.layout.HBox import javafx.scene.layout.Priority import javafx.scene.layout.VBox import javafx.stage.DirectoryChooser -import net.corda.bootstrapper.Constants -import net.corda.bootstrapper.GuiUtils -import net.corda.bootstrapper.NetworkBuilder -import net.corda.bootstrapper.backends.Backend -import net.corda.bootstrapper.baseArgs -import net.corda.bootstrapper.context.Context -import net.corda.bootstrapper.nodes.* -import net.corda.bootstrapper.notaries.NotaryFinder +import net.corda.networkbuilder.Constants +import net.corda.networkbuilder.GuiUtils +import net.corda.networkbuilder.NetworkBuilder +import net.corda.networkbuilder.backends.Backend +import net.corda.networkbuilder.baseArgs +import net.corda.networkbuilder.context.Context +import net.corda.networkbuilder.nodes.* import net.corda.core.identity.CordaX500Name import org.apache.commons.lang3.RandomStringUtils import org.controlsfx.control.SegmentedButton @@ -50,7 +50,7 @@ class BootstrapperView : View("Corda Network Builder") { visuallyTweakBackendSelector() buildButton.run { - enableWhen { controller.baseDir.isNotNull } + enableWhen { controller.hasNodesOrNotaries.and(controller.baseDir.isNotNull) } action { var networkName = "corda-network" @@ -104,7 +104,6 @@ class BootstrapperView : View("Corda Network Builder") { override fun get(index: Int): String { return controller.foundNodes[index].id } - override val size: Int get() = controller.foundNodes.size } @@ -139,7 +138,8 @@ class BootstrapperView : View("Corda Network Builder") { it.instanceName, it.instanceAddress, it.reachableAddress, - it.portMapping[Constants.NODE_P2P_PORT] ?: Constants.NODE_P2P_PORT, + it.portMapping[Constants.NODE_P2P_PORT] + ?: Constants.NODE_P2P_PORT, it.portMapping[Constants.NODE_SSHD_PORT] ?: Constants.NODE_SSHD_PORT)) } @@ -160,7 +160,7 @@ class BootstrapperView : View("Corda Network Builder") { columnResizePolicy = TableView.CONSTRAINED_RESIZE_POLICY hgrow = Priority.ALWAYS - onMouseClicked = EventHandler { _ -> + onMouseClicked = EventHandler { val selectedItem: NodeTemplateInfo = selectionModel.selectedItem ?: return@EventHandler infoTextArea.text = YAML_MAPPER.writeValueAsString(translateForPrinting(selectedItem)) } @@ -221,17 +221,17 @@ class BootstrapperView : View("Corda Network Builder") { nodeFinder.findNodes() } val foundNotaries = CompletableFuture.supplyAsync { - val notaryFinder = NotaryFinder(dir) + val notaryFinder = NodeFinder(dir) notaryFinder.findNotaries() } foundNodes.thenCombine(foundNotaries) { nodes, notaries -> notaries to nodes - }.thenAcceptAsync({ (notaries: List, nodes: List) -> + }.thenAcceptAsync { (notaries: List, nodes: List) -> runLater { controller.foundNodes(nodes) controller.notaries(notaries) } - }) + } } class NodeTemplateInfo(templateId: String, type: NodeType) { @@ -260,7 +260,7 @@ class BootstrapperView : View("Corda Network Builder") { val foundNodes = Collections.synchronizedList(ArrayList()).observable() val foundNotaries = Collections.synchronizedList(ArrayList()).observable() val networkContext = SimpleObjectProperty(null) - + var hasNodesOrNotaries = Bindings.size(foundNotaries).greaterThan(0).or(Bindings.size(foundNotaries).greaterThan(0)) val unsortedNodes = Collections.synchronizedList(ArrayList()).observable() val sortedNodes = SortedList(unsortedNodes, Comparator { o1, o2 -> compareValues(o1.nodeType.toString() + o1.templateId, o2.nodeType.toString() + o2.templateId) * -1 @@ -324,8 +324,10 @@ class BootstrapperView : View("Corda Network Builder") { nodeInstance.nodeInstanceName, nodeInstance.expectedFqName, nodeInstance.reachableAddress, - nodeInstance.portMapping[Constants.NODE_P2P_PORT] ?: Constants.NODE_P2P_PORT, - nodeInstance.portMapping[Constants.NODE_SSHD_PORT] ?: Constants.NODE_SSHD_PORT) + nodeInstance.portMapping[Constants.NODE_P2P_PORT] + ?: Constants.NODE_P2P_PORT, + nodeInstance.portMapping[Constants.NODE_SSHD_PORT] + ?: Constants.NODE_SSHD_PORT) ) } diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/gui/Gui.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/gui/Gui.kt similarity index 86% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/gui/Gui.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/gui/Gui.kt index a517ba7599..7de86ada90 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/gui/Gui.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/gui/Gui.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.gui +package net.corda.networkbuilder.gui import javafx.stage.Stage import tornadofx.App diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/BuiltNode.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/BuiltNode.kt similarity index 95% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/BuiltNode.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/BuiltNode.kt index 47515f9dce..48a68ca273 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/BuiltNode.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/BuiltNode.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes import net.corda.node.services.config.NodeConfiguration import java.io.File @@ -7,7 +7,6 @@ open class BuiltNode(configFile: File, baseDirectory: File, copiedNodeConfig: File, copiedNodeDir: File, val nodeConfig: NodeConfiguration, val localImageId: String) : CopiedNode(configFile, baseDirectory, copiedNodeConfig, copiedNodeDir) { - override fun toString(): String { return "BuiltNode(" + "nodeConfig=$nodeConfig," + diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/CopiedNode.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/CopiedNode.kt similarity index 92% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/CopiedNode.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/CopiedNode.kt index 6e6c04c67f..583d6e5d87 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/CopiedNode.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/CopiedNode.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes import net.corda.node.services.config.NodeConfiguration import java.io.File @@ -12,11 +12,11 @@ open class CopiedNode(configFile: File, baseDirectory: File, ) operator fun component4(): File { - return copiedNodeDir; + return copiedNodeDir } operator fun component5(): File { - return copiedNodeConfig; + return copiedNodeConfig } @@ -35,6 +35,4 @@ open class CopiedNode(configFile: File, baseDirectory: File, fun toBuiltNode(nodeConfig: NodeConfiguration, localImageId: String): BuiltNode { return BuiltNode(this.configFile, this.baseDirectory, this.copiedNodeConfig, this.copiedNodeDir, nodeConfig, localImageId) } - - } \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/FoundNode.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/FoundNode.kt similarity index 84% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/FoundNode.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/FoundNode.kt index 477cbd1b75..3fc427d653 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/FoundNode.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/FoundNode.kt @@ -1,22 +1,22 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes +import net.corda.networkbuilder.Constants import java.io.File open class FoundNode(open val configFile: File, open val baseDirectory: File = configFile.parentFile, - val name: String = configFile.parentFile.name.toLowerCase().replace(net.corda.bootstrapper.Constants.ALPHA_NUMERIC_ONLY_REGEX, "")) { - + val name: String = configFile.parentFile.name.toLowerCase().replace(Constants.ALPHA_NUMERIC_ONLY_REGEX, "")) { operator fun component1(): File { - return baseDirectory; + return baseDirectory } operator fun component2(): File { - return configFile; + return configFile } operator fun component3(): String { - return name; + return name } override fun equals(other: Any?): Boolean { @@ -46,8 +46,4 @@ open class FoundNode(open val configFile: File, fun toCopiedNode(copiedNodeConfig: File, copiedNodeDir: File): CopiedNode { return CopiedNode(this.configFile, this.baseDirectory, copiedNodeConfig, copiedNodeDir) } - - -} - - +} \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeAdder.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeAdder.kt similarity index 87% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeAdder.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeAdder.kt index c90ea6e3ac..fb840c8763 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeAdder.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeAdder.kt @@ -1,7 +1,7 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes -import net.corda.bootstrapper.containers.instance.InstanceInfo -import net.corda.bootstrapper.context.Context +import net.corda.networkbuilder.containers.instance.InstanceInfo +import net.corda.networkbuilder.context.Context import net.corda.core.identity.CordaX500Name import java.util.concurrent.CompletableFuture diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeBuilder.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeBuilder.kt similarity index 87% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeBuilder.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeBuilder.kt index 190da5da6f..ec8f624f60 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeBuilder.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeBuilder.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes import com.github.dockerjava.api.model.BuildResponseItem import com.github.dockerjava.core.async.ResultCallbackTemplate @@ -6,7 +6,7 @@ import com.github.dockerjava.core.command.BuildImageResultCallback import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigValueFactory -import net.corda.bootstrapper.docker.DockerUtils +import net.corda.networkbuilder.docker.DockerUtils import net.corda.common.configuration.parsing.internal.Configuration import net.corda.common.validation.internal.Validated import net.corda.node.services.config.NodeConfiguration @@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory import java.io.File import java.util.concurrent.CompletableFuture +/** Builds a Docker image. */ open class NodeBuilder { companion object { @@ -48,6 +49,9 @@ open class NodeBuilder { override fun onComplete() { super.onComplete() LOG.info("finished building docker image for: $nodeDir with id: ${result?.imageId}") + if (result == null || (result?.id == null && result?.errorDetail != null)) { + future.completeExceptionally(IllegalStateException("Could not build image for: $nodeDir, reason: ${result?.errorDetail}")) + } val config = nodeConfig.parseAsNodeConfigWithFallback(ConfigFactory.parseFile(copiedNode.configFile)).value() future.complete(copiedNode.builtNode(config, result?.imageId!!)) } diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeCopier.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeCopier.kt similarity index 81% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeCopier.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeCopier.kt index fa58d90b89..7c397a0f5b 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeCopier.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeCopier.kt @@ -1,29 +1,32 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigRenderOptions import com.typesafe.config.ConfigValue +import net.corda.core.internal.div import org.slf4j.LoggerFactory import java.io.File +import java.nio.file.Files +import java.nio.file.Path open class NodeCopier(private val cacheDir: File) { - fun copyNode(foundNode: FoundNode): CopiedNode { val nodeCacheDir = File(cacheDir, foundNode.baseDirectory.name) nodeCacheDir.deleteRecursively() LOG.info("copying: ${foundNode.baseDirectory} to $nodeCacheDir") foundNode.baseDirectory.copyRecursively(nodeCacheDir, overwrite = true) + //docker-java lib doesn't copy an empty folder, so if it's empty add a dummy file + ensureDirectoryIsNonEmpty(nodeCacheDir.toPath()/("cordapps")) copyBootstrapperFiles(nodeCacheDir) val configInCacheDir = File(nodeCacheDir, "node.conf") - LOG.info("Applying precanned config " + configInCacheDir) + LOG.info("Applying precanned config $configInCacheDir") val rpcSettings = getDefaultRpcSettings() - val sshSettings = getDefaultSshSettings(); + val sshSettings = getDefaultSshSettings() mergeConfigs(configInCacheDir, rpcSettings, sshSettings) return CopiedNode(foundNode, configInCacheDir, nodeCacheDir) } - fun copyBootstrapperFiles(nodeCacheDir: File) { this.javaClass.classLoader.getResourceAsStream("node-Dockerfile").use { nodeDockerFileInStream -> val nodeDockerFile = File(nodeCacheDir, "Dockerfile") @@ -88,14 +91,21 @@ open class NodeCopier(private val cacheDir: File) { } } + /** Adds a dummy file if the directory is empty. Creates a directory if it doesn't exists. */ + internal fun ensureDirectoryIsNonEmpty(path: Path, dummyFileName: String = "dummy.txt") { + if (!Files.exists(path)) { + Files.createDirectories(path) + } + if (Files.list(path).noneMatch { !Files.isDirectory(it) }) { + Files.createFile(path / dummyFileName) + } + } internal enum class Mode { NOTARY, NODE } - companion object { val LOG = LoggerFactory.getLogger(NodeCopier::class.java) } -} - +} \ No newline at end of file diff --git a/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeFinder.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeFinder.kt new file mode 100644 index 0000000000..4c7df4771a --- /dev/null +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeFinder.kt @@ -0,0 +1,38 @@ +package net.corda.networkbuilder.nodes + +import com.typesafe.config.Config +import com.typesafe.config.ConfigException +import com.typesafe.config.ConfigFactory +import net.corda.networkbuilder.Constants +import net.corda.core.utilities.contextLogger +import java.io.File + +class NodeFinder(private val dirToSearch: File) { + + fun findNodes(): List = findNodes { config -> !isNotary(config) } + + fun findNotaries(): List = findNodes { config -> isNotary(config) } + + private fun isNotary(config: Config) = config.hasPath("notary") + + private fun findNodes(filter: (Config) -> Boolean): List { + return dirToSearch.walkBottomUp().filter { + it.name == "node.conf" && !it.absolutePath.contains(Constants.BOOTSTRAPPER_DIR_NAME) + }.mapNotNull { + try { + ConfigFactory.parseFile(it) to it + } catch (e: ConfigException) { + null + } + }.filter { + filter(it.first) + }.map { (_, nodeConfigFile) -> + LOG.info("We've found a node with name: ${nodeConfigFile.parentFile.name}") + FoundNode(nodeConfigFile, nodeConfigFile.parentFile) + }.toList() + } + + companion object { + val LOG = contextLogger() + } +} \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeInstance.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeInstance.kt similarity index 94% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeInstance.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeInstance.kt index e6d6a440d3..f5622e8037 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeInstance.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeInstance.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes import net.corda.node.services.config.NodeConfiguration import java.io.File @@ -26,6 +26,4 @@ class NodeInstance(configFile: File, nodeInstanceName, actualX500, expectedFqName - ) { -} - + ) \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeInstanceRequest.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeInstanceRequest.kt similarity index 83% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeInstanceRequest.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeInstanceRequest.kt index 0ecb570352..11d5ae2abb 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeInstanceRequest.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeInstanceRequest.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes import net.corda.node.services.config.NodeConfiguration import java.io.File @@ -17,8 +17,7 @@ open class NodeInstanceRequest(configFile: File, baseDirectory: File, } fun toNodeInstance(reachableAddress: String, portMapping: Map): NodeInstance { - return NodeInstance(configFile, baseDirectory, copiedNodeConfig, copiedNodeDir, nodeConfig, localImageId, remoteImageName, nodeInstanceName, actualX500, expectedFqName, reachableAddress, portMapping) + return NodeInstance(configFile, baseDirectory, copiedNodeConfig, copiedNodeDir, nodeConfig, + localImageId, remoteImageName, nodeInstanceName, actualX500, expectedFqName, reachableAddress, portMapping) } - - } \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeInstantiator.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeInstantiator.kt similarity index 93% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeInstantiator.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeInstantiator.kt index 9de81220a9..c3e8933707 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodeInstantiator.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodeInstantiator.kt @@ -1,16 +1,15 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes -import net.corda.bootstrapper.Constants -import net.corda.bootstrapper.containers.instance.InstanceInfo -import net.corda.bootstrapper.containers.instance.Instantiator -import net.corda.bootstrapper.context.Context +import net.corda.networkbuilder.Constants +import net.corda.networkbuilder.containers.instance.InstanceInfo +import net.corda.networkbuilder.containers.instance.Instantiator +import net.corda.networkbuilder.context.Context import net.corda.core.identity.CordaX500Name import java.util.concurrent.CompletableFuture class NodeInstantiator(val instantiator: Instantiator, val context: Context) { - fun createInstanceRequests(pushedNode: PushedNode, nodeCount: Map): List { val namedMap = nodeCount.map { it.key.name.toLowerCase() to it.value }.toMap() @@ -30,7 +29,6 @@ class NodeInstantiator(val instantiator: Instantiator, return createInstanceRequest(node, 0) } - private fun buildX500(baseX500: CordaX500Name, i: Int): String { if (i == 0) { return baseX500.toString() @@ -87,6 +85,4 @@ class NodeInstantiator(val instantiator: Instantiator, fun expectedFqdn(newInstanceName: String): String { return instantiator.getExpectedFQDN(newInstanceName) } - - } \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodePusher.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodePusher.kt similarity index 79% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodePusher.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodePusher.kt index ecb12b363d..8e0b1e68aa 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/NodePusher.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/NodePusher.kt @@ -1,13 +1,12 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes -import net.corda.bootstrapper.containers.push.ContainerPusher -import net.corda.bootstrapper.context.Context +import net.corda.networkbuilder.containers.push.ContainerPusher +import net.corda.networkbuilder.context.Context import java.util.concurrent.CompletableFuture class NodePusher(private val containerPusher: ContainerPusher, private val context: Context) { - fun pushNode(builtNode: BuiltNode): CompletableFuture { val localImageId = builtNode.localImageId diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/PushedNode.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/PushedNode.kt similarity index 95% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/PushedNode.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/PushedNode.kt index d75c4390f5..a34c2a24a0 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/nodes/PushedNode.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/nodes/PushedNode.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.nodes +package net.corda.networkbuilder.nodes import net.corda.node.services.config.NodeConfiguration import java.io.File @@ -20,6 +20,4 @@ open class PushedNode(configFile: File, baseDirectory: File, override fun toString(): String { return "PushedNode(remoteImageName='$remoteImageName') ${super.toString()}" } - - } \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/CopiedNotary.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/notaries/CopiedNotary.kt similarity index 79% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/CopiedNotary.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/notaries/CopiedNotary.kt index f16ef7a70a..f934fb97e2 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/CopiedNotary.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/notaries/CopiedNotary.kt @@ -1,12 +1,11 @@ -package net.corda.bootstrapper.notaries +package net.corda.networkbuilder.notaries -import net.corda.bootstrapper.nodes.CopiedNode +import net.corda.networkbuilder.nodes.CopiedNode import java.io.File class CopiedNotary(configFile: File, baseDirectory: File, copiedNodeConfig: File, copiedNodeDir: File, val nodeInfoFile: File) : - CopiedNode(configFile, baseDirectory, copiedNodeConfig, copiedNodeDir) { -} + CopiedNode(configFile, baseDirectory, copiedNodeConfig, copiedNodeDir) fun CopiedNode.toNotary(nodeInfoFile: File): CopiedNotary { diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/NotaryCopier.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/notaries/NotaryCopier.kt similarity index 78% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/NotaryCopier.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/notaries/NotaryCopier.kt index 92af11ef76..572b47d01c 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/notaries/NotaryCopier.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/notaries/NotaryCopier.kt @@ -1,23 +1,26 @@ -package net.corda.bootstrapper.notaries +package net.corda.networkbuilder.notaries -import net.corda.bootstrapper.nodes.CopiedNode -import net.corda.bootstrapper.nodes.FoundNode -import net.corda.bootstrapper.nodes.NodeCopier +import net.corda.core.internal.div +import net.corda.networkbuilder.nodes.CopiedNode +import net.corda.networkbuilder.nodes.FoundNode +import net.corda.networkbuilder.nodes.NodeCopier import org.slf4j.LoggerFactory import java.io.File -class NotaryCopier(val cacheDir: File) : NodeCopier(cacheDir) { +class NotaryCopier(private val cacheDir: File) : NodeCopier(cacheDir) { fun copyNotary(foundNotary: FoundNode): CopiedNotary { val nodeCacheDir = File(cacheDir, foundNotary.baseDirectory.name) nodeCacheDir.deleteRecursively() LOG.info("copying: ${foundNotary.baseDirectory} to $nodeCacheDir") foundNotary.baseDirectory.copyRecursively(nodeCacheDir, overwrite = true) + //docker-java lib doesn't copy an empty folder, so if it's empty add a dummy file + ensureDirectoryIsNonEmpty(nodeCacheDir.toPath()/("cordapps")) copyNotaryBootstrapperFiles(nodeCacheDir) val configInCacheDir = File(nodeCacheDir, "node.conf") - LOG.info("Applying precanned config " + configInCacheDir) + LOG.info("Applying precanned config $configInCacheDir") val rpcSettings = getDefaultRpcSettings() - val sshSettings = getDefaultSshSettings(); + val sshSettings = getDefaultSshSettings() mergeConfigs(configInCacheDir, rpcSettings, sshSettings, Mode.NOTARY) val generatedNodeInfo = generateNodeInfo(nodeCacheDir) return CopiedNode(foundNotary, configInCacheDir, nodeCacheDir).toNotary(generatedNodeInfo) @@ -32,10 +35,9 @@ class NotaryCopier(val cacheDir: File) : NodeCopier(cacheDir) { val exitCode = nodeInfoGeneratorProcess.waitFor() if (exitCode != 0) { - throw IllegalStateException("Failed to generate nodeInfo for notary: " + dirToGenerateFrom) + throw IllegalStateException("Failed to generate nodeInfo for notary: $dirToGenerateFrom") } - val nodeInfoFile = dirToGenerateFrom.listFiles().filter { it.name.startsWith("nodeInfo") }.single() - return nodeInfoFile; + return dirToGenerateFrom.listFiles().single { it.name.startsWith("nodeInfo") } } private fun copyNotaryBootstrapperFiles(nodeCacheDir: File) { @@ -64,6 +66,4 @@ class NotaryCopier(val cacheDir: File) : NodeCopier(cacheDir) { companion object { val LOG = LoggerFactory.getLogger(NotaryCopier::class.java) } - - } \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/serialization/SerializationHelper.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/serialization/SerializationHelper.kt similarity index 97% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/serialization/SerializationHelper.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/serialization/SerializationHelper.kt index d0efa8d492..c019d666b5 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/serialization/SerializationHelper.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/serialization/SerializationHelper.kt @@ -1,4 +1,4 @@ -package net.corda.bootstrapper.serialization +package net.corda.networkbuilder.serialization import net.corda.core.serialization.internal.SerializationEnvironment import net.corda.core.serialization.internal.nodeSerializationEnv @@ -22,7 +22,6 @@ class SerializationEngine { p2pContext = AMQP_P2P_CONTEXT.withClassLoader(classloader), rpcServerContext = AMQP_P2P_CONTEXT.withClassLoader(classloader), storageContext = AMQP_STORAGE_CONTEXT.withClassLoader(classloader), - checkpointContext = KRYO_CHECKPOINT_CONTEXT.withClassLoader(classloader), checkpointSerializer = KryoCheckpointSerializer ) diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/Volume.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/volumes/Volume.kt similarity index 92% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/Volume.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/volumes/Volume.kt index c65fa79d27..125a828fe7 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/Volume.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/volumes/Volume.kt @@ -1,9 +1,9 @@ -package net.corda.bootstrapper.volumes +package net.corda.networkbuilder.volumes import com.microsoft.azure.storage.file.CloudFile import com.typesafe.config.ConfigFactory -import net.corda.bootstrapper.notaries.CopiedNotary -import net.corda.bootstrapper.serialization.SerializationEngine +import net.corda.networkbuilder.notaries.CopiedNotary +import net.corda.networkbuilder.serialization.SerializationEngine import net.corda.core.node.NetworkParameters import net.corda.core.node.NotaryInfo import net.corda.core.serialization.deserialize @@ -26,7 +26,6 @@ interface Volume { internal val networkMapCa = createDevNetworkMapCa(DEV_ROOT_CA) internal val networkMapCert: X509Certificate = networkMapCa.certificate internal val keyPair = networkMapCa.keyPair - } @@ -34,7 +33,6 @@ interface Volume { this.uploadFromByteArray(array, 0, array.size) } - fun convertNodeIntoToNetworkParams(notaryFiles: List>): NetworkParameters { val notaryInfos = notaryFiles.map { (configFile, nodeInfoFile) -> val validating = ConfigFactory.parseFile(configFile).getConfig("notary").getBooleanCaseInsensitive("validating") @@ -52,6 +50,4 @@ interface Volume { whitelistedContractImplementations = emptyMap()) } } - - } \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/azure/AzureSmbVolume.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/volumes/azure/AzureSmbVolume.kt similarity index 87% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/azure/AzureSmbVolume.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/volumes/azure/AzureSmbVolume.kt index 7c85080729..7e7e0cb61c 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/azure/AzureSmbVolume.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/volumes/azure/AzureSmbVolume.kt @@ -1,14 +1,14 @@ -package net.corda.bootstrapper.volumes.azure +package net.corda.networkbuilder.volumes.azure import com.microsoft.azure.management.Azure import com.microsoft.azure.management.resources.ResourceGroup import com.microsoft.azure.management.storage.StorageAccount import com.microsoft.azure.storage.CloudStorageAccount -import net.corda.bootstrapper.Constants.Companion.restFriendlyName -import net.corda.bootstrapper.notaries.CopiedNotary -import net.corda.bootstrapper.volumes.Volume -import net.corda.bootstrapper.volumes.Volume.Companion.keyPair -import net.corda.bootstrapper.volumes.Volume.Companion.networkMapCert +import net.corda.networkbuilder.Constants.Companion.restFriendlyName +import net.corda.networkbuilder.notaries.CopiedNotary +import net.corda.networkbuilder.volumes.Volume +import net.corda.networkbuilder.volumes.Volume.Companion.keyPair +import net.corda.networkbuilder.volumes.Volume.Companion.networkMapCert import net.corda.core.internal.signWithCert import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME @@ -38,7 +38,6 @@ class AzureSmbVolume(private val azure: Azure, private val resourceGroup: Resour val storageAccountKey: String get() = accKeys.value() - init { while (true) { try { @@ -64,18 +63,12 @@ class AzureSmbVolume(private val azure: Azure, private val resourceGroup: Resour override fun notariesForNetworkParams(notaries: List) { val networkParamsFile = networkParamsFolder.getFileReference(NETWORK_PARAMS_FILE_NAME) networkParamsFile.deleteIfExists() - LOG.info("Storing network-params in AzureFile location: " + networkParamsFile.uri) + LOG.info("Storing network-params in AzureFile location: ${networkParamsFile.uri}") val networkParameters = convertNodeIntoToNetworkParams(notaries.map { it.configFile to it.nodeInfoFile }) networkParamsFile.uploadFromByteArray(networkParameters.signWithCert(keyPair.private, networkMapCert).serialize().bytes) } - companion object { val LOG = LoggerFactory.getLogger(AzureSmbVolume::class.java) } - -} - - - - +} \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/docker/LocalVolume.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/volumes/docker/LocalVolume.kt similarity index 86% rename from tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/docker/LocalVolume.kt rename to tools/network-builder/src/main/kotlin/net/corda/networkbuilder/volumes/docker/LocalVolume.kt index e5ca96c9a0..350814346a 100644 --- a/tools/network-bootstrapper/src/main/kotlin/net/corda/bootstrapper/volumes/docker/LocalVolume.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/volumes/docker/LocalVolume.kt @@ -1,8 +1,8 @@ -package net.corda.bootstrapper.volumes.docker +package net.corda.networkbuilder.volumes.docker -import net.corda.bootstrapper.context.Context -import net.corda.bootstrapper.notaries.CopiedNotary -import net.corda.bootstrapper.volumes.Volume +import net.corda.networkbuilder.context.Context +import net.corda.networkbuilder.notaries.CopiedNotary +import net.corda.networkbuilder.volumes.Volume import net.corda.core.internal.signWithCert import net.corda.core.serialization.serialize import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME @@ -24,7 +24,6 @@ class LocalVolume(scratchDir: File, context: Context) : Volume { LOG.info("wrote network params to local file: ${networkParamsFile.absolutePath}") } - fun getPath(): String { return volumeDir.absolutePath } diff --git a/tools/network-builder/src/main/resources/node-Dockerfile b/tools/network-builder/src/main/resources/node-Dockerfile new file mode 100644 index 0000000000..4387f29054 --- /dev/null +++ b/tools/network-builder/src/main/resources/node-Dockerfile @@ -0,0 +1,21 @@ +FROM corda/corda-zulu-4.1 + +# Copy corda files +ADD --chown=corda:corda node.conf /opt/corda/node.conf +ADD --chown=corda:corda cordapps/ /opt/corda/cordapps + +# Copy node info watcher script +ADD --chown=corda:corda node_info_watcher.sh /opt/corda/ + +COPY --chown=corda:corda run-corda.sh /opt/corda/bin/run-corda + +RUN chmod +x /opt/corda/bin/run-corda && \ + chmod +x /opt/corda/node_info_watcher.sh && \ + sync + +# Working directory for Corda +WORKDIR /opt/corda +ENV HOME=/opt/corda + +# Start it +CMD ["run-corda"] \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/resources/node_info_watcher.sh b/tools/network-builder/src/main/resources/node_info_watcher.sh old mode 100755 new mode 100644 similarity index 100% rename from tools/network-bootstrapper/src/main/resources/node_info_watcher.sh rename to tools/network-builder/src/main/resources/node_info_watcher.sh diff --git a/tools/network-bootstrapper/src/main/resources/node_killer.sh b/tools/network-builder/src/main/resources/node_killer.sh old mode 100755 new mode 100644 similarity index 100% rename from tools/network-bootstrapper/src/main/resources/node_killer.sh rename to tools/network-builder/src/main/resources/node_killer.sh diff --git a/tools/network-builder/src/main/resources/notary-Dockerfile b/tools/network-builder/src/main/resources/notary-Dockerfile new file mode 100644 index 0000000000..ab290f1fe1 --- /dev/null +++ b/tools/network-builder/src/main/resources/notary-Dockerfile @@ -0,0 +1,22 @@ +FROM corda/corda-zulu-4.1 + +# Copy corda files +ADD --chown=corda:corda node.conf /opt/corda/node.conf +ADD --chown=corda:corda cordapps/ /opt/corda/cordapps +ADD --chown=corda:corda certificates/ /opt/corda/certificates + +# Copy node info watcher script +ADD --chown=corda:corda node_info_watcher.sh /opt/corda/ + +COPY --chown=corda:corda run-corda.sh /opt/corda/bin/run-corda + +RUN chmod +x /opt/corda/bin/run-corda && \ + chmod +x /opt/corda/node_info_watcher.sh && \ + sync + +# Working directory for Corda +WORKDIR /opt/corda +ENV HOME=/opt/corda + +# Start it +CMD ["run-corda"] \ No newline at end of file diff --git a/tools/network-bootstrapper/src/main/resources/rpc-settings.conf b/tools/network-builder/src/main/resources/rpc-settings.conf similarity index 100% rename from tools/network-bootstrapper/src/main/resources/rpc-settings.conf rename to tools/network-builder/src/main/resources/rpc-settings.conf diff --git a/tools/network-bootstrapper/src/main/resources/run-corda-node.sh b/tools/network-builder/src/main/resources/run-corda-node.sh old mode 100755 new mode 100644 similarity index 90% rename from tools/network-bootstrapper/src/main/resources/run-corda-node.sh rename to tools/network-builder/src/main/resources/run-corda-node.sh index c5d379d260..bc1d7dd64f --- a/tools/network-bootstrapper/src/main/resources/run-corda-node.sh +++ b/tools/network-builder/src/main/resources/run-corda-node.sh @@ -18,7 +18,7 @@ bash node_info_watcher.sh & cd ${CORDA_HOME} -if java ${JAVA_OPTIONS} -jar ${CORDA_HOME}/corda.jar 2>&1 ; then +if java ${JAVA_OPTIONS} -jar ${CORDA_HOME}/bin/corda.jar 2>&1 ; then echo "Corda exited with zero exit code" else echo "Corda exited with nonzero exit code, sleeping to allow log collection" diff --git a/tools/network-bootstrapper/src/main/resources/run-corda-notary.sh b/tools/network-builder/src/main/resources/run-corda-notary.sh old mode 100755 new mode 100644 similarity index 88% rename from tools/network-bootstrapper/src/main/resources/run-corda-notary.sh rename to tools/network-builder/src/main/resources/run-corda-notary.sh index ec8e21d4e7..7aef8a95dd --- a/tools/network-bootstrapper/src/main/resources/run-corda-notary.sh +++ b/tools/network-builder/src/main/resources/run-corda-notary.sh @@ -14,7 +14,7 @@ bash node_info_watcher.sh & cd ${CORDA_HOME} -if java ${JAVA_OPTIONS} -jar ${CORDA_HOME}/corda.jar 2>&1 ; then +if java ${JAVA_OPTIONS} -jar ${CORDA_HOME}/bin/corda.jar 2>&1 ; then echo "Corda exited with zero exit code" else echo "Corda exited with nonzero exit code, sleeping to allow log collection" diff --git a/tools/network-bootstrapper/src/main/resources/ssh.conf b/tools/network-builder/src/main/resources/ssh.conf similarity index 100% rename from tools/network-bootstrapper/src/main/resources/ssh.conf rename to tools/network-builder/src/main/resources/ssh.conf diff --git a/tools/network-bootstrapper/src/main/resources/views/bootstrapper.css b/tools/network-builder/src/main/resources/views/bootstrapper.css similarity index 100% rename from tools/network-bootstrapper/src/main/resources/views/bootstrapper.css rename to tools/network-builder/src/main/resources/views/bootstrapper.css diff --git a/tools/network-bootstrapper/src/main/resources/views/cordalogo.png b/tools/network-builder/src/main/resources/views/cordalogo.png similarity index 100% rename from tools/network-bootstrapper/src/main/resources/views/cordalogo.png rename to tools/network-builder/src/main/resources/views/cordalogo.png diff --git a/tools/network-bootstrapper/src/main/resources/views/mainPane.fxml b/tools/network-builder/src/main/resources/views/mainPane.fxml similarity index 100% rename from tools/network-bootstrapper/src/main/resources/views/mainPane.fxml rename to tools/network-builder/src/main/resources/views/mainPane.fxml From 645c445d2815555e112c4cd4acba77fd7da9f224 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Tue, 16 Jul 2019 11:44:05 +0100 Subject: [PATCH 67/75] DOCS - Fix broken url to reconnecting rpc code (#5250) (#5278) (cherry picked from commit 2bfd2c8cb5c8ff894d63f825f58e893c44d3e432) --- docs/source/clientrpc.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/clientrpc.rst b/docs/source/clientrpc.rst index 144fa54ebc..bbc62bd9a6 100644 --- a/docs/source/clientrpc.rst +++ b/docs/source/clientrpc.rst @@ -368,11 +368,11 @@ The only way to confirm is to perform a business-level query and retry according In case users require such a functionality to write a resilient RPC client we have a sample that showcases how this can be implemented and also a thorough test that demonstrates it works as expected. -The code that performs the reconnecting logic is: `ReconnectingCordaRPCOps.kt `_. +The code that performs the reconnecting logic is: `ReconnectingCordaRPCOps.kt `_. .. note:: This sample code is not exposed as an official Corda API, and must be included directly in the client codebase and adjusted. -The usage is showcased in the: `RpcReconnectTests.kt `_. +The usage is showcased in the: `RpcReconnectTests.kt `_. In case resiliency is a requirement, then it is recommended that users will write a similar test. How to initialize the `ReconnectingCordaRPCOps`: From 0192fced3e949035b0aa948c3746a1a3dc04ec62 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Tue, 16 Jul 2019 11:44:23 +0100 Subject: [PATCH 68/75] DOCS - Remove mention of hot swapping of cordapp config files (#5260) (#5266) (cherry picked from commit e84995abf981fcbf53ee0c8ddd2489084f635448) --- docs/source/cordapp-build-systems.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/cordapp-build-systems.rst b/docs/source/cordapp-build-systems.rst index 1adcbe33a1..abec6d02bd 100644 --- a/docs/source/cordapp-build-systems.rst +++ b/docs/source/cordapp-build-systems.rst @@ -367,7 +367,7 @@ CorDapp configuration files should be placed in ``/cordapps/config``. name of the JAR of the CorDapp (eg; if your CorDapp is called ``hello-0.1.jar`` the config should be ``config/hello-0.1.conf``). Config files are currently only available in the `Typesafe/Lightbend `_ config format. -These files are loaded when a CorDapp context is created and so can change during runtime. +These files are loaded during node startup. CorDapp configuration can be accessed from ``CordappContext::config`` whenever a ``CordappContext`` is available. For example: From 4e811f7ef0b7f3ec71c08ebd8e746f7ffcb5ba1e Mon Sep 17 00:00:00 2001 From: carolynequinn <44175553+carolynequinn@users.noreply.github.com> Date: Wed, 3 Apr 2019 10:17:51 +0100 Subject: [PATCH 69/75] Update UAT.md - all UAT joining steps + link to new UAT microsite where content will be updated from now on (http://uat.network.r3.com/) (cherry picked from commit bfa547c2af66c7e9b362ef3da831cc95fb72f1d6) --- docs/source/corda-network/UAT.md | 117 +------------------------------ 1 file changed, 1 insertion(+), 116 deletions(-) diff --git a/docs/source/corda-network/UAT.md b/docs/source/corda-network/UAT.md index 260b1d7168..ba01c84319 100644 --- a/docs/source/corda-network/UAT.md +++ b/docs/source/corda-network/UAT.md @@ -32,121 +32,6 @@ Joining the UAT environment Steps to join UAT environment ----------------------------- -**Step 1.** Obtain Corda software - either: -* Open Source, through [github](https://github.com/corda) under an Apache 2 license. -* Corda Enterprise, available via a Corda representative. -There is further guidance available on Corda docs for getting set up on Corda. - -**Step 2.** Request the Trust Root from R3's Identity Operator by mailing uatdoorman@r3.com which will be sent back as a truststore.jks file. In future, the Trust Root will be packaged in the software distribution. - -**Step 3.** [Deploy the node](https://docs.corda.net/deploying-a-node.html) - where applicable, with help from a Corda representative. - -**Step 4.** [Configure the node](https://docs.corda.net/corda-configuration-file.html) – a node.conf file must be included in the root directory of every Corda node. - -Configuring the node includes: - -4.1. **Choosing an email address.** The email address should belong to a suitably authorised employee of the node operator organisation. The email address is only retained by the Operator for the purposes of contact in relation to identity checks and any administrative issues. It is not included in the certificate. - -4.2. **Choosing a Distinguished Name** A DN must be unique within Corda Network. The DN is comprised of separate fields as per the table below. Only O and OU are used for the identity uniqueness check, and the other fields are considered as attributes of the identity. - -All data fields must adhere to the following constraints: -* Only uses Latin, common and inherited unicode scripts -* Upper-case first letter -* At least two letters -* No leading or trailing whitespace -* Does not include the following characters: , , = , $ , " , ' , \ -* Is in NFKC normalization form -* Does not contain the null character - -| | Mandatory | Length (chars) | Validation | Purpose | -| --- | --- | --- | --- | --- | -| **Organisation (O)** | Y | 128 | As per above, and additionally:No double-spacing. May not contain the words "node" or "server". | The O field for the legal entity defines the beneficial owner of states on the ledger. This should be set to the **legal name** of the organisation, as it appears on the official trade register within the jurisdiction in which the entity is registered. This is used to define the owning organisation of the node / certificate. | -| **Organisation Unit (OU)** | N | 64 | As per above | This field is generally used to denote sub-divisions or units of the organisation (legal entity). It may be used by node operators for internal purposes to separate nodes used for different purposes by the same legal entity. | -| **Locality (L)** | Y | 64 | As per above | The city or town in which the registered head-office of the legal entity is located. If the company operates from New York City but is registered in Wilmington, Delaware then please use Wilmington | -| **Country (C)** | Y | 2 | 2-digit ISO code | The country in which the registered head-office of the legal entity is located. | -| **State (S)** | N | 64 | As per above | If your country operates a State or Province system (e.g. USA and Canada) please add the State in which the registered head-office of the legal entity is located. Do not abbreviate. For example, "CA" is not a valid state name. "California" is correct. If the company operates from New York but is registered in Delaware, please use Delaware | -| **Common Name (CN)** | N | 64 | As per above | Available for use by the node operator for their own internal purposes. Often used for home website urls in www. | - -The above fields must be populated accurately with respect to the legal status of the entity being registered. As part of standard onboarding checks for Corda Network, the Identity Operator may verify that these details have been accurately populated and reject requests where the population of these fields does not appear to be correct. - -**4.3. Specify URLs For Initial Registration** -The settings below must be added to the node.conf at the end of the file: - -``` -networkServices { -doormanURL=“https://prod-doorman2-01.corda.network/ED5D077E-F970-428B-8091-F7FCBDA06F8C” -networkMapURL=“https://prod-netmap2-01.corda.network/ED5D077E-F970-428B-8091-F7FCBDA06F8C” -} -devMode = false - -tlsCertCrlDistPoint : “http://crl.corda.network/nodetls.crl” -tlsCertCrlIssuer : “CN=Corda TLS CRL Authority,OU=Corda Network,O=R3 HoldCo LLC,L=New York,C=US” -``` - -**Step 5.** Run the initial registration. -Once the node.conf file is configured, the following should be typed to the command line -"java -jar --initial-registration". This will send a Certificate Signing Request (with the relevant -name and email) to the Identity Operator. - -Once the node.conf file is configured, the following should be typed to the command line "java -jar --initial-registration --network-root-truststore-password trustpass". This will send a CSR (with the relevant DN and email) to the Network Manager service (Identity Operator / Network Map). - -A message similar to the below will be printed to the console: - -``` -Legal Name: O=ABC LIMITED, L=New York, C=US -Email: john.smith@abc.com - -Public Key: EC Public Key - X: d14bc17e650f2a317cbcb95e554f1e26808ca80f67ab804bbc911ec16673abbd - Y: 1978b02a8e693ecd534ceef835091c376cfc4e506decc69b91a872fc13ad1aeb - ------BEGIN CERTIFICATE REQUEST----- -MIIBLTCBywIBADBMMQswCQYDVQQGEwJVUzERMA8GA1UEBwwITmV3IFlvcmsxFjAU -BgNVBAoMDVIzIEhvbGRDbyBMTEMxEjAQBgNVBAsMCUM4MTUyOTE2NzBZMBMGByqG -SM49AgEGCCqGSM49AwEHA0IABNFLwX5lDyoxfLy5XlVPHiaAjKgPZ6uAS7yRHsFm -c6u9GXiwKo5pPs1TTO74NQkcN2z8TlBt7Mabkahy/BOtGuugHTAbBgkqhkiG9w0B -CQExDgwMYWRtaW5AcjMuY29tMBQGCCqGSM49BAMCBggqhkjOPQMBBwNHADBEAiBA -KLF4NLrleNZPKMoxBrr/80fE3kVbFnYtkB2h0JhX1gIgPcV0X0xZQug+njKCyKgf -DkNUdQJPqhkBBEpgVqyZmE8= ------END CERTIFICATE REQUEST----- -Submitting certificate signing request to Corda certificate signing server. -Successfully submitted request to Corda certificate signing server, request ID: 6CBB63558B4B2D9C94F8C14AB713432F60AF692EB30F2E12E628B089C517F3CF. -Start polling server for certificate signing approval. -``` - -Important: the Request ID given in the above should be noted and kept safe for future reference. - -**Step 6.** Sign the [UAT Terms of Use](https://fs22.formsite.com/r3cev/CordaUATAgreement2019/index.html) legal document - -*Sponsored Model* -Business Network Operators need to ensure their participants have signed the UAT Terms of Use before they can receive a participation certificate. The Terms of Use are available as a click-through agreement which will provide direct confirmation of acceptance to the Corda Network Operator. If BNOs prefer to organise acceptance themselves, then they must forward appropriate documentary evidence for each participant (either a signed hard copy with wet signature or a scan of such hard copy). You must specify the precise Distinguished Names in order to confirm that the correct entity has signed and an accurate certificate can be issued. - -*Direct Model* -Direct participants should email the Identity Operator indicating acceptance of the in-force Terms of Use (prior to availability of click-through agreements either attach the relevant document or refer to the document by date, name and version number). - -**Step 7. Identity Checks.** -The Identity Operator does verification checks – upon receipt of a CSR, a number of identity-related checks will be conducted, before issuing a certificate. - -**Identity checks do not constitute formal Know Your Customer (KYC) or Enhanced Due Diligence (EDD) checks. Node operators and their users are responsible for carrying out appropriate due diligence on any participant in relation to transactions performed via Corda Network.** - -Upon receipt of a CSR, the Identity Operator will conduct a number of identity-related checks before issuing a certificate: -1. The DN accurately reflects a real-world legal entity, as registered with an appropriate trade register -2. The node operator (participating entity) has signed the Corda Network Terms of Use -3. The contact email address provided is valid -4. The owner of the email address and an independent and suitably qualified person in the same organisation is aware of / approves the CSR - -*Email contact* -The Corda Network Operator will contact the owner of the email address provided in the CSR and it is important that the owner of this email address is aware of and prepared to respond to contact from the Corda Network Operator in relation to the CSR submission, and that they are able to do so on a timely basis. -Issuance of the certificate cannot proceed until contact has been made and so any delay will add to the elapsed time to issue the certificate and enable the node to join the network. Communications will be sent from 'Corda Network UAT Onboarding' (uatdoorman@r3.com). The email owner should ensure that this address is whitelisted by their email provider. - -**Step 8.** Once identity checks have been completed, a signed node CA certificate will be released by the Operator to the node. A node in polling mode will automatically download and install the certificate in its local trust store. It will also automatically generate additional identity and TLS certificates from the node CA certificate, which are required for subsequent operation of the node. - -At this point, the node will terminate and will need to be restarted. Type "java -jar " into the command line. Once restarted, the node will then proceed to download the network map and discover other nodes within Corda Network. By the end of this process, joiners will be a participant in Corda Network and Corda Network Foundation. - -**Confirming your implementation** - -Installation and configuration of your Corda applications must be undertaken by the node operator. Instructions to install CorDapps can be found on https://docs.corda.net. Specifics on application usage or installation should be available from your CorDapp provider. - -Business Network Operators should co-ordinate any post-install tests that may involve a small number of low value transactions on the business network to assure themselves of the correct setup of their node. Node operators should co-ordinate with their Business Network Operator in this regard. All node-initiated activity on the network from the point of connection is the responsibility of the node operator. +Steps to join are outlined on the [Corda Network UAT microsite](http://uat.network.r3.com/pages/joining/joining.html) For further questions on this process, please contact us - preferably on the mailing list: https://groups.io/g/corda-network From 75caa435bdaf274dcb6c8a79f9c023900efb185d Mon Sep 17 00:00:00 2001 From: Jonathan Locke <36930160+lockathan@users.noreply.github.com> Date: Tue, 30 Jul 2019 09:20:58 +0100 Subject: [PATCH 70/75] Revert "BACKPORT - Update UAT.md docs to remove specific information" --- docs/source/corda-network/UAT.md | 117 ++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/docs/source/corda-network/UAT.md b/docs/source/corda-network/UAT.md index ba01c84319..260b1d7168 100644 --- a/docs/source/corda-network/UAT.md +++ b/docs/source/corda-network/UAT.md @@ -32,6 +32,121 @@ Joining the UAT environment Steps to join UAT environment ----------------------------- -Steps to join are outlined on the [Corda Network UAT microsite](http://uat.network.r3.com/pages/joining/joining.html) +**Step 1.** Obtain Corda software - either: +* Open Source, through [github](https://github.com/corda) under an Apache 2 license. +* Corda Enterprise, available via a Corda representative. +There is further guidance available on Corda docs for getting set up on Corda. + +**Step 2.** Request the Trust Root from R3's Identity Operator by mailing uatdoorman@r3.com which will be sent back as a truststore.jks file. In future, the Trust Root will be packaged in the software distribution. + +**Step 3.** [Deploy the node](https://docs.corda.net/deploying-a-node.html) - where applicable, with help from a Corda representative. + +**Step 4.** [Configure the node](https://docs.corda.net/corda-configuration-file.html) – a node.conf file must be included in the root directory of every Corda node. + +Configuring the node includes: + +4.1. **Choosing an email address.** The email address should belong to a suitably authorised employee of the node operator organisation. The email address is only retained by the Operator for the purposes of contact in relation to identity checks and any administrative issues. It is not included in the certificate. + +4.2. **Choosing a Distinguished Name** A DN must be unique within Corda Network. The DN is comprised of separate fields as per the table below. Only O and OU are used for the identity uniqueness check, and the other fields are considered as attributes of the identity. + +All data fields must adhere to the following constraints: +* Only uses Latin, common and inherited unicode scripts +* Upper-case first letter +* At least two letters +* No leading or trailing whitespace +* Does not include the following characters: , , = , $ , " , ' , \ +* Is in NFKC normalization form +* Does not contain the null character + +| | Mandatory | Length (chars) | Validation | Purpose | +| --- | --- | --- | --- | --- | +| **Organisation (O)** | Y | 128 | As per above, and additionally:No double-spacing. May not contain the words "node" or "server". | The O field for the legal entity defines the beneficial owner of states on the ledger. This should be set to the **legal name** of the organisation, as it appears on the official trade register within the jurisdiction in which the entity is registered. This is used to define the owning organisation of the node / certificate. | +| **Organisation Unit (OU)** | N | 64 | As per above | This field is generally used to denote sub-divisions or units of the organisation (legal entity). It may be used by node operators for internal purposes to separate nodes used for different purposes by the same legal entity. | +| **Locality (L)** | Y | 64 | As per above | The city or town in which the registered head-office of the legal entity is located. If the company operates from New York City but is registered in Wilmington, Delaware then please use Wilmington | +| **Country (C)** | Y | 2 | 2-digit ISO code | The country in which the registered head-office of the legal entity is located. | +| **State (S)** | N | 64 | As per above | If your country operates a State or Province system (e.g. USA and Canada) please add the State in which the registered head-office of the legal entity is located. Do not abbreviate. For example, "CA" is not a valid state name. "California" is correct. If the company operates from New York but is registered in Delaware, please use Delaware | +| **Common Name (CN)** | N | 64 | As per above | Available for use by the node operator for their own internal purposes. Often used for home website urls in www. | + +The above fields must be populated accurately with respect to the legal status of the entity being registered. As part of standard onboarding checks for Corda Network, the Identity Operator may verify that these details have been accurately populated and reject requests where the population of these fields does not appear to be correct. + +**4.3. Specify URLs For Initial Registration** +The settings below must be added to the node.conf at the end of the file: + +``` +networkServices { +doormanURL=“https://prod-doorman2-01.corda.network/ED5D077E-F970-428B-8091-F7FCBDA06F8C” +networkMapURL=“https://prod-netmap2-01.corda.network/ED5D077E-F970-428B-8091-F7FCBDA06F8C” +} +devMode = false + +tlsCertCrlDistPoint : “http://crl.corda.network/nodetls.crl” +tlsCertCrlIssuer : “CN=Corda TLS CRL Authority,OU=Corda Network,O=R3 HoldCo LLC,L=New York,C=US” +``` + +**Step 5.** Run the initial registration. +Once the node.conf file is configured, the following should be typed to the command line +"java -jar --initial-registration". This will send a Certificate Signing Request (with the relevant +name and email) to the Identity Operator. + +Once the node.conf file is configured, the following should be typed to the command line "java -jar --initial-registration --network-root-truststore-password trustpass". This will send a CSR (with the relevant DN and email) to the Network Manager service (Identity Operator / Network Map). + +A message similar to the below will be printed to the console: + +``` +Legal Name: O=ABC LIMITED, L=New York, C=US +Email: john.smith@abc.com + +Public Key: EC Public Key + X: d14bc17e650f2a317cbcb95e554f1e26808ca80f67ab804bbc911ec16673abbd + Y: 1978b02a8e693ecd534ceef835091c376cfc4e506decc69b91a872fc13ad1aeb + +-----BEGIN CERTIFICATE REQUEST----- +MIIBLTCBywIBADBMMQswCQYDVQQGEwJVUzERMA8GA1UEBwwITmV3IFlvcmsxFjAU +BgNVBAoMDVIzIEhvbGRDbyBMTEMxEjAQBgNVBAsMCUM4MTUyOTE2NzBZMBMGByqG +SM49AgEGCCqGSM49AwEHA0IABNFLwX5lDyoxfLy5XlVPHiaAjKgPZ6uAS7yRHsFm +c6u9GXiwKo5pPs1TTO74NQkcN2z8TlBt7Mabkahy/BOtGuugHTAbBgkqhkiG9w0B +CQExDgwMYWRtaW5AcjMuY29tMBQGCCqGSM49BAMCBggqhkjOPQMBBwNHADBEAiBA +KLF4NLrleNZPKMoxBrr/80fE3kVbFnYtkB2h0JhX1gIgPcV0X0xZQug+njKCyKgf +DkNUdQJPqhkBBEpgVqyZmE8= +-----END CERTIFICATE REQUEST----- +Submitting certificate signing request to Corda certificate signing server. +Successfully submitted request to Corda certificate signing server, request ID: 6CBB63558B4B2D9C94F8C14AB713432F60AF692EB30F2E12E628B089C517F3CF. +Start polling server for certificate signing approval. +``` + +Important: the Request ID given in the above should be noted and kept safe for future reference. + +**Step 6.** Sign the [UAT Terms of Use](https://fs22.formsite.com/r3cev/CordaUATAgreement2019/index.html) legal document + +*Sponsored Model* +Business Network Operators need to ensure their participants have signed the UAT Terms of Use before they can receive a participation certificate. The Terms of Use are available as a click-through agreement which will provide direct confirmation of acceptance to the Corda Network Operator. If BNOs prefer to organise acceptance themselves, then they must forward appropriate documentary evidence for each participant (either a signed hard copy with wet signature or a scan of such hard copy). You must specify the precise Distinguished Names in order to confirm that the correct entity has signed and an accurate certificate can be issued. + +*Direct Model* +Direct participants should email the Identity Operator indicating acceptance of the in-force Terms of Use (prior to availability of click-through agreements either attach the relevant document or refer to the document by date, name and version number). + +**Step 7. Identity Checks.** +The Identity Operator does verification checks – upon receipt of a CSR, a number of identity-related checks will be conducted, before issuing a certificate. + +**Identity checks do not constitute formal Know Your Customer (KYC) or Enhanced Due Diligence (EDD) checks. Node operators and their users are responsible for carrying out appropriate due diligence on any participant in relation to transactions performed via Corda Network.** + +Upon receipt of a CSR, the Identity Operator will conduct a number of identity-related checks before issuing a certificate: +1. The DN accurately reflects a real-world legal entity, as registered with an appropriate trade register +2. The node operator (participating entity) has signed the Corda Network Terms of Use +3. The contact email address provided is valid +4. The owner of the email address and an independent and suitably qualified person in the same organisation is aware of / approves the CSR + +*Email contact* +The Corda Network Operator will contact the owner of the email address provided in the CSR and it is important that the owner of this email address is aware of and prepared to respond to contact from the Corda Network Operator in relation to the CSR submission, and that they are able to do so on a timely basis. +Issuance of the certificate cannot proceed until contact has been made and so any delay will add to the elapsed time to issue the certificate and enable the node to join the network. Communications will be sent from 'Corda Network UAT Onboarding' (uatdoorman@r3.com). The email owner should ensure that this address is whitelisted by their email provider. + +**Step 8.** Once identity checks have been completed, a signed node CA certificate will be released by the Operator to the node. A node in polling mode will automatically download and install the certificate in its local trust store. It will also automatically generate additional identity and TLS certificates from the node CA certificate, which are required for subsequent operation of the node. + +At this point, the node will terminate and will need to be restarted. Type "java -jar " into the command line. Once restarted, the node will then proceed to download the network map and discover other nodes within Corda Network. By the end of this process, joiners will be a participant in Corda Network and Corda Network Foundation. + +**Confirming your implementation** + +Installation and configuration of your Corda applications must be undertaken by the node operator. Instructions to install CorDapps can be found on https://docs.corda.net. Specifics on application usage or installation should be available from your CorDapp provider. + +Business Network Operators should co-ordinate any post-install tests that may involve a small number of low value transactions on the business network to assure themselves of the correct setup of their node. Node operators should co-ordinate with their Business Network Operator in this regard. All node-initiated activity on the network from the point of connection is the responsibility of the node operator. For further questions on this process, please contact us - preferably on the mailing list: https://groups.io/g/corda-network From cb0140a791eb02ba1d6b9803ff8ba3143f1dcf6a Mon Sep 17 00:00:00 2001 From: carolynequinn <44175553+carolynequinn@users.noreply.github.com> Date: Wed, 3 Apr 2019 10:17:51 +0100 Subject: [PATCH 71/75] ENT-3928: Remove network map URL exposed in docs Update UAT.md to remove network maps URL. Removed all UAT joining steps Added link to new UAT microsite where content will be updated from now on (http://uat.network.r3.com/) --- docs/source/corda-network/UAT.md | 117 +------------------------------ 1 file changed, 1 insertion(+), 116 deletions(-) diff --git a/docs/source/corda-network/UAT.md b/docs/source/corda-network/UAT.md index 260b1d7168..ba01c84319 100644 --- a/docs/source/corda-network/UAT.md +++ b/docs/source/corda-network/UAT.md @@ -32,121 +32,6 @@ Joining the UAT environment Steps to join UAT environment ----------------------------- -**Step 1.** Obtain Corda software - either: -* Open Source, through [github](https://github.com/corda) under an Apache 2 license. -* Corda Enterprise, available via a Corda representative. -There is further guidance available on Corda docs for getting set up on Corda. - -**Step 2.** Request the Trust Root from R3's Identity Operator by mailing uatdoorman@r3.com which will be sent back as a truststore.jks file. In future, the Trust Root will be packaged in the software distribution. - -**Step 3.** [Deploy the node](https://docs.corda.net/deploying-a-node.html) - where applicable, with help from a Corda representative. - -**Step 4.** [Configure the node](https://docs.corda.net/corda-configuration-file.html) – a node.conf file must be included in the root directory of every Corda node. - -Configuring the node includes: - -4.1. **Choosing an email address.** The email address should belong to a suitably authorised employee of the node operator organisation. The email address is only retained by the Operator for the purposes of contact in relation to identity checks and any administrative issues. It is not included in the certificate. - -4.2. **Choosing a Distinguished Name** A DN must be unique within Corda Network. The DN is comprised of separate fields as per the table below. Only O and OU are used for the identity uniqueness check, and the other fields are considered as attributes of the identity. - -All data fields must adhere to the following constraints: -* Only uses Latin, common and inherited unicode scripts -* Upper-case first letter -* At least two letters -* No leading or trailing whitespace -* Does not include the following characters: , , = , $ , " , ' , \ -* Is in NFKC normalization form -* Does not contain the null character - -| | Mandatory | Length (chars) | Validation | Purpose | -| --- | --- | --- | --- | --- | -| **Organisation (O)** | Y | 128 | As per above, and additionally:No double-spacing. May not contain the words "node" or "server". | The O field for the legal entity defines the beneficial owner of states on the ledger. This should be set to the **legal name** of the organisation, as it appears on the official trade register within the jurisdiction in which the entity is registered. This is used to define the owning organisation of the node / certificate. | -| **Organisation Unit (OU)** | N | 64 | As per above | This field is generally used to denote sub-divisions or units of the organisation (legal entity). It may be used by node operators for internal purposes to separate nodes used for different purposes by the same legal entity. | -| **Locality (L)** | Y | 64 | As per above | The city or town in which the registered head-office of the legal entity is located. If the company operates from New York City but is registered in Wilmington, Delaware then please use Wilmington | -| **Country (C)** | Y | 2 | 2-digit ISO code | The country in which the registered head-office of the legal entity is located. | -| **State (S)** | N | 64 | As per above | If your country operates a State or Province system (e.g. USA and Canada) please add the State in which the registered head-office of the legal entity is located. Do not abbreviate. For example, "CA" is not a valid state name. "California" is correct. If the company operates from New York but is registered in Delaware, please use Delaware | -| **Common Name (CN)** | N | 64 | As per above | Available for use by the node operator for their own internal purposes. Often used for home website urls in www. | - -The above fields must be populated accurately with respect to the legal status of the entity being registered. As part of standard onboarding checks for Corda Network, the Identity Operator may verify that these details have been accurately populated and reject requests where the population of these fields does not appear to be correct. - -**4.3. Specify URLs For Initial Registration** -The settings below must be added to the node.conf at the end of the file: - -``` -networkServices { -doormanURL=“https://prod-doorman2-01.corda.network/ED5D077E-F970-428B-8091-F7FCBDA06F8C” -networkMapURL=“https://prod-netmap2-01.corda.network/ED5D077E-F970-428B-8091-F7FCBDA06F8C” -} -devMode = false - -tlsCertCrlDistPoint : “http://crl.corda.network/nodetls.crl” -tlsCertCrlIssuer : “CN=Corda TLS CRL Authority,OU=Corda Network,O=R3 HoldCo LLC,L=New York,C=US” -``` - -**Step 5.** Run the initial registration. -Once the node.conf file is configured, the following should be typed to the command line -"java -jar --initial-registration". This will send a Certificate Signing Request (with the relevant -name and email) to the Identity Operator. - -Once the node.conf file is configured, the following should be typed to the command line "java -jar --initial-registration --network-root-truststore-password trustpass". This will send a CSR (with the relevant DN and email) to the Network Manager service (Identity Operator / Network Map). - -A message similar to the below will be printed to the console: - -``` -Legal Name: O=ABC LIMITED, L=New York, C=US -Email: john.smith@abc.com - -Public Key: EC Public Key - X: d14bc17e650f2a317cbcb95e554f1e26808ca80f67ab804bbc911ec16673abbd - Y: 1978b02a8e693ecd534ceef835091c376cfc4e506decc69b91a872fc13ad1aeb - ------BEGIN CERTIFICATE REQUEST----- -MIIBLTCBywIBADBMMQswCQYDVQQGEwJVUzERMA8GA1UEBwwITmV3IFlvcmsxFjAU -BgNVBAoMDVIzIEhvbGRDbyBMTEMxEjAQBgNVBAsMCUM4MTUyOTE2NzBZMBMGByqG -SM49AgEGCCqGSM49AwEHA0IABNFLwX5lDyoxfLy5XlVPHiaAjKgPZ6uAS7yRHsFm -c6u9GXiwKo5pPs1TTO74NQkcN2z8TlBt7Mabkahy/BOtGuugHTAbBgkqhkiG9w0B -CQExDgwMYWRtaW5AcjMuY29tMBQGCCqGSM49BAMCBggqhkjOPQMBBwNHADBEAiBA -KLF4NLrleNZPKMoxBrr/80fE3kVbFnYtkB2h0JhX1gIgPcV0X0xZQug+njKCyKgf -DkNUdQJPqhkBBEpgVqyZmE8= ------END CERTIFICATE REQUEST----- -Submitting certificate signing request to Corda certificate signing server. -Successfully submitted request to Corda certificate signing server, request ID: 6CBB63558B4B2D9C94F8C14AB713432F60AF692EB30F2E12E628B089C517F3CF. -Start polling server for certificate signing approval. -``` - -Important: the Request ID given in the above should be noted and kept safe for future reference. - -**Step 6.** Sign the [UAT Terms of Use](https://fs22.formsite.com/r3cev/CordaUATAgreement2019/index.html) legal document - -*Sponsored Model* -Business Network Operators need to ensure their participants have signed the UAT Terms of Use before they can receive a participation certificate. The Terms of Use are available as a click-through agreement which will provide direct confirmation of acceptance to the Corda Network Operator. If BNOs prefer to organise acceptance themselves, then they must forward appropriate documentary evidence for each participant (either a signed hard copy with wet signature or a scan of such hard copy). You must specify the precise Distinguished Names in order to confirm that the correct entity has signed and an accurate certificate can be issued. - -*Direct Model* -Direct participants should email the Identity Operator indicating acceptance of the in-force Terms of Use (prior to availability of click-through agreements either attach the relevant document or refer to the document by date, name and version number). - -**Step 7. Identity Checks.** -The Identity Operator does verification checks – upon receipt of a CSR, a number of identity-related checks will be conducted, before issuing a certificate. - -**Identity checks do not constitute formal Know Your Customer (KYC) or Enhanced Due Diligence (EDD) checks. Node operators and their users are responsible for carrying out appropriate due diligence on any participant in relation to transactions performed via Corda Network.** - -Upon receipt of a CSR, the Identity Operator will conduct a number of identity-related checks before issuing a certificate: -1. The DN accurately reflects a real-world legal entity, as registered with an appropriate trade register -2. The node operator (participating entity) has signed the Corda Network Terms of Use -3. The contact email address provided is valid -4. The owner of the email address and an independent and suitably qualified person in the same organisation is aware of / approves the CSR - -*Email contact* -The Corda Network Operator will contact the owner of the email address provided in the CSR and it is important that the owner of this email address is aware of and prepared to respond to contact from the Corda Network Operator in relation to the CSR submission, and that they are able to do so on a timely basis. -Issuance of the certificate cannot proceed until contact has been made and so any delay will add to the elapsed time to issue the certificate and enable the node to join the network. Communications will be sent from 'Corda Network UAT Onboarding' (uatdoorman@r3.com). The email owner should ensure that this address is whitelisted by their email provider. - -**Step 8.** Once identity checks have been completed, a signed node CA certificate will be released by the Operator to the node. A node in polling mode will automatically download and install the certificate in its local trust store. It will also automatically generate additional identity and TLS certificates from the node CA certificate, which are required for subsequent operation of the node. - -At this point, the node will terminate and will need to be restarted. Type "java -jar " into the command line. Once restarted, the node will then proceed to download the network map and discover other nodes within Corda Network. By the end of this process, joiners will be a participant in Corda Network and Corda Network Foundation. - -**Confirming your implementation** - -Installation and configuration of your Corda applications must be undertaken by the node operator. Instructions to install CorDapps can be found on https://docs.corda.net. Specifics on application usage or installation should be available from your CorDapp provider. - -Business Network Operators should co-ordinate any post-install tests that may involve a small number of low value transactions on the business network to assure themselves of the correct setup of their node. Node operators should co-ordinate with their Business Network Operator in this regard. All node-initiated activity on the network from the point of connection is the responsibility of the node operator. +Steps to join are outlined on the [Corda Network UAT microsite](http://uat.network.r3.com/pages/joining/joining.html) For further questions on this process, please contact us - preferably on the mailing list: https://groups.io/g/corda-network From 8ac472b933a2fd6faeb26116460f8aa507426ab6 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Tue, 16 Jul 2019 11:31:37 +0100 Subject: [PATCH 72/75] ENT-3504 Do not throw exception for missing fiber and log instead - OS version Change to `SingleThreadedStateMachineManager`. Instead of throwing an exception when a flow's fiber cannot be found, just log the message at info level. (cherry picked from commit 70b2a94fda4e1b4f4ee7aff2e369d87fb1c9f7f5) --- .../statemachine/SingleThreadedStateMachineManager.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt index 76994a826e..8b06ef135c 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/SingleThreadedStateMachineManager.kt @@ -452,12 +452,12 @@ class SingleThreadedStateMachineManager( } } else { // It happens when flows restart and the old sessions messages still arrive from a peer. - logger.info("Cannot find flow corresponding to session ID $recipientId.") + logger.info("Cannot find flow corresponding to session ID - $recipientId.") } } else { - val flow = mutex.locked { flows[flowId] } - ?: throw IllegalStateException("Cannot find fiber corresponding to ID $flowId") - flow.fiber.scheduleEvent(Event.DeliverSessionMessage(sessionMessage, deduplicationHandler, sender)) + mutex.locked { flows[flowId] }?.run { + fiber.scheduleEvent(Event.DeliverSessionMessage(sessionMessage, deduplicationHandler, sender)) + } ?: logger.info("Cannot find fiber corresponding to flow ID $flowId") } } catch (exception: Exception) { logger.error("Exception while routing $sessionMessage", exception) From ffe708c1ad5b23133ce876755b4b67c85e7f3471 Mon Sep 17 00:00:00 2001 From: Dan Newton Date: Wed, 24 Jul 2019 15:30:27 +0100 Subject: [PATCH 73/75] DOCS - Point network bootstrapper url to the artifactory download location (cherry picked from commit 41634d1fda740a75961f53582370e6040e90026b) --- docs/source/network-bootstrapper.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/network-bootstrapper.rst b/docs/source/network-bootstrapper.rst index cf6a3b565a..1831481576 100644 --- a/docs/source/network-bootstrapper.rst +++ b/docs/source/network-bootstrapper.rst @@ -24,7 +24,7 @@ You can find out more about network maps and network parameters from :doc:`netwo Bootstrapping a test network ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The Corda Network Bootstrapper can be downloaded from `here `__. +The Corda Network Bootstrapper can be downloaded from `here `__. Create a directory containing a node config file, ending in "_node.conf", for each node you want to create. "devMode" must be set to true. Then run the following command: From a41213bc86f2e330f821d720912297475a7cb46b Mon Sep 17 00:00:00 2001 From: LankyDan Date: Wed, 7 Aug 2019 14:23:33 +0100 Subject: [PATCH 74/75] Add constants for the open source and samples repos branch names * Add constants for the open source and samples repos branch names Open source branch name - `openSourceBranchName` Sample repos branch name - `openSourceSamplesBranchName` * Add base path constants to the open source and samples repos Fully replace base path to open source codebase - `openSourceBranch` Fully replace base path to open source samples - `openSourceSamplesBranch` These can be accessed in the docs using `os_branch` and `os_samples_branch` Correct usages in the docs to use these constants. * Correct links Cherry picked from - commit 91667559 --- constants.properties | 2 ++ docs/source/api-testing.rst | 8 ++++---- docs/source/azure-vm.rst | 2 +- docs/source/conf.py | 4 +++- docs/source/generating-a-node.rst | 2 +- docs/source/running-a-notary.rst | 4 ++-- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/constants.properties b/constants.properties index 611e68ef43..0c3a9c0d70 100644 --- a/constants.properties +++ b/constants.properties @@ -23,3 +23,5 @@ snakeYamlVersion=1.19 caffeineVersion=2.6.2 metricsVersion=3.2.5 metricsNewRelicVersion=1.1.1 +openSourceBranch=https://github.com/corda/corda/blob/release/4 +openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4 diff --git a/docs/source/api-testing.rst b/docs/source/api-testing.rst index 9d7aa36bdc..05338834db 100644 --- a/docs/source/api-testing.rst +++ b/docs/source/api-testing.rst @@ -161,8 +161,8 @@ Further examples * See the flow testing tutorial :doc:`here ` * See the oracle tutorial :doc:`here ` for information on testing ``@CordaService`` classes * Further examples are available in the Example CorDapp in - `Java `_ and - `Kotlin `_ + `Java <|os_samples_branch|/cordapp-example/workflows-java/src/test/java/com/example/test/flow/IOUFlowTests.java>`_ and + `Kotlin <|os_samples_branch|/cordapp-example/workflows-kotlin/src/test/kotlin/com/example/test/flow/IOUFlowTests.kt>`_ Contract testing ---------------- @@ -380,5 +380,5 @@ Further examples * See the flow testing tutorial :doc:`here ` * Further examples are available in the Example CorDapp in - `Java `_ and - `Kotlin `_ + `Java <|os_samples_branch|/cordapp-example/workflows-java/src/test/java/com/example/test/flow/IOUFlowTests.java>`_ and + `Kotlin <|os_samples_branch|/cordapp-example/workflows-kotlin/src/test/kotlin/com/example/test/flow/IOUFlowTests.kt>`_ diff --git a/docs/source/azure-vm.rst b/docs/source/azure-vm.rst index a5793d4872..0a00f46855 100644 --- a/docs/source/azure-vm.rst +++ b/docs/source/azure-vm.rst @@ -101,7 +101,7 @@ The nodes you will use to send and receive Yo messages require the Yo! CorDapp j Connect to one of your Corda nodes (make sure this is not the Notary node) using an SSH client of your choice (e.g. Putty) and log into the virtual machine using the public IP address and your SSH key or username / password combination you defined in Step 1 of the Azure build process. Type the following command: -Build the yo cordapp sample which you can find here: https://github.com/corda/samples/tree/release-V|platform_version|/yo-cordapp and install it in the cordapp directory. +Build the yo cordapp sample which you can find here: |os_samples_branch|/yo-cordapp and install it in the cordapp directory. Now restart Corda and the Corda webserver using the following commands or restart your Corda VM from the Azure portal: diff --git a/docs/source/conf.py b/docs/source/conf.py index fd5454059e..4408abbc28 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -23,7 +23,9 @@ corda_substitutions = { "|kotlin_version|" : constants_properties_dict["kotlinVersion"], "|gradle_plugins_version|" : constants_properties_dict["gradlePluginsVersion"], "|quasar_version|" : constants_properties_dict["quasarVersion"], - "|platform_version|" : constants_properties_dict["platformVersion"] + "|platform_version|" : constants_properties_dict["platformVersion"], + "|os_branch|" : constants_properties_dict["openSourceBranch"], + "|os_samples_branch|" : constants_properties_dict["openSourceSamplesBranch"] } def setup(app): diff --git a/docs/source/generating-a-node.rst b/docs/source/generating-a-node.rst index 53e115c7af..4e27326ea1 100644 --- a/docs/source/generating-a-node.rst +++ b/docs/source/generating-a-node.rst @@ -251,7 +251,7 @@ following line to each node's ``node.conf`` file: Where ``2222`` is the port you want to open to SSH into the shell. -Below you can find the example task from the `IRS Demo `_ included in the samples directory of main Corda GitHub repository: +Below you can find the example task from the `IRS Demo <|os_branch|/samples/irs-demo/cordapp/build.gradle#L111>`_ included in the samples directory of main Corda GitHub repository: .. sourcecode:: groovy diff --git a/docs/source/running-a-notary.rst b/docs/source/running-a-notary.rst index 28e725de2a..89ee965fe2 100644 --- a/docs/source/running-a-notary.rst +++ b/docs/source/running-a-notary.rst @@ -35,14 +35,14 @@ Crash fault-tolerant (experimental) =================================== Corda provides a prototype `Raft-based `_ highly available notary implementation. You can try it out on our -`notary demo `_ page. Note that it has known limitations +`notary demo <|os_branch|/samples/notary-demo>`_ page. Note that it has known limitations and is not recommended for production use. Byzantine fault-tolerant (experimental) ======================================= A prototype BFT notary implementation based on `BFT-Smart `_ is available. You can -try it out on our `notary demo `_ page. Note that it +try it out on our `notary demo <|os_branch|/samples/notary-demo>`_ page. Note that it is still experimental and there is active work ongoing for a production ready solution. Additionally, BFT-Smart requires Java serialization which is disabled by default in Corda due to security risks, and it will only work in dev mode where this can be customised. From 33bd3b2c1dc9a8e779076ff1f0c42a9ed277cc15 Mon Sep 17 00:00:00 2001 From: edward-prosser <50100511+edward-prosser@users.noreply.github.com> Date: Mon, 19 Aug 2019 22:41:51 +0100 Subject: [PATCH 75/75] Backporting changes to quickstart (#5306) Signed-off-by: Ed Prosser --- docs/source/conf.py | 9 +- docs/source/index.rst | 11 +- docs/source/quickstart-build.rst | 593 ++++++++++++++++++++++++++++++ docs/source/quickstart-deploy.rst | 49 +++ docs/source/quickstart-index.rst | 126 ++----- 5 files changed, 692 insertions(+), 96 deletions(-) create mode 100644 docs/source/quickstart-build.rst create mode 100644 docs/source/quickstart-deploy.rst diff --git a/docs/source/conf.py b/docs/source/conf.py index 4408abbc28..0401d641f0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -148,7 +148,13 @@ html_add_permalinks = True # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -# html_theme_options = {} + +html_theme_options = { + 'includehidden':True, + 'collapse_navigation':False, + 'sticky_navigation':True, + 'titles_only':True +} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] @@ -287,4 +293,3 @@ latex_elements = { 'extraclassoptions' : 'openany', } - diff --git a/docs/source/index.rst b/docs/source/index.rst index aa0e02dac8..260bee533b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -30,6 +30,8 @@ We look forward to seeing what you can do with Corda! .. toctree:: :maxdepth: 1 + :hidden: + :titlesonly: release-notes app-upgrade-notes @@ -40,6 +42,8 @@ We look forward to seeing what you can do with Corda! .. toctree:: :caption: Development :maxdepth: 1 + :hidden: + :titlesonly: quickstart-index.rst key-concepts.rst @@ -55,6 +59,8 @@ We look forward to seeing what you can do with Corda! .. toctree:: :caption: Operations :maxdepth: 2 + :hidden: + :titlesonly: corda-nodes-index.rst corda-networks-index.rst @@ -70,6 +76,8 @@ We look forward to seeing what you can do with Corda! :caption: Corda Network :maxdepth: 2 :if_tag: htmlmode + :hidden: + :titlesonly: corda-network/index.md corda-network/UAT.md @@ -87,9 +95,10 @@ We look forward to seeing what you can do with Corda! :caption: Participate :maxdepth: 2 :if_tag: htmlmode + :hidden: + :titlesonly: contributing-index.rst deterministic-modules.rst design/design-docs-index.rst changelog - diff --git a/docs/source/quickstart-build.rst b/docs/source/quickstart-build.rst new file mode 100644 index 0000000000..c4b406c353 --- /dev/null +++ b/docs/source/quickstart-build.rst @@ -0,0 +1,593 @@ +.. highlight:: kotlin +.. raw:: html + + + + +Building your own CorDapp +========================= + +After examining a functioning CorDapp, the next challenge is to create one of your own. We're going to build a simple supply chain CorDapp representing a network between a car dealership, a car manufacturer, and a bank. + +To model this network, you need to create one state (representing cars), one contract (to control the rules governing cars), and one flow (to create cars). This CorDapp will be very basic, but entirely functional and deployable. + +Step One: Download a template CorDapp +------------------------------------- + +The first thing you need to do is clone a CorDapp template to modify. + +1. Open a terminal and navigate to a directory to store the new project. + +2. Run the following command to clone the template CorDapp: ``git clone https://github.com/corda/cordapp-template-kotlin.git`` + +3. Open IntelliJ and open the CorDapp template project. + +4. Click **File** > **Project Structure**. To set the project SDK click **New...** > **JDK**, and navigating to the installation directory of your JDK. Click **Apply**. + +5. Select **Modules** > **+** > **Import Module**. Select the ``cordapp-template-kotlin`` folder and click **Open**. Select **Import module from external model** > **Gradle** > **Next** > tick the **Use auto-import** checkbox > **Finish** > **Ok**. Gradle will now download all the project dependencies and perform some indexing. + +Step Two: Creating states +------------------------- + +Since the CorDapp models a car dealership network, a state must be created to represent cars. States are immutable objects representing on-ledger facts. A state might represent a physical asset like a car, or an intangible asset or agreement like an IOU. For more information on states, see the `state documentation <./key-concepts-states.html>`_. + +1. From IntelliJ expand the source files and navigate to the following state template file: ``contracts > src > main > kotlin > com.template > states > TemplateState.kt``. + +2. Right-click on **TemplateState.kt** in the project navigation on the left. Select **Refactor** > **Copy**. + +3. Rename the file to ``CarState`` and click **OK**. + +4. Double-click the new state file to open it. Add the following imports to the top of the state file: + + .. container:: codeset + + .. sourcecode:: kotlin + + package com.template.states + + import com.template.contracts.CarContract + import com.template.contracts.TemplateContract + import net.corda.core.contracts.BelongsToContract + import net.corda.core.contracts.ContractState + import net.corda.core.contracts.UniqueIdentifier + import net.corda.core.identity.AbstractParty + import net.corda.core.identity.Party + + It's important to specify what classes are required in each state, contract, and flow. This process must be repeated with each file as it is created. + +5. Update ``@BelongsToContract(TemplateContract:class)`` to specify ``CarContract::class``. + +6. Add the following fields to the state: + * ``owningBank`` of type ``Party`` + * ``holdingDealer`` of type ``Party`` + * ``manufacturer`` of type ``Party`` + * ``vin`` of type ``String`` + * ``licensePlateNumber`` of type ``String`` + * ``make`` of type ``String`` + * ``model`` of type ``String`` + * ``dealershipLocation`` of type ``String`` + * ``linearId`` of type ``UniqueIdentifier`` + + Don't worry if you're not sure exactly how these should appear, you can check your code shortly. + +7. Remove the ``data`` and ``participants`` parameters. + +8. Add a body to the ``CarState`` class that overrides participants to contain a list of ``owningBank``, ``holdingDealer``, and ``manufacturer``. + +9. The ``CarState`` file should now appear as follows: + + .. container:: codeset + + .. sourcecode:: kotlin + + package com.template.states + + import com.template.contracts.CarContract + import com.template.contracts.TemplateContract + import net.corda.core.contracts.BelongsToContract + import net.corda.core.contracts.ContractState + import net.corda.core.contracts.UniqueIdentifier + import net.corda.core.identity.AbstractParty + import net.corda.core.identity.Party + + // ********* + // * State * + // ********* + + @BelongsToContract(CarContract::class) + data class CarState( + val owningBank: Party, + val holdingDealer: Party, + val manufacturer: Party, + val vin: String, + val licensePlateNumber: String, + val make: String, + val model: String, + val dealershipLocation: String, + val linearId: UniqueIdentifier + ) : ContractState { + override val participants: List = listOf(owningBank, holdingDealer, manufacturer) + } + +10. Save the ``CarState.kt`` file. + +The ``CarState`` definition has now been created. It lists the properties and associated types required of all instances of this state. + + +Step Three: Creating contracts +------------------------------ + +After creating a state, you must create a contract. Contracts define the rules that govern how states can be created and evolved. For example, a contract for a Cash state should check that any transaction that changes the ownership of the cash is signed by the current owner and does not create cash from thin air. To learn more about contracts, see the `contracts documentation <./key-concepts-contracts.html>`_. + +1. From IntelliJ, expand the project source and navigate to: ``contracts > src > main > kotlin > com > template > contracts > TemplateContract.kt`` + +2. Right-click on **TemplateContract.kt** in the project navigation on the left. Select **Refactor > Copy**. + +3. Rename the file to ``CarContract`` and click **OK**. + +4. Double-click the new contract file to open it. + +5. Add the following imports to the top of the file: + + .. container:: codeset + + .. sourcecode:: kotlin + + package com.template.contracts + + import com.template.states.CarState + import net.corda.core.contracts.CommandData + import net.corda.core.contracts.Contract + import net.corda.core.contracts.requireSingleCommand + import net.corda.core.contracts.requireThat + import net.corda.core.transactions.LedgerTransaction + +6. Update the class name to: ``CarContract`` + +7. Replace ``const val ID = "com.template.contracts.TemplateContract"`` with ``val ID = CarContract::class.qualifiedName!!``. This ID field is used to identify contracts when building a transaction. This ID declaration ensures that the contract name is created dynamically and can simplify code refactoring. + +8. Update the ``Action`` command to an ``Issue`` command. This represents an issuance of an instance of the ``CarState`` state. + + Commands are the operations that can be performed on a state. A contract will often define command logic for several operations that can be performed on the state in question, for example, issuing a state, changing ownership, and marking the state retired. + +9. Add ``val command = tx.commands.requireSingleCommand().value`` at the beginning of the ``verify()`` method. The ``verify()`` method defines the verification rules that commands must satisfy to be valid. + +10. The final function of the contract is to prevent unwanted behaviour during the flow. After the ``val command = tx.commands...`` line, add the following requirement code: + + .. container:: codeset + + .. sourcecode:: kotlin + + when(command) { + is Commands.Issue -> requireThat { + "There should be no input state" using (tx.inputs.isEmpty()) + } + } + +11. Inside the ``requireThat`` block add additional lines defining the following requirements: + + * There should be one output state. + * The output state must be of the type ``CarState``. + * The ``licensePlateNumber`` must be seven characters long. + +12. The ``CarContract.kt`` file should look as follows: + + .. container:: codeset + + .. sourcecode:: kotlin + + package com.template.contracts + + import com.template.states.CarState + import net.corda.core.contracts.CommandData + import net.corda.core.contracts.Contract + import net.corda.core.contracts.requireSingleCommand + import net.corda.core.contracts.requireThat + import net.corda.core.transactions.LedgerTransaction + + class CarContract : Contract { + companion object { + const val ID = "com.template.contracts.CarContract" + } + + override fun verify(tx: LedgerTransaction) { + + val command = tx.commands.requireSingleCommand().value + + when(command) { + is Commands.Issue -> requireThat { + "There should be no input state" using (tx.inputs.isEmpty()) + "There should be one input state" using (tx.outputs.size == 1) + "The output state must be of type CarState" using (tx.outputs.get(0).data is CarState) + val outputState = tx.outputs.get(0).data as CarState + "The licensePlateNumber must be seven characters long" using (outputState.licensePlateNumber.length == 7) + } + } + } + + interface Commands : CommandData { + class Issue : Commands + } + } + +13. Save the ``CarContract.kt`` file. The contract file now defines rules that all transactions creating car states must follow. + +Step Four: Creating a flow +-------------------------- + +1. From IntelliJ, expand the project source and navigate to: ``workflows > src > main > kotlin > com.template.flows > Flows.kt`` + +2. Right-click on **Flows.kt** in the project navigation on the left. Select **Refactor > Copy**. + +3. Rename the file to ``CarFlow`` and click **OK**. + +4. Add the following imports to the top of the file: + + .. container:: codeset + + .. sourcecode:: kotlin + + package com.template.flows + + import co.paralleluniverse.fibers.Suspendable + import com.template.contracts.CarContract + import com.template.states.CarState + import net.corda.core.contracts.Command + import net.corda.core.contracts.UniqueIdentifier + import net.corda.core.contracts.requireThat + import net.corda.core.flows.* + import net.corda.core.identity.Party + import net.corda.core.node.ServiceHub + import net.corda.core.transactions.SignedTransaction + import net.corda.core.transactions.TransactionBuilder + +5. Double-click the new contract file to open it. + +6. Update the name of the ``Initiator`` class to ``CarIssueInitiator``. + +7. Update the name of the ``Responder`` class to ``CarIssueResponder``. + +8. Update the ``@InitiatedBy`` property of ``CarIssueResponder`` to ``CarIssueInitiator::class``. + +9. Now that the flow structure is in place, we can begin writing the code to create a transaction to issue a car state. Add parameters to the ``CarIssueInitiator`` class for all the fields of the ``CarState`` definition, except for ``linearId``. + +10. Inside the ``call()`` function of the initiator, create a variable for the notary node: ``val notary = serviceHub.networkMapCache.notaryIdentities.first()`` + + .. note:: + + The **networkMapCache** contains information about the nodes and notaries inside the network. + +11. Create a variable for an ``Issue`` command. + + The first parameter of the command must be the command type, in this case ``Issue``. As discussed above, the command tells other nodes what the purpose of the transaction is. + + The second parameter of the command must be a list of keys from the relevant parties, in this case ``owningBank``, ``holdingDealer``, and ``manufacturer``. As well as informing parties what the purpose of the transaction is, the command also specifies which signatures must be present on the associated transaction in order for it to be valid. + +12. Create a ``CarState`` object using the parameters of ``CarIssueInitiator``. + + The last parameter for ``CarState`` must be a new ``UniqueIdentifier()`` object. + +13. The ``CarFlow.kt`` file should look like this: + + .. container:: codeset + + .. sourcecode:: kotlin + + @InitiatingFlow + @StartableByRPC + class CarIssueInitiator( + val owningBank: Party, + val holdingDealer: Party, + val manufacturer: Party, + val vin: String, + val licensePlateNumber: String, + val make: String, + val model: String, + val dealershipLocation: String + ) : FlowLogic() { + + @Suspendable + override fun call() { + val notary = serviceHub.networkMapCache.notaryIdentities.first() + val command = Command(CarContract.Commands.Issue(), listOf(owningBank, holdingDealer, manufacturer).map { it.owningKey }) + val carState = CarState(owningBank, holdingDealer, manufacturer, vin, licensePlateNumber, make, model, dealershipLocation, UniqueIdentifier()) + } + } + + @InitiatedBy(CarIssueInitiator::class) + class CarIssueResponder(val counterpartySession: FlowSession) : FlowLogic() { + @Suspendable + override fun call(){ + + } + } + } + +14. Update the ``FlowLogic`` to ``FlowLogic`` in both the initiator and responder class. This indicates that the ``SignedTransaction`` produced by this flow is returned from ``call`` and sent to the caller of the flow. If left unchanged, ``FlowLogic`` will return nothing. + +15. Update the return type of both ``call()`` transactions to be of type ``SignedTransaction``. + +16. In the ``call()`` function, create a ``TransactionBuilder`` object similarly. The ``TransactionBuilder`` class should take in the notary node. The output state and command must be added to the ``TransactionBuilder``. + +17. Verify the transaction by calling ``verify(serviceHub)`` on the ``TransactionBuilder``. + +18. Sign the transaction and store the result in a variable, using the following `serviceHub <./api-service-hub.html>`_ method: + + .. container:: codeset + + .. sourcecode:: kotlin + + val notary = serviceHub.networkMapCache.notaryIdentities.first() + +19. Delete the ``progressTracker`` as it won't be used in this tutorial. + +20. The ``CarFlow.kt`` file should now look like this: + + .. container:: codeset + + .. sourcecode:: kotlin + + @InitiatingFlow + @StartableByRPC + class CarIssueInitiator( + val owningBank: Party, + val holdingDealer: Party, + val manufacturer: Party, + val vin: String, + val licensePlateNumber: String, + val make: String, + val model: String, + val dealershipLocation: String + ) : FlowLogic() { + + @Suspendable + override fun call(): SignedTransaction { + + val notary = serviceHub.networkMapCache.notaryIdentities.first() + val command = Command(CarContract.Commands.Issue(), listOf(owningBank, holdingDealer, manufacturer).map { it.owningKey }) + val carState = CarState( + owningBank, + holdingDealer, + manufacturer, + vin, + licensePlateNumber, + make, + model, + dealershipLocation, + UniqueIdentifier() + ) + + val txBuilder = TransactionBuilder(notary) + .addOutputState(carState, CarContract.ID) + .addCommand(command) + + txBuilder.verify(serviceHub) + val tx = serviceHub.signInitialTransaction(txBuilder) + } + } + + @InitiatedBy(CarIssueInitiator::class) + class CarIssueResponder(val counterpartySession: FlowSession) : FlowLogic() { + @Suspendable + override fun call(): SignedTransaction { + + } + } + } + +21. To finish the initiator's ``call()`` function, other parties must sign the transaction. Add the following code to send the transaction to the other relevant parties: + + .. container:: codeset + + .. sourcecode:: kotlin + + val sessions = (carState.participants - ourIdentity).map { initiateFlow(it as Party) } + val stx = subFlow(CollectSignaturesFlow(tx, sessions)) + return subFlow(FinalityFlow(stx, sessions)) + + The first line creates a ``List`` object by calling ``initiateFlow()`` for each party other than the initiating party. The second line collects signatures from the relevant parties and returns a signed transaction. The third line calls ``FinalityFlow()``, finalizes the transaction using the notary or notary pool. + + .. note:: + + Sessions are used for sending and receiving objects between nodes. ``ourIdentity`` is removed from the list of participants to open sessions to because a session does not need to be opened to the initiating party. + +22. Lastly, the body of the responder flow must be completed. The following code checks the transaction contents, signs it, and sends it back to the initiator: + + .. container:: codeset + + .. sourcecode:: kotlin + + @Suspendable + override fun call(): SignedTransaction { + val signedTransactionFlow = object : SignTransactionFlow(counterpartySession) { + override fun checkTransaction(stx: SignedTransaction) = requireThat { + val output = stx.tx.outputs.single().data + "The output must be a CarState" using (output is CarState) + } + } + val txWeJustSignedId = subFlow(signedTransactionFlow) + return subFlow(ReceiveFinalityFlow(counterpartySession, txWeJustSignedId.id)) + } + + .. note:: + + The ``checkTransaction`` function should be used *only* to model business logic. A contract's ``verify`` function should be used to define what is and is not possible within a transaction. + +23. The completed ``CarFlow.kt`` should look like this: + + .. container:: codeset + + .. sourcecode:: kotlin + + package com.template.flows + + import co.paralleluniverse.fibers.Suspendable + import com.template.contracts.CarContract + import com.template.states.CarState + import net.corda.core.contracts.Command + import net.corda.core.contracts.UniqueIdentifier + import net.corda.core.contracts.requireThat + import net.corda.core.flows.* + import net.corda.core.identity.Party + import net.corda.core.node.ServiceHub + import net.corda.core.transactions.SignedTransaction + import net.corda.core.transactions.TransactionBuilder + + @InitiatingFlow + @StartableByRPC + class CarIssueInitiator( + val owningBank: Party, + val holdingDealer: Party, + val manufacturer: Party, + val vin: String, + val licensePlateNumber: String, + val make: String, + val model: String, + val dealershipLocation: String + ) : FlowLogic() { + @Suspendable + override fun call(): SignedTransaction { + + val notary = serviceHub.networkMapCache.notaryIdentities.first() + val command = Command(CarContract.Commands.Issue(), listOf(owningBank, holdingDealer, manufacturer).map { it.owningKey }) + val carState = CarState( + owningBank, + holdingDealer, + manufacturer, + vin, + licensePlateNumber, + make, + model, + dealershipLocation, + UniqueIdentifier() + ) + + val txBuilder = TransactionBuilder(notary) + .addOutputState(carState, CarContract.ID) + .addCommand(command) + + txBuilder.verify(serviceHub) + val tx = serviceHub.signInitialTransaction(txBuilder) + + val sessions = (carState.participants - ourIdentity).map { initiateFlow(it as Party) } + val stx = subFlow(CollectSignaturesFlow(tx, sessions)) + return subFlow(FinalityFlow(stx, sessions)) + } + } + + @InitiatedBy(CarIssueInitiator::class) + class CarIssueResponder(val counterpartySession: FlowSession) : FlowLogic() { + + @Suspendable + override fun call(): SignedTransaction { + val signedTransactionFlow = object : SignTransactionFlow(counterpartySession) { + override fun checkTransaction(stx: SignedTransaction) = requireThat { + val output = stx.tx.outputs.single().data + "The output must be a CarState" using (output is CarState) + } + } + val txWeJustSignedId = subFlow(signedTransactionFlow) + return subFlow(ReceiveFinalityFlow(counterpartySession, txWeJustSignedId.id)) + } + } + +Step Five: Update the Gradle build +---------------------------------- + +The Gradle build files must be updated to change the node configuration. + +1. Navigate to the ``build.gradle`` file in the root ``cordapp-template-kotlin`` directory. + +2. In the ``deployNodes`` task, update the nodes to read as follows: + + .. container:: codeset + + .. sourcecode:: kotlin + + node { + name "O=Notary,L=London,C=GB" + notary = [validating : false] + p2pPort 10002 + rpcSettings { + address("localhost:10003") + adminAddress("localhost:10043") + } + } + node { + name "O=Dealership,L=London,C=GB" + p2pPort 10005 + rpcSettings { + address("localhost:10006") + adminAddress("localhost:10046") + } + rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]] + } + node { + name "O=Manufacturer,L=New York,C=US" + p2pPort 10008 + rpcSettings { + address("localhost:10009") + adminAddress("localhost:10049") + } + rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]] + } + node { + name "O=BankofAmerica,L=New York,C=US" + p2pPort 10010 + rpcSettings { + address("localhost:10007") + adminAddress("localhost:10047") + } + rpcUsers = [[ user: "user1", "password": "test", "permissions": ["ALL"]]] + } + + The ``nodeDefaults`` defines what CorDapps are installed on the nodes by default. To install additional CorDapps on the nodes, update the ``nodeDefaults`` definition, or add the CorDapps to each node definition individually. + +3. Save the updated ``build.gradle`` file. + +Step Six: Deploying your CorDapp locally +---------------------------------------- + +Now that the CorDapp code has been completed and the build file updated, the CorDapp can be deployed. + +1. Open a terminal and navigate to the root directory of the project. + +2. To deploy the nodes on Windows run the following command: ``gradlew clean deployNodes`` + + To deploy the nodes on Mac or Linux run the following command: ``./gradlew clean deployNodes`` + +3. To start the nodes on Windows run the following command: ``build\nodes\runnodes`` + + To start the nodes on Mac/Linux run the following command: ``build/nodes/runnodes`` + + .. note:: + + Maintain window focus on the node windows, if the nodes fail to load, close them using ``ctrl + d``. The ``runnodes`` script opens each node directory and runs ``java -jar corda.jar``. + +4. To run flows in your CorDapp, enter the following flow command from any non-notary terminal window: + + .. container:: codeset + + .. sourcecode:: kotlin + + ``flow start CarIssueInitiator owningBank: BankofAmerica, holdingDealer: Dealership, manufacturer: Manufacturer, vin: "abc", licensePlateNumber: "abc1234", make: "Honda", model: "Civic", dealershipLocation: "NYC"`` + +5. To check that the state was correctly issued, query the node using the following command: + + ``run vaultQuery contractStateType: com.template.states.CarState`` + + The vault is the node's repository of all information from the ledger that involves that node, stored in a relational model. After running the query, the terminal should display the state created by the flow command. This command can be run from the terminal window of any non-notary node, as all parties are participants in this transaction. + +Next steps +---------- + +The getting started experience is designed to be lightweight and get to code as quickly as possible, for more detail, see the following documentation: + +* `CorDapp design best practice <./writing-a-cordapp.html>`_ +* `Testing CorDapp contracts <./tutorial-test-dsl.html>`_ + +For operational users, see the following documentation: + +* `Node structure and configuration `_ +* `Deploying a node to a server `_ +* `Notary documentation `_ diff --git a/docs/source/quickstart-deploy.rst b/docs/source/quickstart-deploy.rst new file mode 100644 index 0000000000..51c9efd0df --- /dev/null +++ b/docs/source/quickstart-deploy.rst @@ -0,0 +1,49 @@ +Running the example CorDapp +=========================== + +At this point we've set up the development environment, and have an example CorDapp in an IntelliJ project. In this section, the CorDapp will be deployed to locally running Corda nodes. + +The local Corda network includes one notary, and three nodes, each representing parties in the network. A Corda node is an individual instance of Corda representing one party in a network. For more information on nodes, see the `node documentation <./key-concepts-node.html>`_. + +Before continuing, ensure that you've `set up your development environment <./quickstart-index.html>`_. + +Step One: Deploy the CorDapp locally +------------------------------------ + +The first step is to deploy the CorDapp to nodes running locally. + +1. Navigate to the root directory of the example CorDapp. + +2. To deploy the nodes on Windows run the following command: ``gradlew clean deployNodes`` + + To deploy the nodes on Mac or Linux run the following command: ``./gradlew clean deployNodes`` + +3. To best understand the deployment process, there are several perspectives it is helpful to see. On Windows run the following command: ``workflows-kotlin\build\nodes\runnodes`` + + On Mac/Linux run the following command: ``workflows-kotlin/build/nodes/runnodes`` + + This command opens four terminal windows: the notary, and a node each for PartyA, PartyB, and PartyC. A notary is a validation service that prevents double-spending, enforces timestamping, and may also validate transactions. For more information on notaries, see the `notary documentation <./key-concepts-notaries.html>`_. + + .. note:: + + Maintain window focus on the node windows, if the nodes fail to load, close them using ``ctrl + d``. The ``runnodes`` script opens each node directory and runs ``java -jar corda.jar``. + +4. Go through the tabs to see the perspectives of other network members. + +Step Two: Run a CorDapp transaction +----------------------------------- + +1. Open the terminal window for PartyA. From this window, any flows executed will be from the perspective of PartyA. + +2. To execute the ``ExampleFlow.kt`` flow, run the following command: ``flow start ExampleFlow iouValue: 1, otherParty: PartyB`` + + A flow is the mechanism by which a transaction takes place using Corda. This flow creates an instance of the IOU state, which requires an ``iouValue`` property. Flows are contained in CorDapps, and define the mechanisms by which parties transact. For more information on flows, see the `flow documentation `_. + +3. To check whether PartyB has received the transaction, open the terminal window showing PartyB's perspective, and run the following command: ``run vaultQuery contractStateType: com.example.state.IOUState`` + + This command displays all of the IOU states in the node's vault. States are immutable objects that represent shared facts between the parties. States serve as the inputs and outputs of transactions. + +Next steps +---------- + +After deploying the example CorDapp, the next step is to start `writing a CorDapp <./quickstart-build.html>`_ containing your own contract, states, and flows. diff --git a/docs/source/quickstart-index.rst b/docs/source/quickstart-index.rst index 3b3abb4382..b654cd7ea4 100644 --- a/docs/source/quickstart-index.rst +++ b/docs/source/quickstart-index.rst @@ -1,111 +1,51 @@ -Quickstart -========== +Getting started developing CorDapps +=================================== -Welcome to the Corda Quickstart Guide. Follow the links below to help get going quickly with Corda. +.. toctree:: + :hidden: + :titlesonly: + :maxdepth: 0 -I want to: + quickstart-deploy + quickstart-build -* :ref:`Learn ` about Corda for the first time -* :ref:`Develop ` a CorDapp -* :ref:`Run ` and test a CorDapp on a local Corda network -* :ref:`Add ` a node to an existing test Corda network -* :ref:`Add ` a node to an existing production network +Getting started with Corda will walk you through the process of setting up a development environment, deploying an example CorDapp, and building your own CorDapp based on the example. -.. _quickstart-learn: +1. `Setting up a development environment`_ +2. `Deploying an example CorDapp <./quickstart-deploy.html>`_ +3. `Building your own CorDapp <./quickstart-build.html>`_ -Learn about Corda for the first time +Setting up a development environment ------------------------------------ -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+============================================================================================+ -| :doc:`key-concepts` | The key concepts and features of the Corda Platform | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`getting-set-up` | Set up your machine for running and developing CorDapps | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`tutorial-cordapp` | A guide to running a simple CorDapp | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ +Prerequisites +~~~~~~~~~~~~~ -.. _quickstart-develop: +* **Java 8 JVK** - We require at least version |java_version|, but do not currently support Java 9 or higher. +* **IntelliJ IDEA** - IntelliJ is an IDE that offers strong support for Kotlin and Java development. We support versions **2017.x**, **2018.x** and **2019.x** (with Kotlin plugin version |kotlin_version|). +* **Git** - We use Git to host our sample CorDapp and provide version control. -Develop a CorDapp ------------------ +Step One: Downloading a sample project +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+============================================================================================+ -| :doc:`hello-world-introduction` | A coding walk-through of a basic CorDapp | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`cordapp-overview` | An introduction to CordApps | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`writing-a-cordapp` | How to structure a CorDapp project | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`cordapp-build-systems` | How to build a CorDapp | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`corda-api` | A guide to the CorDapp API | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ +1. Open a command prompt or terminal. +2. Clone the CorDapp example repo by running: ``git clone https://github.com/corda/samples`` +3. Move into the ``cordapp-example`` folder by running: ``cd samples/cordapp-example`` +4. Checkout the corresponding branch by running: ``git checkout release-V4`` in the current directory. -.. _quickstart-run: -Run and test a CorDapp on local Corda network ---------------------------------------------- +Step Two: Creating an IntelliJ project +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+------------------------------------------------+----------------------------------------------------------------------------------------+ -| Useful links | Description | -+================================================+========================================================================================+ -| :doc:`generating-a-node` | Guidance on creating Corda nodes for development and testing locally and on Docker | -+------------------------------------------------+----------------------------------------------------------------------------------------+ -| :doc:`node-structure` | The Corda node folder structure and how to name your node | -+------------------------------------------------+----------------------------------------------------------------------------------------+ -| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | -+------------------------------------------------+----------------------------------------------------------------------------------------+ -| :doc:`running-a-node` | Guidance on running Corda nodes locally and on Docker | -+------------------------------------------------+----------------------------------------------------------------------------------------+ -| :doc:`setting-up-a-dynamic-compatibility-zone` | Considerations for setting up a Corda network | -+------------------------------------------------+----------------------------------------------------------------------------------------+ -| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | -+------------------------------------------------+----------------------------------------------------------------------------------------+ -| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | -+------------------------------------------------+----------------------------------------------------------------------------------------+ -| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | -+------------------------------------------------+----------------------------------------------------------------------------------------+ +1. Open IntelliJ. From the splash screen, click **Open**, navigate to and select the ``cordapp-example`` folder, and click **Ok**. This creates an IntelliJ project to work from. -.. _quickstart-add: +2. Click **File** > **Project Structure**. To set the project SDK click **New...** > **JDK**, and navigating to the installation directory of your JDK. Click **Apply**. -Add a node to an existing test Corda network --------------------------------------------- +3. Select **Modules** > **+** > **Import Module**. Select the ``cordapp-example`` folder and click **Open**. Select **Import module from external model** > **Gradle** > **Next** > tick the **Use auto-import** checkbox > **Finish** > **Ok**. Gradle will now download all the project dependencies and perform some indexing. -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| Useful links | Description | -+============================================+============================================================================================+ -| :doc:`node-structure` | The Corda node folder structure and how to name your node | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`corda-configuration-file` | A detailed description of the Corda node configuration file with examples | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`deploying-a-node` | A step-by-step guide on deploying a Corda node to your own server | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`azure-vm` | A step-by-step guide on creating a Corda Network on Azure | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`aws-vm` | A step-by-step guide on creating a Corda Network on AWS | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`shell` | Guidance on using an embedded command line to control and monitor a node | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`node-administration` | How to monitor a Corda node using an RPC interface | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`node-explorer` | A GUI-based tool to view transactional data and transactional history for a node | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ -| :doc:`blob-inspector` | A troubleshooting tool allowing you to read the contents of a binary blob file | -+--------------------------------------------+--------------------------------------------------------------------------------------------+ +Your CorDapp development environment is now complete. -.. _quickstart-production: +Next steps +---------- -Add a node to an existing production network --------------------------------------------- - -+---------------------------------------------------------------------------------------------------------+ -| Corda Network is a global production network of Corda nodes, operated by the independent | -| Corda Network Foundation. You can learn more here: https://corda.network/participation/index.html | -+---------------------------------------------------------------------------------------------------------+ -| Corda Testnet is a test network, operated for the community by R3. You can learn | -| more here: https://testnet.corda.network | -+---------------------------------------------------------------------------------------------------------+ +Now that you've successfully set up your CorDapp development environment, we'll cover deploying an example CorDapp locally, before writing a CorDapp from scratch.