From 2851534ae7b17e3bfac9b242ae964b7511bc1991 Mon Sep 17 00:00:00 2001 From: Ramzi El-Yafi Date: Wed, 11 Dec 2019 08:29:37 +0000 Subject: [PATCH 01/83] Prevent on-demand tests re-triggering from branch indexing --- .ci/dev/smoke/Jenkinsfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.ci/dev/smoke/Jenkinsfile b/.ci/dev/smoke/Jenkinsfile index d5332b2508..16ae19b53d 100644 --- a/.ci/dev/smoke/Jenkinsfile +++ b/.ci/dev/smoke/Jenkinsfile @@ -5,7 +5,8 @@ killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) pipeline { agent { label 'k8s' } - options { timestamps() } + options { timestamps() + overrideIndexTriggers(false) } triggers { issueCommentTrigger('.*smoke tests.*') @@ -94,4 +95,4 @@ def currentBuildTriggeredByComment() { } return triggerCause != null -} \ No newline at end of file +} From 575df97c52b2ad884d85db57ac87a78cdc676ce4 Mon Sep 17 00:00:00 2001 From: Ramzi El-Yafi Date: Fri, 13 Dec 2019 10:08:51 +0000 Subject: [PATCH 02/83] Mark integration test tasks with "big" node taint --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 5f1ae94300..ff3b1f3eb4 100644 --- a/build.gradle +++ b/build.gradle @@ -606,6 +606,7 @@ task allParallelIntegrationTest(type: ParallelTestGroup) { coresPerFork 5 memoryInGbPerFork 12 distribute DistributeTestsBy.METHOD + nodeTaints "big" } task allParallelUnitTest(type: ParallelTestGroup) { podLogLevel PodLogLevel.INFO @@ -624,6 +625,7 @@ task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) { coresPerFork 6 memoryInGbPerFork 10 distribute DistributeTestsBy.METHOD + nodeTaints "big" } task parallelRegressionTest(type: ParallelTestGroup) { testGroups "test", "integrationTest", "slowIntegrationTest", "smokeTest" @@ -632,6 +634,7 @@ task parallelRegressionTest(type: ParallelTestGroup) { coresPerFork 6 memoryInGbPerFork 10 distribute DistributeTestsBy.METHOD + nodeTaints "big" } task allParallelSmokeTest(type: ParallelTestGroup) { testGroups "slowIntegrationTest", "smokeTest" @@ -640,6 +643,7 @@ task allParallelSmokeTest(type: ParallelTestGroup) { coresPerFork 6 memoryInGbPerFork 10 distribute DistributeTestsBy.CLASS + nodeTaints "big" } apply plugin: 'com.r3.testing.distributed-testing' apply plugin: 'com.r3.testing.image-building' From 5458f11998548711712ce348c558c7b95459f614 Mon Sep 17 00:00:00 2001 From: Ramzi El-Yafi Date: Tue, 17 Dec 2019 13:02:56 +0000 Subject: [PATCH 03/83] Jenkins file for nightly regression tests (#5786) * Jenkins file for nightly regression tests * Use k8s instead of gke cluster --- .ci/dev/nightly-regression/Jenkinsfile | 65 ++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .ci/dev/nightly-regression/Jenkinsfile diff --git a/.ci/dev/nightly-regression/Jenkinsfile b/.ci/dev/nightly-regression/Jenkinsfile new file mode 100644 index 0000000000..44ea33875b --- /dev/null +++ b/.ci/dev/nightly-regression/Jenkinsfile @@ -0,0 +1,65 @@ +@Library('existing-build-control') +import static com.r3.build.BuildControl.killAllExistingBuildsForJob + +killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) + +pipeline { + agent { label 'k8s' } + options { + timestamps() + overrideIndexTriggers(false) + buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '7')) + } + triggers { + pollSCM ignorePostCommitHooks: true, scmpoll_spec: '@midnight' + } + + environment { + DOCKER_TAG_TO_USE = "${env.GIT_COMMIT.subSequence(0, 8)}" + EXECUTOR_NUMBER = "${env.EXECUTOR_NUMBER}" + BUILD_ID = "${env.BUILD_ID}-${env.JOB_NAME}" + ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials') + } + + stages { + stage('Generate Build Image') { + steps { + withCredentials([string(credentialsId: 'container_reg_passwd', variable: 'DOCKER_PUSH_PWD')]) { + sh "./gradlew " + + "-Dkubenetize=true " + + "-Ddocker.push.password=\"\${DOCKER_PUSH_PWD}\" " + + "-Ddocker.work.dir=\"/tmp/\${EXECUTOR_NUMBER}\" " + + "-Ddocker.build.tag=\"\${DOCKER_TAG_TO_USE}\"" + + " clean pushBuildImage --stacktrace" + } + sh "kubectl auth can-i get pods" + } + } + + stage('Regression Test') { + steps { + sh "./gradlew " + + "-DbuildId=\"\${BUILD_ID}\" " + + "-Dkubenetize=true " + + "-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " + + "-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " + + "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + + "-Dgit.branch=\"\${GIT_BRANCH}\" " + + "-Dgit.target.branch=\"\${GIT_BRANCH}\" " + + " parallelRegressionTest --stacktrace" + } + } + } + + + post { + always { + archiveArtifacts artifacts: '**/pod-logs/**/*.log', fingerprint: false + junit testResults: '**/build/test-results-xml/**/*.xml', allowEmptyResults: true + } + cleanup { + deleteDir() /* clean up our workspace */ + } + } +} + From 35c58f1b9bd99e91e8f846987a126749e39e9fe8 Mon Sep 17 00:00:00 2001 From: carolynequinn <44175553+carolynequinn@users.noreply.github.com> Date: Thu, 19 Dec 2019 18:28:11 +0000 Subject: [PATCH 04/83] DOCS: Update UAT.md (#5602) --- docs/source/corda-network/UAT.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/source/corda-network/UAT.md b/docs/source/corda-network/UAT.md index deff5dd389..06eee44129 100644 --- a/docs/source/corda-network/UAT.md +++ b/docs/source/corda-network/UAT.md @@ -1,9 +1,9 @@ -Corda Network: UAT Environment +Corda Network: Pre-Production Environment ============================= -Corda Network UAT seeks to provide a test environment which is as close as possible to Corda Network in its make-up and operation. +Corda Network Pre-Production (or UAT) seeks to provide a test environment which is as close as possible to Corda Network in its make-up and operation. -For owners of tested CorDapps with a firm plan to take them into production, a bespoke UAT environment can be provided by R3. Here, such CorDapps can be further tested in the network configuration they will experience in production, utilising relevant Corda Network Services (including the Identity Operator, and trusted notaries). +For owners of tested CorDapps with a firm plan to take them into production, a bespoke Pre-Production environment is provided. Here, such CorDapps can be further tested in the network configuration they will experience in production, utilising relevant Corda Network Services (including the Identity Operator, Network Map and notaries). Corda UAT is not intended for customers' full test cycles, as it is expected that the bulk of CorDapp testing will occur in simpler network configurations run by the CorDapp provider, but is available for testing of functionally complete and tested CorDapps in realistic network settings to simulate the real-world business environment, including the production settings of network parameters, Corda network services and supported Corda versions. @@ -12,10 +12,10 @@ UAT is therefore more aligned to the testing of the operational characteristics More information about UAT will continue to be uploaded on this site or related sub-sites. -Joining the UAT environment +Joining the Pre-Production environment --------------------------- -*The below joining steps assume the potential participant is joining the UAT environment directly, and as such is not “sponsoring” or onboarding other participants. If this is the case, please contact your Corda representative for how to ‘sponsor’ end-participants onto UAT.* +*The below joining steps assume the potential participant is joining the Pre-Production environment directly, and as such is not “sponsoring” or onboarding other participants. If this is the case, please contact your Corda representative for how to ‘sponsor’ end-participants on.* **Pre-requisites:** @@ -29,12 +29,12 @@ Joining the UAT environment * Access to the appropriate environment has been agreed with your project representative with sufficient advance notice (4 weeks standard but may be longer if you have special service requirements) to ensure appropriate SLAs can be in place. Your project representative will be able to supply the booking template. **Note**: -Corda Network UAT is governed by an [independent Foundation](https://corda.network/governance/index.html). +Corda Network Pre-Production is governed by an [independent Foundation](https://corda.network/governance/index.html). -Steps to join UAT environment ------------------------------ +Steps to join the Pre-Production environment +------------------------------------------- -Steps to join are outlined on the [Corda Network UAT microsite](http://uat.network.r3.com/pages/joining/joining.html) +Steps to join are outlined on the [Corda Network microsite](https://corda.network/participation/index.html) - follow any specific instructions for 'Pre-Production'. -For further questions on this process, please contact us - preferably on the mailing list: https://groups.io/g/corda-network +For further questions on this process, please contact us - preferably on the mailing list: https://groups.io/g/corda-network or at info@corda.network From bd436e640d1db572389222ec1ddab691e5bf0beb Mon Sep 17 00:00:00 2001 From: Ramzi El-Yafi Date: Fri, 20 Dec 2019 10:11:26 +0000 Subject: [PATCH 05/83] Fix report generation against regression builds (#5818) --- .ci/dev/regression/Jenkinsfile | 41 +++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 9447598306..99aa0d1a64 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -52,7 +52,46 @@ pipeline { always { archiveArtifacts artifacts: '**/pod-logs/**/*.log', fingerprint: false junit '**/build/test-results-xml/**/*.xml' - allure includeProperties: false, jdk: '', results: [[path: '**/build/test-results-xml/**']] + + script { + try { + /* + * Copy all JUnit results files into a single top level directory. + * This is necessary to stop the allure plugin from hitting out + * of memory errors due to being passed many directories with + * long paths. + * + * File names are pre-pended with the pod number when + * copied to avoid collisions between files where the same test + * classes have run on multiple pods. + */ + sh label: 'Compact test results', + script: + '''#!/bin/bash + shopt -s globstar + rm -rf allure-input + mkdir allure-input + + for i in **/test-results-xml/**/test-runs/test-reports/** + do + [ -f $i ] && + cp $i allure-input/$(echo $i | sed -e \\ + \'s/.*test-results-xml\\/.*-\\(.*\\)\\/test-runs\\/.*\\/\\(.*\\)$/\\1\\-\\2/\') + done + + echo "Finished compacting JUnit results" + ''' + allure includeProperties: false, + jdk: '', + results: [[path: '**/allure-input']] + } catch (err) { + echo("Allure report generation failed: $err") + + if (currentBuild.resultIsBetterOrEqualTo('SUCCESS')) { + currentBuild.result = 'UNSTABLE' + } + } + } } cleanup { deleteDir() /* clean up our workspace */ From 38fb51b74b068d9e05aff0f524a8e166ed60f387 Mon Sep 17 00:00:00 2001 From: Ramzi El-Yafi Date: Tue, 14 Jan 2020 11:10:42 +0000 Subject: [PATCH 06/83] Strategic fix for allure report generation memory issues (#5845) --- .ci/dev/regression/Jenkinsfile | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 99aa0d1a64..a4017c5c86 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -65,22 +65,13 @@ pipeline { * copied to avoid collisions between files where the same test * classes have run on multiple pods. */ - sh label: 'Compact test results', - script: - '''#!/bin/bash - shopt -s globstar - rm -rf allure-input - mkdir allure-input - - for i in **/test-results-xml/**/test-runs/test-reports/** - do - [ -f $i ] && - cp $i allure-input/$(echo $i | sed -e \\ - \'s/.*test-results-xml\\/.*-\\(.*\\)\\/test-runs\\/.*\\/\\(.*\\)$/\\1\\-\\2/\') - done - - echo "Finished compacting JUnit results" - ''' + fileOperations([fileCopyOperation( + includes: '**/test-results-xml/**/test-runs/test-reports/**', + targetLocation: 'allure-input', + flattenFiles: true, + renameFiles: true, + sourceCaptureExpression: '.*test-results-xml/.*-([\\d]+)/.*/([^/]+)$', + targetNameExpression: '$1-$2')]) allure includeProperties: false, jdk: '', results: [[path: '**/allure-input']] From 86347abe798b1621402f454f53cabece79cb4c1c Mon Sep 17 00:00:00 2001 From: Ramzi El-Yafi Date: Tue, 14 Jan 2020 11:52:05 +0000 Subject: [PATCH 07/83] TM-137 Daily email report for release branch regression tests (#5797) * Daily email report for release branch regression tests * Manual merge fix --- .ci/dev/regression/Jenkinsfile | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index a4017c5c86..4c0182e0b1 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -52,6 +52,39 @@ pipeline { always { archiveArtifacts artifacts: '**/pod-logs/**/*.log', fingerprint: false junit '**/build/test-results-xml/**/*.xml' + allure includeProperties: false, jdk: '', results: [[path: '**/build/test-results-xml/**']] + + script + { + // We want to send a summary email, but want to limit to once per day. + // Comparing the dates of the previous and current builds achieves this, + // i.e. we will only send an email for the first build on a given day. + def prevBuildDate = new Date( + currentBuild?.previousBuild.timeInMillis ?: 0).clearTime() + def currentBuildDate = new Date( + currentBuild.timeInMillis).clearTime() + + if (prevBuildDate != currentBuildDate) { + def statusSymbol = '\u2753' + switch(currentBuild.result) { + case 'SUCCESS': + statusSymbol = '\u2705' + break; + case 'UNSTABLE': + case 'FAILURE': + statusSymbol = '\u274c' + break; + default: + break; + } + + echo('First build for this date, sending summary email') + emailext to: '$DEFAULT_RECIPIENTS', + subject: "$statusSymbol" + '$BRANCH_NAME regression tests - $BUILD_STATUS', + mimeType: 'text/html', + body: '${SCRIPT, template="groovy-html.template"}' + } else { + echo('Already sent summary email today, suppressing') script { try { From b195e4f703ab6a6fa245e1e7d1da39c09c226b58 Mon Sep 17 00:00:00 2001 From: Ramzi El-Yafi Date: Thu, 16 Jan 2020 11:55:01 +0000 Subject: [PATCH 08/83] Manual fix of mergr for PR-5797 (#5856) --- .ci/dev/regression/Jenkinsfile | 67 +++++++++++++++++----------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 4c0182e0b1..942b864291 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -52,39 +52,6 @@ pipeline { always { archiveArtifacts artifacts: '**/pod-logs/**/*.log', fingerprint: false junit '**/build/test-results-xml/**/*.xml' - allure includeProperties: false, jdk: '', results: [[path: '**/build/test-results-xml/**']] - - script - { - // We want to send a summary email, but want to limit to once per day. - // Comparing the dates of the previous and current builds achieves this, - // i.e. we will only send an email for the first build on a given day. - def prevBuildDate = new Date( - currentBuild?.previousBuild.timeInMillis ?: 0).clearTime() - def currentBuildDate = new Date( - currentBuild.timeInMillis).clearTime() - - if (prevBuildDate != currentBuildDate) { - def statusSymbol = '\u2753' - switch(currentBuild.result) { - case 'SUCCESS': - statusSymbol = '\u2705' - break; - case 'UNSTABLE': - case 'FAILURE': - statusSymbol = '\u274c' - break; - default: - break; - } - - echo('First build for this date, sending summary email') - emailext to: '$DEFAULT_RECIPIENTS', - subject: "$statusSymbol" + '$BRANCH_NAME regression tests - $BUILD_STATUS', - mimeType: 'text/html', - body: '${SCRIPT, template="groovy-html.template"}' - } else { - echo('Already sent summary email today, suppressing') script { try { @@ -116,6 +83,40 @@ pipeline { } } } + + script + { + // We want to send a summary email, but want to limit to once per day. + // Comparing the dates of the previous and current builds achieves this, + // i.e. we will only send an email for the first build on a given day. + def prevBuildDate = new Date( + currentBuild?.previousBuild.timeInMillis ?: 0).clearTime() + def currentBuildDate = new Date( + currentBuild.timeInMillis).clearTime() + + if (prevBuildDate != currentBuildDate) { + def statusSymbol = '\u2753' + switch(currentBuild.result) { + case 'SUCCESS': + statusSymbol = '\u2705' + break; + case 'UNSTABLE': + case 'FAILURE': + statusSymbol = '\u274c' + break; + default: + break; + } + + echo('First build for this date, sending summary email') + emailext to: '$DEFAULT_RECIPIENTS', + subject: "$statusSymbol" + '$BRANCH_NAME regression tests - $BUILD_STATUS', + mimeType: 'text/html', + body: '${SCRIPT, template="groovy-html.template"}' + } else { + echo('Already sent summary email today, suppressing') + } + } } cleanup { deleteDir() /* clean up our workspace */ From f02bfc134b51981221a8ab47e965010e66e34a4e Mon Sep 17 00:00:00 2001 From: Christian Sailer Date: Thu, 23 Jan 2020 16:40:38 +0000 Subject: [PATCH 09/83] NOTICK disable max line length check in detekt. (#5882) --- detekt-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detekt-config.yml b/detekt-config.yml index 0fce7da45a..e1e15b2f99 100644 --- a/detekt-config.yml +++ b/detekt-config.yml @@ -172,7 +172,7 @@ style: ignoreNamedArgument: true ignoreEnums: true MaxLineLength: - active: true + active: false excludes: "**/buildSrc/**" maxLineLength: 140 excludePackageStatements: true From ffe75db464fc942d7074dc2234f0d738fd1ebfdd Mon Sep 17 00:00:00 2001 From: Ramzi El-Yafi Date: Fri, 24 Jan 2020 10:39:23 +0000 Subject: [PATCH 10/83] TM-170 Generic on-demand-test configuration (#5863) * Generic on-demand test configuration * Rename library "magic" string from existing-build-control to corda-shared-build-pipeline-steps --- .ci/dev/integration/Jenkinsfile | 2 +- .ci/dev/nightly-regression/Jenkinsfile | 2 +- .ci/dev/on-demand-tests/Jenkinsfile | 6 ++++++ .ci/dev/on-demand-tests/commentMappings.yml | 4 ++++ .ci/dev/regression/Jenkinsfile | 2 +- .ci/dev/smoke/Jenkinsfile | 2 +- .ci/dev/unit/Jenkinsfile | 2 +- Jenkinsfile | 4 ++-- 8 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 .ci/dev/on-demand-tests/Jenkinsfile create mode 100644 .ci/dev/on-demand-tests/commentMappings.yml diff --git a/.ci/dev/integration/Jenkinsfile b/.ci/dev/integration/Jenkinsfile index ca32e752b4..de89aec99e 100644 --- a/.ci/dev/integration/Jenkinsfile +++ b/.ci/dev/integration/Jenkinsfile @@ -1,5 +1,5 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob -@Library('existing-build-control') +@Library('corda-shared-build-pipeline-steps') import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) diff --git a/.ci/dev/nightly-regression/Jenkinsfile b/.ci/dev/nightly-regression/Jenkinsfile index 44ea33875b..db140bece0 100644 --- a/.ci/dev/nightly-regression/Jenkinsfile +++ b/.ci/dev/nightly-regression/Jenkinsfile @@ -1,4 +1,4 @@ -@Library('existing-build-control') +@Library('corda-shared-build-pipeline-steps') import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) diff --git a/.ci/dev/on-demand-tests/Jenkinsfile b/.ci/dev/on-demand-tests/Jenkinsfile new file mode 100644 index 0000000000..25127ef133 --- /dev/null +++ b/.ci/dev/on-demand-tests/Jenkinsfile @@ -0,0 +1,6 @@ +@Library('corda-shared-build-pipeline-steps') _ +import static com.r3.build.BuildControl.killAllExistingBuildsForJob + +killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) + +onDemandTestPipeline('k8s', '.ci/dev/on-demand-tests/commentMappings.yml') diff --git a/.ci/dev/on-demand-tests/commentMappings.yml b/.ci/dev/on-demand-tests/commentMappings.yml new file mode 100644 index 0000000000..3b68c8366c --- /dev/null +++ b/.ci/dev/on-demand-tests/commentMappings.yml @@ -0,0 +1,4 @@ +integration: { allParallelIntegrationTest } +pr-merge: { parallelRegressionTest } +smoke: { allParallelSmokeTest } +unit: { allParallelUnitTest } diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 942b864291..d08e37967c 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -1,4 +1,4 @@ -@Library('existing-build-control') +@Library('corda-shared-build-pipeline-steps') import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) diff --git a/.ci/dev/smoke/Jenkinsfile b/.ci/dev/smoke/Jenkinsfile index 16ae19b53d..f24c97a898 100644 --- a/.ci/dev/smoke/Jenkinsfile +++ b/.ci/dev/smoke/Jenkinsfile @@ -1,4 +1,4 @@ -@Library('existing-build-control') +@Library('corda-shared-build-pipeline-steps') import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) diff --git a/.ci/dev/unit/Jenkinsfile b/.ci/dev/unit/Jenkinsfile index 9bd4c4243b..736e4bf235 100644 --- a/.ci/dev/unit/Jenkinsfile +++ b/.ci/dev/unit/Jenkinsfile @@ -1,5 +1,5 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob -@Library('existing-build-control') +@Library('corda-shared-build-pipeline-steps') import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) diff --git a/Jenkinsfile b/Jenkinsfile index dcd0522239..0c5b0f188f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob -@Library('existing-build-control') +@Library('corda-shared-build-pipeline-steps') import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) @@ -71,4 +71,4 @@ pipeline { deleteDir() /* clean up our workspace */ } } -} \ No newline at end of file +} From 8f7367add7c5ec5b05650e35e8bfd1b957736369 Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Thu, 19 Dec 2019 10:52:34 +0000 Subject: [PATCH 11/83] CORDA-3509: add documentation about Corda features and their corresponding platformVersion and network minimumPlatformVersion --- docs/source/api-contract-constraints.rst | 3 + docs/source/features-versions.rst | 85 ++++++++++++++++++++++++ docs/source/network-map.rst | 4 ++ 3 files changed, 92 insertions(+) create mode 100644 docs/source/features-versions.rst diff --git a/docs/source/api-contract-constraints.rst b/docs/source/api-contract-constraints.rst index ad00dbc65f..f019212989 100644 --- a/docs/source/api-contract-constraints.rst +++ b/docs/source/api-contract-constraints.rst @@ -12,6 +12,9 @@ API: Contract Constraints .. note:: Before reading this page, you should be familiar with the key concepts of :doc:`key-concepts-contracts`. +.. note:: As of Corda |corda_version| the `minimumPlatformVersion` required to use these features is 4 + (see :ref:`Network Parameters ` and :doc:`features-versions` for more details). + .. contents:: Reasons for Contract Constraints diff --git a/docs/source/features-versions.rst b/docs/source/features-versions.rst new file mode 100644 index 0000000000..f103bb7a9d --- /dev/null +++ b/docs/source/features-versions.rst @@ -0,0 +1,85 @@ +Corda Features to Versions +========================== + +New versions of Corda introduce new features. These fall into one of three categories which have subtle but important implications for +node owners, application developers and network operators. + +The first set are changes that have no impact on application developers or the Corda network protocol. An example would be support for +a new HSM or database system, for example, and which are of interest only to a node's operator. + +The second set are new or changed APIs, which are of interest to CorDapp developers. When a release of Corda ships such features, the +Platform Version of that node is incremented so that a CorDapp that relies on such a new or changed feature can detect this (eg to +prevent it from running on a node without the feature or to trigger an alternative optimised codepath if the feature is present). The +app developer should set the CorDapp's minimumPlatformVersion parameter to signal the minimum Platform Version against which the app +can run or has been tested. If the application has also been tested against a greater platform version and can exploit it if present, +the node can also set the targetPlatformVersion field. + +The third set of changes are those which could affect the operation of a Corda network. Examples would include a change to the +serialisation format or flow/wire protocol, or introduction of a new transaction component. These are changes to the core data model and +these features have the property that it is not safe for any node or application to take advantage of until all nodes on the network +are capable of understanding them. Such features are thus only enabled in a node if the network to which it is connected has published +a minimumPlatformVersion in its network parameters that is greater than or equal to the Corda Platform Version that introduced the +feature. For example, Corda 4.0 nodes, which implement Corda Platform Version 4, can only take advantage of the Corda Reference States +feature when connected to a network with mPV 4. + +Generally the rules work this way: + +- IF (CorDapp.mPV > node.PV) THEN + prevent the CorDapp from running (this signals that it cannot run without the new feature). +- IF (CorDapp.mPV <= node.PV AND CorDapp.targetPV < node.PV) THEN + this means the node is ahead of the CorDapp so it might choose to trigger some code paths that emulate some old behaviour that the + CorDapp expected on that version. +- IF (CorDapp.mPV <= node.PV AND CorDapp.targetPV == node.PV) THEN + just use the new mechanism because the CorDapp and the node are perfectly aligned. +- IF (CorDapp.mPV <= node.PV AND CorDapp.targetPV > node.PV) THEN + this means that the CorDapp is ahead of the running node, but it must have some alternative runtime code paths built in to be able + to simulate the new behaviour using old apis. + +.. list-table:: Corda Features + :header-rows: 1 + + * - Feature + - Corda Platform Version (PV) + - Min Network Platform Version (network mPV) + - Introduced in OS version + - Introduced in Enterprise version + * - Observer Nodes + - 2 + - 2 + - 2.0 + - n/a + * - Corda Serialization Framework + - 3 + - 3 + - 3.0 + - 3.0 + * - Hash Constraints + - 1 + - 1 + - 1.0 + - 1.0 + * - Whitelist Constraints + - 3 + - 3 + - 3.0 + - 3.0 + * - Inline Finality Flow + - 4 + - 3 + - 4.0 + - 4.0 + * - Reference States + - 4 + - 4 + - 4.0 + - 4.0 + * - Signature Constraints + - 4 + - 4 + - 4.0 + - 4.0 + * - Underlying Support for Accounts + - 5 + - 4 + - 4.3 + - 4.3 diff --git a/docs/source/network-map.rst b/docs/source/network-map.rst index fba6220071..9c7ffab830 100644 --- a/docs/source/network-map.rst +++ b/docs/source/network-map.rst @@ -97,6 +97,8 @@ cluster generated like this can be sized for the maximum size you may need, and More information can be found in :doc:`network-bootstrapper`. +.. _network-parameters: + Network parameters ------------------ @@ -152,6 +154,8 @@ The current set of network parameters: Encountering an owned contract in a JAR that is not signed by the rightful owner is most likely a sign of malicious behaviour, and should be reported. The transaction verification logic will throw an exception when this happens. +.. note:: To determine which `minimumPlatformVersion` a zone must mandate in order to permit all the features of Corda |corda_version| see :doc:`features-versions` + More parameters will be added in future releases to regulate things like allowed port numbers, whether or not IPv6 connectivity is required for zone members, required cryptographic algorithms and roll-out schedules (e.g. for moving to post quantum cryptography), parameters related to SGX and so on. From 338671e6b2de95158cd425cf372dda2c207bf42d Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Mon, 27 Jan 2020 09:42:28 +0000 Subject: [PATCH 12/83] CORDA-3509: add documentation about Corda features and their corresponding platformVersion and network minimumPlatformVersion --- docs/source/features-versions.rst | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/docs/source/features-versions.rst b/docs/source/features-versions.rst index f103bb7a9d..9ed8801926 100644 --- a/docs/source/features-versions.rst +++ b/docs/source/features-versions.rst @@ -22,18 +22,13 @@ a minimumPlatformVersion in its network parameters that is greater than or equal feature. For example, Corda 4.0 nodes, which implement Corda Platform Version 4, can only take advantage of the Corda Reference States feature when connected to a network with mPV 4. -Generally the rules work this way: - -- IF (CorDapp.mPV > node.PV) THEN - prevent the CorDapp from running (this signals that it cannot run without the new feature). -- IF (CorDapp.mPV <= node.PV AND CorDapp.targetPV < node.PV) THEN - this means the node is ahead of the CorDapp so it might choose to trigger some code paths that emulate some old behaviour that the - CorDapp expected on that version. -- IF (CorDapp.mPV <= node.PV AND CorDapp.targetPV == node.PV) THEN - just use the new mechanism because the CorDapp and the node are perfectly aligned. -- IF (CorDapp.mPV <= node.PV AND CorDapp.targetPV > node.PV) THEN - this means that the CorDapp is ahead of the running node, but it must have some alternative runtime code paths built in to be able - to simulate the new behaviour using old apis. +If there is a Platform Version below which your application will not run or is not supported, then signal that with the application's +`CorDapp.mPV` field. This will prevent older nodes from running your app. Nodes which support newer Platform Versions may also use this +field to trigger code paths that emulate behaviours that were in force on that older Platform Version to maximise compatibility. However, +if you have tested your app against newer versions of Corda and know your node can take advantage of the new Platform Version behaviours +if present, you can signal this by using `CorDapp.targetPV` to declare the latest Platform Version against which the app has been tested +and is known to work. In this way, it is possible to ship CorDapps that can both run on all nodes supporting some minimum Platform Version +of Corda as well as opt in to newer features should they happen to be available on any given node. .. list-table:: Corda Features :header-rows: 1 From b5d9da25ecabc7a3791bffef7c0caf357571e116 Mon Sep 17 00:00:00 2001 From: nargas-ritu Date: Fri, 31 Jan 2020 13:54:01 +0000 Subject: [PATCH 13/83] Bump OS release version 4.5 --- .../src/main/kotlin/net/corda/common/logging/Constants.kt | 2 +- constants.properties | 2 +- docker/src/bash/example-mini-network.sh | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt b/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt index bb666a3c6e..6e878a6616 100644 --- a/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt +++ b/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt @@ -9,4 +9,4 @@ package net.corda.common.logging * (originally added to source control for ease of use) */ -internal const val CURRENT_MAJOR_RELEASE = "4.4-SNAPSHOT" \ No newline at end of file +internal const val CURRENT_MAJOR_RELEASE = "4.5-SNAPSHOT" diff --git a/constants.properties b/constants.properties index 2d7ecca94b..8c7463359b 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.4 +cordaVersion=4.5 versionSuffix=SNAPSHOT gradlePluginsVersion=5.0.6 kotlinVersion=1.2.71 diff --git a/docker/src/bash/example-mini-network.sh b/docker/src/bash/example-mini-network.sh index 15e4e2ca82..dff18f71e4 100755 --- a/docker/src/bash/example-mini-network.sh +++ b/docker/src/bash/example-mini-network.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash NODE_LIST=("dockerNode1" "dockerNode2" "dockerNode3") NETWORK_NAME=mininet -CORDAPP_VERSION="4.4-SNAPSHOT" -DOCKER_IMAGE_VERSION="corda-zulu-4.4-snapshot" +CORDAPP_VERSION="4.5-SNAPSHOT" +DOCKER_IMAGE_VERSION="corda-zulu-4.5-snapshot" mkdir cordapps rm -f cordapps/* From 90df56c173bb5ab2d5bfb9447ff73c020e5fa428 Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Mon, 3 Feb 2020 09:47:12 +0000 Subject: [PATCH 14/83] CORDA-3565: Port `ServiceStateSupport` from ENT to OS (#5916) * CORDA-3565: `ServiceStateSupport` and supporting classes * CORDA-3565:Plug `ServiceLifecycleSupport` into `MessagingService` * CORDA-3565: Detekt baseline update * CORDA-3565: React to MessagingServer going up and addition logging for up/down Co-authored-by: Matthew Nesbit --- detekt-baseline.xml | 4 +- .../lifecycle/LifecycleStatusHelper.kt | 30 ++++++++++++ .../lifecycle/ServiceLifecycleSupport.kt | 41 ++++++++++++++++ .../internal/lifecycle/ServiceStateHelper.kt | 47 +++++++++++++++++++ .../messaging/ArtemisMessagingTest.kt | 2 +- .../net/corda/node/internal/AbstractNode.kt | 25 +++++++--- .../kotlin/net/corda/node/internal/Node.kt | 2 +- .../node/services/messaging/Messaging.kt | 8 +--- .../services/messaging/P2PMessagingClient.kt | 24 +++++----- .../node/internal/MockNodeMessagingService.kt | 20 +++++--- 10 files changed, 167 insertions(+), 36 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/LifecycleStatusHelper.kt create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/ServiceLifecycleSupport.kt create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/ServiceStateHelper.kt diff --git a/detekt-baseline.xml b/detekt-baseline.xml index d652aa4996..663eed7d6d 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -1715,7 +1715,7 @@ TooManyFunctions:LedgerTransaction.kt$LedgerTransaction : FullTransaction TooManyFunctions:LocalSerializerFactory.kt$DefaultLocalSerializerFactory : LocalSerializerFactory TooManyFunctions:LocalTypeInformationBuilder.kt$LocalTypeInformationBuilder - TooManyFunctions:MockNodeMessagingService.kt$MockNodeMessagingService : SingletonSerializeAsTokenMessagingService + TooManyFunctions:MockNodeMessagingService.kt$MockNodeMessagingService : SingletonSerializeAsTokenMessagingServiceServiceStateSupport TooManyFunctions:NetworkBootstrapper.kt$NetworkBootstrapper : NetworkBootstrapperWithOverridableParameters TooManyFunctions:Node.kt$Node : AbstractNode TooManyFunctions:NodeAttachmentService.kt$NodeAttachmentService : AttachmentStorageInternalSingletonSerializeAsToken @@ -1724,7 +1724,7 @@ TooManyFunctions:NodeVaultService.kt$NodeVaultService : SingletonSerializeAsTokenVaultServiceInternal TooManyFunctions:OGSwapPricingExample.kt$SwapPricingExample TooManyFunctions:ObservableUtilities.kt$net.corda.client.jfx.utils.ObservableUtilities.kt - TooManyFunctions:P2PMessagingClient.kt$P2PMessagingClient : SingletonSerializeAsTokenMessagingServiceAddressToArtemisQueueResolver + TooManyFunctions:P2PMessagingClient.kt$P2PMessagingClient : SingletonSerializeAsTokenMessagingServiceAddressToArtemisQueueResolverServiceStateSupport TooManyFunctions:PathUtils.kt$net.corda.core.internal.PathUtils.kt TooManyFunctions:Perceivable.kt$net.corda.finance.contracts.universal.Perceivable.kt TooManyFunctions:PersistentIdentityService.kt$PersistentIdentityService : SingletonSerializeAsTokenIdentityServiceInternal diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/LifecycleStatusHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/LifecycleStatusHelper.kt new file mode 100644 index 0000000000..16a61ec502 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/LifecycleStatusHelper.kt @@ -0,0 +1,30 @@ +package net.corda.nodeapi.internal.lifecycle + +import java.util.concurrent.ConcurrentHashMap +import javax.json.Json + +object LifecycleStatusHelper { + private val serviceStatusMap = ConcurrentHashMap() + + fun setServiceStatus(serviceName: String, active: Boolean) { + serviceStatusMap[serviceName] = active + } + + fun getServiceStatus(serviceName: String) = serviceStatusMap.getOrDefault(serviceName, false) + + /** + * Return a string copy of a JSON object containing the status of each service, + * and whether this bridge is the master. + */ + fun getServicesStatusReport(isMaster: Boolean): String { + return Json.createObjectBuilder().apply { + val statusList = Json.createArrayBuilder().apply { + serviceStatusMap.forEach { name: String, status: Boolean -> + add(Json.createObjectBuilder().add(name, status).build()) + } + } + add("master", isMaster) + add("services", statusList) + }.build().toString() + } +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/ServiceLifecycleSupport.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/ServiceLifecycleSupport.kt new file mode 100644 index 0000000000..c74a8e5436 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/ServiceLifecycleSupport.kt @@ -0,0 +1,41 @@ +package net.corda.nodeapi.internal.lifecycle + +import rx.Observable + +/** + * Basic interface to represent the dynamic life cycles of services that may be running, but may have to await external dependencies. + * Implementations of this should be implemented in a thread safe fashion. + */ +interface ServiceStateSupport { + /** + * Reads the current dynamic status of the service, which should only become true after the service has been started, + * any dynamic resources have been started/registered and any network connections have been completed. + * Failure to acquire a resource, or manual stop of the service, should return this to false. + */ + val active: Boolean + + /** + * This Observer signals changes in the [active] variable, it should not be triggered for events that don't flip the [active] state. + */ + val activeChange: Observable +} + +/** + * Simple interface for generic start/stop service lifecycle and the [active] flag indicating runtime ready state. + */ +interface ServiceLifecycleSupport : ServiceStateSupport, AutoCloseable { + /** + * Manual call to allow the service to start the process towards becoming active. + * Note wiring up service dependencies should happen in the constructor phase, unless this is to avoid a circular reference. + * Also, resources allocated as a result of start should be cleaned up as much as possible by stop. + * The [start] method should allow multiple reuse, assuming a [stop] call was made to clear the state. + */ + fun start() + + /** + * Release the resources created by [start] and drops the [active] state to false. + */ + fun stop() + + override fun close() = stop() +} \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/ServiceStateHelper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/ServiceStateHelper.kt new file mode 100644 index 0000000000..f1c42a1773 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/lifecycle/ServiceStateHelper.kt @@ -0,0 +1,47 @@ +package net.corda.nodeapi.internal.lifecycle + +import org.slf4j.Logger +import rx.Observable +import rx.subjects.BehaviorSubject +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock + +/** + * Simple implementation of [ServiceStateSupport] service domino logic using RxObservables. + */ +class ServiceStateHelper(private val log: Logger, private val serviceName: String = log.name.split(".").last()) : ServiceStateSupport { + private val lock = ReentrantLock() + + // Volatile to prevent deadlocks when locking on read. + @Volatile + private var _active: Boolean = false + override var active: Boolean + get() = _active + set(value) { + lock.withLock { + if (value != _active) { + _active = value + log.info("Status change to $value") + LifecycleStatusHelper.setServiceStatus(serviceName, value) + _activeChange.onNext(value) + } + } + } + + private val _activeChange: BehaviorSubject = BehaviorSubject.create(false) + private val _threadSafeObservable: Observable = _activeChange.serialize().distinctUntilChanged() + override val activeChange: Observable + get() = _threadSafeObservable +} + +/** + * Simple implementation of [ServiceStateSupport] where it only reports [active] true when a set of dependencies are all [active] true. + */ +class ServiceStateCombiner(val services: List) : ServiceStateSupport { + override val active: Boolean + get() = services.all { it.active } + + private val _activeChange = Observable.combineLatest(services.map { it.activeChange }, { x -> x.all { y -> y as Boolean } }).serialize().distinctUntilChanged() + override val activeChange: Observable + get() = _activeChange +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt index 6808d45c53..d3f7d2d4a2 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt @@ -212,7 +212,7 @@ class ArtemisMessagingTest { startNodeMessagingClient(maxMessageSize = clientMaxMessageSize) // Run after the handlers are added, otherwise (some of) the messages get delivered and discarded / dead-lettered. - thread(isDaemon = true) { messagingClient.run() } + thread(isDaemon = true) { messagingClient.start() } return Pair(messagingClient, receivedMessages) } 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 38675eb979..496b9c0f7f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -57,6 +57,7 @@ import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.SerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken +import net.corda.core.toFuture import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.days @@ -316,7 +317,13 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val contractUpgradeService = ContractUpgradeServiceImpl(cacheFactory).tokenize() val auditService = DummyAuditService().tokenize() @Suppress("LeakingThis") - protected val network: MessagingService = makeMessagingService().tokenize() + protected val network: MessagingService = makeMessagingService().tokenize().apply { + activeChange.subscribe({ + log.info("MessagingService active change to: $it") + }, { + log.warn("MessagingService subscription error", it) + }) + } val services = ServiceHubInternalImpl().tokenize() @Suppress("LeakingThis") val smm = makeStateMachineManager() @@ -535,16 +542,20 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val resultingNodeInfo = createStartedNode(nodeInfo, rpcOps, notaryService).also { _started = it } val readyFuture = smmStartedFuture.flatMap { log.debug("SMM ready") - network.ready + network.activeChange.filter { it }.toFuture() } resultingNodeInfo to readyFuture } - readyFuture.map { - // NB: Dispatch lifecycle events outside of transaction to ensure attachments and the like persisted into the DB - log.debug("Distributing events") - nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.AfterNodeStart(nodeServicesContext)) - nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.StateMachineStarted(nodeServicesContext)) + readyFuture.map { ready -> + if (ready) { + // NB: Dispatch lifecycle events outside of transaction to ensure attachments and the like persisted into the DB + log.debug("Distributing events") + nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.AfterNodeStart(nodeServicesContext)) + nodeLifecycleEventsDistributor.distributeEvent(NodeLifecycleEvent.StateMachineStarted(nodeServicesContext)) + } else { + log.warn("Not distributing events as NetworkMap is not ready") + } } return resultingNodeInfo } diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 359d8ad5de..4914df4833 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -634,7 +634,7 @@ open class Node(configuration: NodeConfiguration, fun run() { internalRpcMessagingClient?.start(rpcBroker!!.serverControl) printBasicNodeInfo("Running P2PMessaging loop") - (network as P2PMessagingClient).run() + (network as P2PMessagingClient).start() } private var shutdown = false diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt b/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt index a5f2868dc7..a9be394ac9 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/Messaging.kt @@ -13,6 +13,7 @@ import net.corda.core.utilities.ByteSequence import net.corda.node.services.statemachine.DeduplicationId import net.corda.node.services.statemachine.ExternalEvent import net.corda.node.services.statemachine.SenderDeduplicationId +import net.corda.nodeapi.internal.lifecycle.ServiceLifecycleSupport import java.time.Instant import javax.annotation.concurrent.ThreadSafe @@ -27,7 +28,7 @@ import javax.annotation.concurrent.ThreadSafe * is *reliable* and as such messages may be stored to disk once queued. */ @ThreadSafe -interface MessagingService : AutoCloseable { +interface MessagingService : ServiceLifecycleSupport { /** * A unique identifier for this sender that changes whenever a node restarts. This is used in conjunction with a sequence * number for message de-duplication at the recipient. @@ -107,11 +108,6 @@ interface MessagingService : AutoCloseable { /** Returns an address that refers to this node. */ val myAddress: SingleMessageRecipient - - /** - * Signals when ready and fully operational - */ - val ready: CordaFuture } fun MessagingService.send(topicSession: String, payload: Any, to: MessageRecipients, deduplicationId: SenderDeduplicationId = SenderDeduplicationId(DeduplicationId.createRandom(newSecureRandom()), ourSenderUUID), additionalHeaders: Map = emptyMap()) = send(createMessage(topicSession, payload.serialize().bytes, deduplicationId, additionalHeaders), to) 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 713eba071e..b4b74bc505 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 @@ -7,8 +7,6 @@ import net.corda.core.flows.StateMachineRunId import net.corda.core.identity.CordaX500Name import net.corda.core.internal.NamedCacheFactory import net.corda.core.internal.ThreadBox -import net.corda.core.internal.concurrent.OpenFuture -import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient @@ -42,6 +40,8 @@ import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.PEERS_PREF import net.corda.nodeapi.internal.ArtemisTcpTransport.Companion.p2pConnectorTcpTransport import net.corda.nodeapi.internal.bridging.BridgeControl import net.corda.nodeapi.internal.bridging.BridgeEntry +import net.corda.nodeapi.internal.lifecycle.ServiceStateHelper +import net.corda.nodeapi.internal.lifecycle.ServiceStateSupport import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.requireMessageSize import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration @@ -92,8 +92,9 @@ class P2PMessagingClient(val config: NodeConfiguration, private val metricRegistry: MetricRegistry, cacheFactory: NamedCacheFactory, private val isDrainingModeOn: () -> Boolean, - private val drainingModeWasChangedEvents: Observable> -) : SingletonSerializeAsToken(), MessagingService, AddressToArtemisQueueResolver { + private val drainingModeWasChangedEvents: Observable>, + private val stateHelper: ServiceStateHelper = ServiceStateHelper(log) +) : SingletonSerializeAsToken(), MessagingService, AddressToArtemisQueueResolver, ServiceStateSupport by stateHelper { companion object { private val log = contextLogger() } @@ -140,13 +141,11 @@ class P2PMessagingClient(val config: NodeConfiguration, private val delayStartQueues = Collections.newSetFromMap(ConcurrentHashMap()) private val handlers = ConcurrentHashMap() - private val handlersChangedSignal = java.lang.Object() + private val handlersChangedSignal = Object() private val deduplicator = P2PMessageDeduplicator(cacheFactory, database) internal var messagingExecutor: MessagingExecutor? = null - override val ready: OpenFuture = openFuture() - /** * @param myIdentity The primary identity of the node, which defines the messaging address for externally received messages. * It is also used to construct the myAddress field, which is ultimately advertised in the network map. @@ -332,7 +331,7 @@ class P2PMessagingClient(val config: NodeConfiguration, /** * Starts the p2p event loop: this method only returns once [stop] has been called. */ - fun run() { + override fun start() { val latch = CountDownLatch(1) try { synchronized(handlersChangedSignal) { @@ -355,8 +354,8 @@ class P2PMessagingClient(val config: NodeConfiguration, p2pConsumer!! } consumer.start() - log.debug("Signalling ready") - ready.set(null) + log.debug("Signalling active") + stateHelper.active = true log.debug("Awaiting on latch") latch.await() } finally { @@ -456,12 +455,13 @@ class P2PMessagingClient(val config: NodeConfiguration, * from a thread that's a part of the [net.corda.node.utilities.AffinityExecutor] given to the constructor, * it returns immediately and shutdown is asynchronous. */ - fun stop() { + override fun stop() { val running = state.locked { // We allow stop() to be called without a run() in between, but it must have at least been started. check(started) val prevRunning = running running = false + stateHelper.active = false networkChangeSubscription?.unsubscribe() require(p2pConsumer != null, { "stop can't be called twice" }) require(producer != null, { "stop can't be called twice" }) @@ -504,8 +504,6 @@ class P2PMessagingClient(val config: NodeConfiguration, } } - override fun close() = stop() - @Suspendable override fun send(message: Message, target: MessageRecipients, sequenceKey: Any) { requireMessageSize(message.data.size, maxMessageSize) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockNodeMessagingService.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockNodeMessagingService.kt index 913d26a2d6..ac8b2606bc 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockNodeMessagingService.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/MockNodeMessagingService.kt @@ -5,8 +5,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.PLATFORM_VERSION import net.corda.core.internal.ThreadBox -import net.corda.core.internal.concurrent.OpenFuture -import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.MessageRecipients import net.corda.core.node.services.PartyInfo import net.corda.core.serialization.SingletonSerializeAsToken @@ -19,6 +17,8 @@ import net.corda.node.services.statemachine.DeduplicationId import net.corda.node.services.statemachine.ExternalEvent import net.corda.node.services.statemachine.SenderDeduplicationId import net.corda.node.utilities.AffinityExecutor +import net.corda.nodeapi.internal.lifecycle.ServiceStateHelper +import net.corda.nodeapi.internal.lifecycle.ServiceStateSupport import net.corda.testing.node.InMemoryMessagingNetwork import java.time.Instant import java.util.* @@ -28,7 +28,9 @@ import kotlin.concurrent.thread @ThreadSafe class MockNodeMessagingService(private val configuration: NodeConfiguration, - private val executor: AffinityExecutor) : SingletonSerializeAsToken(), MessagingService { + private val executor: AffinityExecutor, + private val stateHelper: ServiceStateHelper = ServiceStateHelper(log)) : SingletonSerializeAsToken(), + MessagingService, ServiceStateSupport by stateHelper { private companion object { private val log = contextLogger() } @@ -38,8 +40,6 @@ class MockNodeMessagingService(private val configuration: NodeConfiguration, @Volatile private var running = true - override val ready: OpenFuture = openFuture() - private inner class InnerState { val handlers: MutableList = ArrayList() val pendingRedelivery = LinkedHashSet() @@ -58,6 +58,14 @@ class MockNodeMessagingService(private val configuration: NodeConfiguration, var spy: MessagingServiceSpy? = null + override fun start() { + throw IllegalAccessException() + } + + override fun stop() { + throw IllegalAccessException() + } + /** * @param manuallyPumped if set to true, then you are expected to call [MockNodeMessagingService.pumpReceive] * in order to cause the delivery of a single message, which will occur on the thread of the caller. If set to false @@ -89,7 +97,7 @@ class MockNodeMessagingService(private val configuration: NodeConfiguration, } network.addNotaryIdentity(this, notaryService) - ready.set(null) + stateHelper.active = true } override fun getAddressOfParty(partyInfo: PartyInfo): MessageRecipients { From 5a4bdc0a00b7f3c623a8cfad33a6da4a2515287e Mon Sep 17 00:00:00 2001 From: Denis Rekalov Date: Mon, 10 Feb 2020 17:40:23 +0000 Subject: [PATCH 15/83] ENT-4915, ENT-4926, CORDA-3585: Update docs for X500 name and SSH shell hostkey --- docs/source/node-naming.rst | 14 +++++++++++--- docs/source/shell.rst | 2 ++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/source/node-naming.rst b/docs/source/node-naming.rst index a25522b86a..f698ddbdc9 100644 --- a/docs/source/node-naming.rst +++ b/docs/source/node-naming.rst @@ -27,9 +27,11 @@ The name must also obey the following constraints: * The ``organisation``, ``locality`` and ``country`` attributes are present - * The ``state``, ``organisational-unit`` and ``common name`` attributes are optional +* The ``state``, ``organisational-unit`` and ``common name`` attributes are optional -* The fields of the name have the following maximum character lengths: +* The maximum number of characters in the whole x500 name string is 128 characters + +* The fields of the name have character lengths **less** than the following maximum values: * Common name: 64 * Organisation: 128 @@ -40,12 +42,18 @@ The name must also obey the following constraints: * The ``country`` attribute is a valid `ISO 3166-1` two letter code in upper-case * The ``organisation`` field of the name obeys the following constraints: + * Has at least two letters - * Does not include the following characters: ``,`` , ``"``, ``\`` + +* All data fields adhere to the following constraints: + + * Upper-case first letter + * Does not include the following characters: ``,``, ``=``, ``$``, ``"``, ``'``, ``\`` * Is in NFKC normalization form * Does not contain the null character * Only the latin, common and inherited unicode scripts are supported * No double-spacing + * No leading or trailing whitespace This is to avoid right-to-left issues, debugging issues when we can't pronounce names over the phone, and character confusability attacks. diff --git a/docs/source/shell.rst b/docs/source/shell.rst index 4e0ffda81a..07d2d55fa4 100644 --- a/docs/source/shell.rst +++ b/docs/source/shell.rst @@ -91,6 +91,8 @@ The host key is loaded from the ``/sshkey/hostkey.pem`` fil generated automatically. In development mode, the seed may be specified to give the same results on the same computer in order to avoid host-checking errors. +Only RSA key is currently supported as a host key. If ``hostkey.pem`` is not RSA, it will be replaced by the newly generated RSA key. + Connecting to the shell *********************** From 9ccbfe178c267a64da1732fb627b9dabcbefb044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Waldemar=20=C5=BBurowski?= <45210402+wzur-r3@users.noreply.github.com> Date: Tue, 11 Feb 2020 08:40:22 +0000 Subject: [PATCH 16/83] Updating Artifactory URLs for Corda OS 4.3 (#5930) * Switching Artifactory URLs from obsolete ci-artifactory to software.r3.com --- build.gradle | 4 ++-- create-jdk8u/build.gradle | 2 +- docker/src/bash/example-mini-network.sh | 8 ++++---- docs/source/network-builder.rst | 4 ++-- docs/source/node-upgrade-notes.rst | 2 +- docs/source/testnet-explorer-corda.rst | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/build.gradle b/build.gradle index 93001ccf3b..6e49f4f5f1 100644 --- a/build.gradle +++ b/build.gradle @@ -107,7 +107,7 @@ buildscript { ext.artifactory_plugin_version = constants.getProperty('artifactoryPluginVersion') ext.hikari_version = '3.3.1' ext.liquibase_version = '3.6.3' - ext.artifactory_contextUrl = 'https://ci-artifactory.corda.r3cev.com/artifactory' + ext.artifactory_contextUrl = 'https://software.r3.com/artifactory' ext.snake_yaml_version = constants.getProperty('snakeYamlVersion') ext.docker_compose_rule_version = '0.35.0' ext.selenium_version = '3.141.59' @@ -153,7 +153,7 @@ buildscript { url 'https://kotlin.bintray.com/kotlinx' } maven { - url "https://ci-artifactory.corda.r3cev.com/artifactory/corda-dependencies-dev" + url "$artifactory_contextUrl/corda-dependencies-dev" } maven { url "$artifactory_contextUrl/corda-releases" diff --git a/create-jdk8u/build.gradle b/create-jdk8u/build.gradle index 7c03dcbe3e..809bfdadc5 100644 --- a/create-jdk8u/build.gradle +++ b/create-jdk8u/build.gradle @@ -3,7 +3,7 @@ buildscript { file("../constants.properties").withInputStream { constants.load(it) } ext { - artifactory_contextUrl = 'https://ci-artifactory.corda.r3cev.com/artifactory' + artifactory_contextUrl = 'https://software.r3.com/artifactory' artifactory_plugin_version = constants.getProperty('artifactoryPluginVersion') proguard_version = constants.getProperty("proguardVersion") } diff --git a/docker/src/bash/example-mini-network.sh b/docker/src/bash/example-mini-network.sh index 33da0fbf25..569ea52e25 100755 --- a/docker/src/bash/example-mini-network.sh +++ b/docker/src/bash/example-mini-network.sh @@ -7,9 +7,9 @@ DOCKER_IMAGE_VERSION="corda-zulu-4.3-SNAPSHOT" mkdir cordapps rm -f cordapps/* -wget -O cordapps/finance-contracts.jar https://ci-artifactory.corda.r3cev.com/artifactory/list/corda-dev/net/corda/corda-finance-contracts/${CORDAPP_VERSION}/corda-finance-contracts-${CORDAPP_VERSION}.jar -wget -O cordapps/finance-workflows.jar https://ci-artifactory.corda.r3cev.com/artifactory/list/corda-dev/net/corda/corda-finance-workflows/${CORDAPP_VERSION}/corda-finance-workflows-${CORDAPP_VERSION}.jar -wget -O cordapps/confidential-identities.jar https://ci-artifactory.corda.r3cev.com/artifactory/list/corda-dev/net/corda/corda-confidential-identities/${CORDAPP_VERSION}/corda-confidential-identities-${CORDAPP_VERSION}.jar +wget -O cordapps/finance-contracts.jar https://software.r3.com/artifactory/list/corda-dev/net/corda/corda-finance-contracts/${CORDAPP_VERSION}/corda-finance-contracts-${CORDAPP_VERSION}.jar +wget -O cordapps/finance-workflows.jar https://software.r3.com/artifactory/list/corda-dev/net/corda/corda-finance-workflows/${CORDAPP_VERSION}/corda-finance-workflows-${CORDAPP_VERSION}.jar +wget -O cordapps/confidential-identities.jar https://software.r3.com/artifactory/list/corda-dev/net/corda/corda-confidential-identities/${CORDAPP_VERSION}/corda-confidential-identities-${CORDAPP_VERSION}.jar rm keystore @@ -83,4 +83,4 @@ do --name ${NODE} \ --network="${NETWORK_NAME}" \ corda/${DOCKER_IMAGE_VERSION}:latest config-generator --generic -done \ No newline at end of file +done diff --git a/docs/source/network-builder.rst b/docs/source/network-builder.rst index 1629920948..168888ee21 100644 --- a/docs/source/network-builder.rst +++ b/docs/source/network-builder.rst @@ -16,7 +16,7 @@ Unlike the official image, a `node.conf` file and CorDapps are embedded into the 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: @@ -195,4 +195,4 @@ node has been started correctly, run the following in the previously connected S Shutting down the nodes ----------------------- -Run ``docker kill $(docker ps -q)`` to kill all running Docker processes. \ No newline at end of file +Run ``docker kill $(docker ps -q)`` to kill all running Docker processes. diff --git a/docs/source/node-upgrade-notes.rst b/docs/source/node-upgrade-notes.rst index d6c0da05b4..8830eb9bef 100644 --- a/docs/source/node-upgrade-notes.rst +++ b/docs/source/node-upgrade-notes.rst @@ -50,7 +50,7 @@ for further information. Step 4. Replace ``corda.jar`` with the new version -------------------------------------------------- -Download the latest version of Corda from `our Artifactory site `_. +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. diff --git a/docs/source/testnet-explorer-corda.rst b/docs/source/testnet-explorer-corda.rst index e0fdbc2fd4..e62452253f 100644 --- a/docs/source/testnet-explorer-corda.rst +++ b/docs/source/testnet-explorer-corda.rst @@ -34,8 +34,8 @@ couple of resources. .. code-block:: 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://software.r3.com/artifactory/corda-releases/net/corda/corda-finance-contracts/|corda_version|/corda-finance-contracts-|corda_version|.jar + wget https://software.r3.com/artifactory/corda-releases/net/corda/corda-finance-workflows/|corda_version|/corda-finance-workflows-|corda_version|.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: @@ -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/corda-tools-explorer-|corda_version|-corda.jar + https://software.r3.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). From dfbf6120d26b70a1c92fb764f2fa8c0c4025ab90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Waldemar=20=C5=BBurowski?= <45210402+wzur-r3@users.noreply.github.com> Date: Tue, 11 Feb 2020 14:39:57 +0000 Subject: [PATCH 17/83] Updating Artifactory URLs for Corda OS (#5930) (#5939) (#5941) * Switching Artifactory URLs from obsolete ci-artifactory to software.r3.com --- build.gradle | 4 ++-- docker/src/bash/example-mini-network.sh | 6 +++--- docs/source/network-builder.rst | 4 ++-- docs/source/node-upgrade-notes.rst | 2 +- docs/source/testnet-explorer-corda.rst | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 40307aacdd..b3ed0c016d 100644 --- a/build.gradle +++ b/build.gradle @@ -110,7 +110,7 @@ buildscript { ext.artifactory_plugin_version = constants.getProperty('artifactoryPluginVersion') ext.hikari_version = '3.3.1' ext.liquibase_version = '3.6.3' - ext.artifactory_contextUrl = 'https://ci-artifactory.corda.r3cev.com/artifactory' + ext.artifactory_contextUrl = 'https://software.r3.com/artifactory' ext.snake_yaml_version = constants.getProperty('snakeYamlVersion') ext.docker_compose_rule_version = '0.35.0' ext.selenium_version = '3.141.59' @@ -157,7 +157,7 @@ buildscript { url 'https://kotlin.bintray.com/kotlinx' } maven { - url "https://ci-artifactory.corda.r3cev.com/artifactory/corda-dependencies-dev" + url "$artifactory_contextUrl/corda-dependencies-dev" } maven { url "$artifactory_contextUrl/corda-releases" diff --git a/docker/src/bash/example-mini-network.sh b/docker/src/bash/example-mini-network.sh index dff18f71e4..4d1630809b 100755 --- a/docker/src/bash/example-mini-network.sh +++ b/docker/src/bash/example-mini-network.sh @@ -7,9 +7,9 @@ DOCKER_IMAGE_VERSION="corda-zulu-4.5-snapshot" mkdir cordapps rm -f cordapps/* -wget -O cordapps/finance-contracts.jar https://ci-artifactory.corda.r3cev.com/artifactory/list/corda-dev/net/corda/corda-finance-contracts/${CORDAPP_VERSION}/corda-finance-contracts-${CORDAPP_VERSION}.jar -wget -O cordapps/finance-workflows.jar https://ci-artifactory.corda.r3cev.com/artifactory/list/corda-dev/net/corda/corda-finance-workflows/${CORDAPP_VERSION}/corda-finance-workflows-${CORDAPP_VERSION}.jar -wget -O cordapps/confidential-identities.jar https://ci-artifactory.corda.r3cev.com/artifactory/list/corda-dev/net/corda/corda-confidential-identities/${CORDAPP_VERSION}/corda-confidential-identities-${CORDAPP_VERSION}.jar +wget -O cordapps/finance-contracts.jar https://software.r3.com/artifactory/list/corda-dev/net/corda/corda-finance-contracts/${CORDAPP_VERSION}/corda-finance-contracts-${CORDAPP_VERSION}.jar +wget -O cordapps/finance-workflows.jar https://software.r3.com/artifactory/list/corda-dev/net/corda/corda-finance-workflows/${CORDAPP_VERSION}/corda-finance-workflows-${CORDAPP_VERSION}.jar +wget -O cordapps/confidential-identities.jar https://software.r3.com/artifactory/list/corda-dev/net/corda/corda-confidential-identities/${CORDAPP_VERSION}/corda-confidential-identities-${CORDAPP_VERSION}.jar rm keystore diff --git a/docs/source/network-builder.rst b/docs/source/network-builder.rst index 1629920948..168888ee21 100644 --- a/docs/source/network-builder.rst +++ b/docs/source/network-builder.rst @@ -16,7 +16,7 @@ Unlike the official image, a `node.conf` file and CorDapps are embedded into the 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: @@ -195,4 +195,4 @@ node has been started correctly, run the following in the previously connected S Shutting down the nodes ----------------------- -Run ``docker kill $(docker ps -q)`` to kill all running Docker processes. \ No newline at end of file +Run ``docker kill $(docker ps -q)`` to kill all running Docker processes. diff --git a/docs/source/node-upgrade-notes.rst b/docs/source/node-upgrade-notes.rst index d6c0da05b4..8830eb9bef 100644 --- a/docs/source/node-upgrade-notes.rst +++ b/docs/source/node-upgrade-notes.rst @@ -50,7 +50,7 @@ for further information. Step 4. Replace ``corda.jar`` with the new version -------------------------------------------------- -Download the latest version of Corda from `our Artifactory site `_. +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. diff --git a/docs/source/testnet-explorer-corda.rst b/docs/source/testnet-explorer-corda.rst index e0fdbc2fd4..e62452253f 100644 --- a/docs/source/testnet-explorer-corda.rst +++ b/docs/source/testnet-explorer-corda.rst @@ -34,8 +34,8 @@ couple of resources. .. code-block:: 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://software.r3.com/artifactory/corda-releases/net/corda/corda-finance-contracts/|corda_version|/corda-finance-contracts-|corda_version|.jar + wget https://software.r3.com/artifactory/corda-releases/net/corda/corda-finance-workflows/|corda_version|/corda-finance-workflows-|corda_version|.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: @@ -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/corda-tools-explorer-|corda_version|-corda.jar + https://software.r3.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). From 42eca48a02934cb2a05eae2566a58ac5afa0545d Mon Sep 17 00:00:00 2001 From: Razvan Codreanu <52859362+Schife@users.noreply.github.com> Date: Wed, 12 Feb 2020 13:58:30 +0000 Subject: [PATCH 18/83] TM-197 Build stability changes (#5947) * TM-197 switching 4.3 to use local k8s instances and also make the maximum duration of builds 3 hours, fix 1 test and ignore 2 flaky ones * update to use local-k8s version of the plugin Co-authored-by: Stefano Franz --- .ci/dev/integration/Jenkinsfile | 7 +++++-- .ci/dev/nightly-regression/Jenkinsfile | 3 ++- .ci/dev/on-demand-tests/Jenkinsfile | 2 +- .ci/dev/regression/Jenkinsfile | 3 ++- .ci/dev/smoke/Jenkinsfile | 9 ++++++--- .ci/dev/unit/Jenkinsfile | 7 +++++-- Jenkinsfile | 7 +++++-- build.gradle | 16 ++++++++-------- .../coretests/crypto/X509NameConstraintsTest.kt | 2 ++ .../distributed/DistributedServiceTests.kt | 3 +++ 10 files changed, 39 insertions(+), 20 deletions(-) diff --git a/.ci/dev/integration/Jenkinsfile b/.ci/dev/integration/Jenkinsfile index de89aec99e..1e5d032e8d 100644 --- a/.ci/dev/integration/Jenkinsfile +++ b/.ci/dev/integration/Jenkinsfile @@ -5,8 +5,11 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) pipeline { - agent { label 'k8s' } - options { timestamps() } + agent { label 'local-k8s' } + options { + timestamps() + timeout(time: 3, unit: 'HOURS') + } environment { DOCKER_TAG_TO_USE = "${UUID.randomUUID().toString().toLowerCase().subSequence(0, 12)}" diff --git a/.ci/dev/nightly-regression/Jenkinsfile b/.ci/dev/nightly-regression/Jenkinsfile index db140bece0..de26a41c90 100644 --- a/.ci/dev/nightly-regression/Jenkinsfile +++ b/.ci/dev/nightly-regression/Jenkinsfile @@ -4,11 +4,12 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) pipeline { - agent { label 'k8s' } + agent { label 'local-k8s' } options { timestamps() overrideIndexTriggers(false) buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '7')) + timeout(time: 3, unit: 'HOURS') } triggers { pollSCM ignorePostCommitHooks: true, scmpoll_spec: '@midnight' diff --git a/.ci/dev/on-demand-tests/Jenkinsfile b/.ci/dev/on-demand-tests/Jenkinsfile index 25127ef133..f59d3d67d0 100644 --- a/.ci/dev/on-demand-tests/Jenkinsfile +++ b/.ci/dev/on-demand-tests/Jenkinsfile @@ -3,4 +3,4 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) -onDemandTestPipeline('k8s', '.ci/dev/on-demand-tests/commentMappings.yml') +onDemandTestPipeline('local-k8s', '.ci/dev/on-demand-tests/commentMappings.yml') diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index d08e37967c..ed550bd401 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -4,10 +4,11 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) pipeline { - agent { label 'gke' } + agent { label 'local-k8s' } options { timestamps() buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '7')) + timeout(time: 3, unit: 'HOURS') } environment { diff --git a/.ci/dev/smoke/Jenkinsfile b/.ci/dev/smoke/Jenkinsfile index f24c97a898..05aec41e59 100644 --- a/.ci/dev/smoke/Jenkinsfile +++ b/.ci/dev/smoke/Jenkinsfile @@ -4,9 +4,12 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) pipeline { - agent { label 'k8s' } - options { timestamps() - overrideIndexTriggers(false) } + agent { label 'local-k8s' } + options { + timestamps() + overrideIndexTriggers(false) + timeout(time: 3, unit: 'HOURS') + } triggers { issueCommentTrigger('.*smoke tests.*') diff --git a/.ci/dev/unit/Jenkinsfile b/.ci/dev/unit/Jenkinsfile index 736e4bf235..98f43b4428 100644 --- a/.ci/dev/unit/Jenkinsfile +++ b/.ci/dev/unit/Jenkinsfile @@ -5,8 +5,11 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) pipeline { - agent { label 'k8s' } - options { timestamps() } + agent { label 'local-k8s' } + options { + timestamps() + timeout(time: 3, unit: 'HOURS') + } environment { DOCKER_TAG_TO_USE = "${UUID.randomUUID().toString().toLowerCase().subSequence(0, 12)}" diff --git a/Jenkinsfile b/Jenkinsfile index 0c5b0f188f..b81f50ed61 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,8 +5,11 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) pipeline { - agent { label 'k8s' } - options { timestamps() } + agent { label 'local-k8s' } + options { + timestamps() + timeout(time: 3, unit: 'HOURS') + } environment { DOCKER_TAG_TO_USE = "${env.GIT_COMMIT.subSequence(0, 8)}" diff --git a/build.gradle b/build.gradle index 6e49f4f5f1..e1b3594f59 100644 --- a/build.gradle +++ b/build.gradle @@ -179,7 +179,7 @@ buildscript { // Capsule gradle plugin forked and maintained locally to support Gradle 5.x // See https://github.com/corda/gradle-capsule-plugin classpath "us.kirchmeier:gradle-capsule-plugin:1.0.4_r3" - classpath group: "com.r3.testing", name: "gradle-distributed-testing-plugin", version: "1.2-SNAPSHOT", changing: true + classpath group: "com.r3.testing", name: "gradle-distributed-testing-plugin", version: "1.2-LOCAL-K8S-SHARED-CACHE-SNAPSHOT", changing: true classpath "com.bmuschko:gradle-docker-plugin:5.0.0" } } @@ -620,7 +620,7 @@ task allParallelIntegrationTest(type: ParallelTestGroup) { testGroups "integrationTest" numberOfShards 10 streamOutput false - coresPerFork 5 + coresPerFork 2 memoryInGbPerFork 12 distribute DistributeTestsBy.METHOD nodeTaints "big" @@ -630,7 +630,7 @@ task allParallelUnitTest(type: ParallelTestGroup) { testGroups "test" numberOfShards 10 streamOutput false - coresPerFork 3 + coresPerFork 2 memoryInGbPerFork 12 distribute DistributeTestsBy.CLASS nodeTaints "small" @@ -639,25 +639,25 @@ task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) { testGroups "test", "integrationTest" numberOfShards 15 streamOutput false - coresPerFork 6 + coresPerFork 2 memoryInGbPerFork 10 distribute DistributeTestsBy.METHOD nodeTaints "big" } task parallelRegressionTest(type: ParallelTestGroup) { testGroups "test", "integrationTest", "slowIntegrationTest", "smokeTest" - numberOfShards 6 + numberOfShards 10 streamOutput false - coresPerFork 6 + coresPerFork 2 memoryInGbPerFork 10 distribute DistributeTestsBy.METHOD nodeTaints "big" } task allParallelSmokeTest(type: ParallelTestGroup) { testGroups "slowIntegrationTest", "smokeTest" - numberOfShards 4 + numberOfShards 10 streamOutput false - coresPerFork 6 + coresPerFork 2 memoryInGbPerFork 10 distribute DistributeTestsBy.CLASS nodeTaints "big" diff --git a/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt index cb285c5aff..c355ef8567 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt @@ -12,6 +12,7 @@ import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.jce.provider.BouncyCastleProvider import org.junit.Test +import java.security.Security import java.security.UnrecoverableKeyException import java.security.cert.CertPathValidator import java.security.cert.CertPathValidatorException @@ -94,6 +95,7 @@ class X509NameConstraintsTest { @Test fun `x500 name with correct cn and extra attribute`() { + Security.addProvider(BouncyCastleProvider()) val acceptableNames = listOf("CN=Bank A TLS, UID=", "O=Bank A") .map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray() diff --git a/node/src/integration-test-slow/kotlin/net/corda/node/services/distributed/DistributedServiceTests.kt b/node/src/integration-test-slow/kotlin/net/corda/node/services/distributed/DistributedServiceTests.kt index 82eaec0d59..c028d710ce 100644 --- a/node/src/integration-test-slow/kotlin/net/corda/node/services/distributed/DistributedServiceTests.kt +++ b/node/src/integration-test-slow/kotlin/net/corda/node/services/distributed/DistributedServiceTests.kt @@ -25,6 +25,7 @@ import net.corda.testing.node.internal.DummyClusterSpec import net.corda.testing.node.internal.FINANCE_CORDAPPS import net.corda.testing.node.internal.cordappWithPackages import org.assertj.core.api.Assertions.assertThat +import org.junit.Ignore import org.junit.Test import rx.Observable import java.util.* @@ -81,6 +82,7 @@ class DistributedServiceTests { } // TODO This should be in RaftNotaryServiceTests + @Ignore @Test fun `cluster survives if a notary is killed`() { setup { @@ -119,6 +121,7 @@ class DistributedServiceTests { // TODO Use a dummy distributed service rather than a Raft Notary Service as this test is only about Artemis' ability // to handle distributed services + @Ignore @Test fun `requests are distributed evenly amongst the nodes`() { setup { From 9c743897fd9c525838cfa35ee85d573c90148ab5 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 12 Feb 2020 17:15:46 +0000 Subject: [PATCH 19/83] CORDA-3590: Optimise classloading for DJVM deserialization. (#5944) --- .../djvm/DelegatingClassLoader.kt | 2 +- .../djvm/DeserializeDurationTest.kt | 39 +++++++++++++++++++ .../djvm/DeserializeOffsetTimeTest.kt | 2 +- 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/DelegatingClassLoader.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/DelegatingClassLoader.kt index 9be12a86b2..ce916bd2ff 100644 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/DelegatingClassLoader.kt +++ b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/DelegatingClassLoader.kt @@ -5,6 +5,6 @@ import net.corda.djvm.rewiring.SandboxClassLoader class DelegatingClassLoader(private val delegate: SandboxClassLoader) : ClassLoader(null) { @Throws(ClassNotFoundException::class) override fun loadClass(name: String, resolve: Boolean): Class<*> { - return delegate.loadForSandbox(name).type + return delegate.toSandboxClass(name) } } diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt new file mode 100644 index 0000000000..7a2c6a4db8 --- /dev/null +++ b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt @@ -0,0 +1,39 @@ +package net.corda.serialization.djvm + +import net.corda.core.serialization.internal._contextSerializationEnv +import net.corda.core.serialization.serialize +import net.corda.serialization.djvm.SandboxType.KOTLIN +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.fail +import java.time.Duration +import java.util.function.Function + +@ExtendWith(LocalSerialization::class) +class DeserializeDurationTest : TestBase(KOTLIN) { + @Test + fun `test deserializing duration`() { + val duration = Duration.ofSeconds(12345, 6789) + val data = duration.serialize() + + sandbox { + _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) + + val sandboxDuration = data.deserializeFor(classLoader) + + val taskFactory = classLoader.createRawTaskFactory() + val showDuration = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowDuration::class.java) + val result = showDuration.apply(sandboxDuration) ?: fail("Result cannot be null") + + assertEquals(duration.toString(), result.toString()) + assertEquals(SANDBOX_STRING, result::class.java.name) + } + } + + class ShowDuration : Function { + override fun apply(duration: Duration): String { + return duration.toString() + } + } +} diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt index 5d12059e0e..c23fe08ebe 100644 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt +++ b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt @@ -13,7 +13,7 @@ import java.util.function.Function @ExtendWith(LocalSerialization::class) class DeserializeOffsetTimeTest : TestBase(KOTLIN) { @Test - fun `test deserializing instant`() { + fun `test deserializing offset time`() { val time = OffsetTime.now() val data = time.serialize() From 1c80ad8be22ed552576d37eb4e104554957acb54 Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Thu, 13 Feb 2020 13:11:39 +0000 Subject: [PATCH 20/83] CORDA-3576: Use the connectionMaxRetryInterval configuration when reconnection the RPC client (#5957) --- .../rpc/internal/ReconnectingCordaRPCOps.kt | 8 ++++-- .../net/corda/core/internal/InternalUtils.kt | 25 ++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) 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 index 9b4a76b579..158788ee81 100644 --- 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 @@ -17,6 +17,7 @@ import net.corda.client.rpc.internal.ReconnectingCordaRPCOps.ReconnectingRPCConn import net.corda.client.rpc.reconnect.CouldNotStartFlowException import net.corda.core.flows.StateMachineRunId import net.corda.core.internal.messaging.InternalCordaRPCOps +import net.corda.core.internal.min import net.corda.core.internal.times import net.corda.core.internal.uncheckedCast import net.corda.core.messaging.ClientRpcSslOptions @@ -262,9 +263,12 @@ class ReconnectingCordaRPCOps private constructor( } // Could not connect this time round - pause before giving another try. Thread.sleep(retryInterval.toMillis()) - // TODO - make the exponential retry factor configurable. val nextRoundRobinIndex = (roundRobinIndex + 1) % nodeHostAndPorts.size - val nextInterval = retryInterval * rpcConfiguration.connectionRetryIntervalMultiplier + val nextInterval = min( + rpcConfiguration.connectionMaxRetryInterval, + retryInterval * rpcConfiguration.connectionRetryIntervalMultiplier + ) + log.info("Could not establish connection. Next retry interval $nextInterval") return establishConnectionWithRetry(nextInterval, nextRoundRobinIndex, remainingRetries) } override val proxy: CordaRPCOps diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index 77e27e2439..fcfd9a5986 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -4,7 +4,12 @@ package net.corda.core.internal import net.corda.core.DeleteForDJVM import net.corda.core.KeepForDJVM -import net.corda.core.crypto.* +import net.corda.core.crypto.Crypto +import net.corda.core.crypto.DigitalSignature +import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.SignedData +import net.corda.core.crypto.sha256 +import net.corda.core.crypto.sign import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.deserialize @@ -40,11 +45,23 @@ import java.security.KeyPair import java.security.MessageDigest import java.security.PrivateKey import java.security.PublicKey -import java.security.cert.* +import java.security.cert.CertPath +import java.security.cert.CertPathValidator +import java.security.cert.CertPathValidatorException +import java.security.cert.PKIXCertPathValidatorResult +import java.security.cert.PKIXParameters +import java.security.cert.TrustAnchor +import java.security.cert.X509Certificate import java.time.Duration import java.time.temporal.Temporal import java.util.* -import java.util.Spliterator.* +import java.util.Spliterator.DISTINCT +import java.util.Spliterator.IMMUTABLE +import java.util.Spliterator.NONNULL +import java.util.Spliterator.ORDERED +import java.util.Spliterator.SIZED +import java.util.Spliterator.SORTED +import java.util.Spliterator.SUBSIZED import java.util.concurrent.ExecutorService import java.util.concurrent.TimeUnit import java.util.stream.Collectors @@ -78,6 +95,8 @@ infix fun Temporal.until(endExclusive: Temporal): Duration = Duration.between(th operator fun Duration.div(divider: Long): Duration = dividedBy(divider) operator fun Duration.times(multiplicand: Long): Duration = multipliedBy(multiplicand) operator fun Duration.times(multiplicand: Double): Duration = Duration.ofNanos((toNanos() * multiplicand).roundToLong()) +fun min(d1: Duration, d2: Duration): Duration = if (d1 <= d2) d1 else d2 + /** * Returns the single element matching the given [predicate], or `null` if the collection is empty, or throws exception From 16b28527167287807f44b93d4ae5c425bde4a8a2 Mon Sep 17 00:00:00 2001 From: Christian Sailer Date: Thu, 13 Feb 2020 13:26:15 +0000 Subject: [PATCH 21/83] Remove seemingly unused config parsing classes (#5917) --- .../VersionedSpecificationRegistry.kt | 30 ---- .../versioned/VersionedParsingExampleTest.kt | 131 ------------------ 2 files changed, 161 deletions(-) delete mode 100644 common/configuration-parsing/src/main/kotlin/net/corda/common/configuration/parsing/internal/versioned/VersionedSpecificationRegistry.kt delete mode 100644 common/configuration-parsing/src/test/kotlin/net/corda/common/configuration/parsing/internal/versioned/VersionedParsingExampleTest.kt diff --git a/common/configuration-parsing/src/main/kotlin/net/corda/common/configuration/parsing/internal/versioned/VersionedSpecificationRegistry.kt b/common/configuration-parsing/src/main/kotlin/net/corda/common/configuration/parsing/internal/versioned/VersionedSpecificationRegistry.kt deleted file mode 100644 index 11cff499d8..0000000000 --- a/common/configuration-parsing/src/main/kotlin/net/corda/common/configuration/parsing/internal/versioned/VersionedSpecificationRegistry.kt +++ /dev/null @@ -1,30 +0,0 @@ -package net.corda.common.configuration.parsing.internal.versioned - -import com.typesafe.config.Config -import net.corda.common.configuration.parsing.internal.Configuration -import net.corda.common.configuration.parsing.internal.Valid -import net.corda.common.configuration.parsing.internal.valid -import net.corda.common.validation.internal.Validated.Companion.invalid - -class VersionedSpecificationRegistry private constructor(private val versionFromConfig: (Config) -> Valid, private val specifications: Map>) : (Config) -> Valid> { - - companion object { - - fun mapping(versionParser: Configuration.Value.Parser, specifications: Map>) = VersionedSpecificationRegistry({ config -> versionParser.parse(config) }, specifications) - - fun mapping(versionParser: Configuration.Value.Parser, vararg specifications: Pair>) = VersionedSpecificationRegistry({ config -> versionParser.parse(config) }, specifications.toMap()) - - fun mapping(versionParser: (Config) -> Valid, specifications: Map>) = VersionedSpecificationRegistry(versionParser, specifications) - - fun mapping(versionParser: (Config) -> Valid, vararg specifications: Pair>) = VersionedSpecificationRegistry(versionParser, specifications.toMap()) - } - - override fun invoke(configuration: Config): Valid> { - - return versionFromConfig.invoke(configuration).mapValid { version -> - - val value = specifications[version] - value?.let { valid(it) } ?: invalid, Configuration.Validation.Error>(Configuration.Validation.Error.UnsupportedVersion.of(version)) - } - } -} \ No newline at end of file diff --git a/common/configuration-parsing/src/test/kotlin/net/corda/common/configuration/parsing/internal/versioned/VersionedParsingExampleTest.kt b/common/configuration-parsing/src/test/kotlin/net/corda/common/configuration/parsing/internal/versioned/VersionedParsingExampleTest.kt deleted file mode 100644 index 59c6889b3c..0000000000 --- a/common/configuration-parsing/src/test/kotlin/net/corda/common/configuration/parsing/internal/versioned/VersionedParsingExampleTest.kt +++ /dev/null @@ -1,131 +0,0 @@ -package net.corda.common.configuration.parsing.internal.versioned - -import com.typesafe.config.Config -import net.corda.common.configuration.parsing.internal.* -import net.corda.common.validation.internal.Validated -import net.corda.common.validation.internal.Validated.Companion.invalid -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test - -class VersionedParsingExampleTest { - - @Test(timeout=300_000) - fun correct_parsing_function_is_used_for_present_version() { - - val versionParser = Configuration.Version.Extractor.fromPath("configuration.metadata.version") - val extractVersion: (Config) -> Valid = { config -> versionParser.parseRequired(config) } - val parseConfiguration = VersionedSpecificationRegistry.mapping(extractVersion, 1 to RpcSettingsSpec.V1, 2 to RpcSettingsSpec.V2) - - val principalAddressValue = Address("localhost", 8080) - val adminAddressValue = Address("127.0.0.1", 8081) - - val configurationV1 = configObject("configuration.metadata.version" to 1, "principalHost" to principalAddressValue.host, "principalPort" to principalAddressValue.port, "adminHost" to adminAddressValue.host, "adminPort" to adminAddressValue.port).toConfig().also { println(it.serialize()) } - val rpcSettingsFromVersion1Conf = parseConfiguration.invoke(configurationV1).mapValid { it.parse(configurationV1) } - - assertResult(rpcSettingsFromVersion1Conf, principalAddressValue, adminAddressValue) - - val addressesValue = configObject("principal" to "${principalAddressValue.host}:${principalAddressValue.port}", "admin" to "${adminAddressValue.host}:${adminAddressValue.port}") - val configurationV2 = configObject("configuration.metadata.version" to 2, "configuration.value.addresses" to addressesValue).toConfig().also { println(it.serialize()) } - val rpcSettingsFromVersion2Conf = parseConfiguration.invoke(configurationV2).mapValid { it.parse(configurationV2) } - - assertResult(rpcSettingsFromVersion2Conf, principalAddressValue, adminAddressValue) - } - - @Test(timeout=300_000) - fun default_value_is_used_for_absent_version() { - - val defaultVersion = 2 - val versionParser = Configuration.Version.Extractor.fromPath("configuration.metadata.version", defaultVersion) - val extractVersion: (Config) -> Valid = { config -> versionParser.parseRequired(config) } - val parseConfiguration = VersionedSpecificationRegistry.mapping(extractVersion, 1 to RpcSettingsSpec.V1, 2 to RpcSettingsSpec.V2) - - val principalAddressValue = Address("localhost", 8080) - val adminAddressValue = Address("127.0.0.1", 8081) - - val addressesValue = configObject("principal" to "${principalAddressValue.host}:${principalAddressValue.port}", "admin" to "${adminAddressValue.host}:${adminAddressValue.port}") - val configurationV2 = configObject("configuration.value.addresses" to addressesValue).toConfig().also { println(it.serialize()) } - val rpcSettingsFromVersion2Conf = parseConfiguration.invoke(configurationV2).mapValid { it.parse(configurationV2) } - - assertResult(rpcSettingsFromVersion2Conf, principalAddressValue, adminAddressValue) - } - - private fun assertResult(result: Valid, principalAddressValue: Address, adminAddressValue: Address) { - - assertThat(result.isValid).isTrue() - assertThat(result.value()).satisfies { value -> - - assertThat(value.principal).isEqualTo(principalAddressValue) - assertThat(value.admin).isEqualTo(adminAddressValue) - } - } - - private data class RpcSettings(val principal: Address, val admin: Address) - - private object RpcSettingsSpec { - - private fun addressFor(host: String, port: Int): Valid
{ - - return try { - require(host.isNotBlank()) - require(port > 0) - Validated.valid(Address(host, port)) - } catch (e: Exception) { - return Validated.invalid(Configuration.Validation.Error.BadValue.of(host, Address::class.java.simpleName, "Value must be of format \"host(String):port(Int > 0)\" e.g., \"127.0.0.1:8080\"")) - } - } - - object V1 : Configuration.Specification("RpcSettings") { - - private val principalHost by string() - private val principalPort by int() - - private val adminHost by string() - private val adminPort by int() - - override fun parseValid(configuration: Config): Valid { - - val principalHost = configuration[principalHost] - val principalPort = configuration[principalPort] - - val adminHost = configuration[adminHost] - val adminPort = configuration[adminPort] - - val principalAddress = addressFor(principalHost, principalPort) - val adminAddress = addressFor(adminHost, adminPort) - - return if (principalAddress.isValid && adminAddress.isValid) { - return valid(RpcSettings(principalAddress.value(), adminAddress.value())) - } else { - invalid(principalAddress.errors + adminAddress.errors) - } - } - } - - object V2 : Configuration.Specification("RpcSettings", prefix = "configuration.value") { - - private object AddressesSpec : Configuration.Specification("Addresses") { - - val principal by string().mapValid(::parseAddress) - - val admin by string().mapValid(::parseAddress) - - override fun parseValid(configuration: Config) = valid(Addresses(configuration[principal],configuration[admin])) - - private fun parseAddress(rawValue: String): Valid
{ - - return Address.validFromRawValue(rawValue) { error -> Configuration.Validation.Error.BadValue.of(error) } - } - } - - private val addresses by nested(AddressesSpec) - - override fun parseValid(configuration: Config): Valid { - - val addresses = configuration[addresses] - return valid(RpcSettings(addresses.principal, addresses.admin)) - } - } - } -} - -private fun Configuration.Version.Extractor.parseRequired(config: Config, options: Configuration.Validation.Options = Configuration.Validation.Options.defaults) = parse(config, options).map { it } \ No newline at end of file From 84be73837426247c193d680387f06d26ee6e5ec1 Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Thu, 13 Feb 2020 14:08:56 +0000 Subject: [PATCH 22/83] CORDA-3752: rethrow non-rpc specific errors rather than try to reconnect to the server (#5922) --- client/rpc/build.gradle | 3 ++ .../corda/client/rpc/CordaRPCClientTest.kt | 51 ++++++++++++++++-- .../resources/net/corda/client/rpc/test.jar | Bin 0 -> 161 bytes .../rpc/internal/ReconnectingCordaRPCOps.kt | 7 +-- 4 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 client/rpc/src/integration-test/resources/net/corda/client/rpc/test.jar diff --git a/client/rpc/build.gradle b/client/rpc/build.gradle index 2e53526dba..ee557d933b 100644 --- a/client/rpc/build.gradle +++ b/client/rpc/build.gradle @@ -35,6 +35,9 @@ sourceSets { runtimeClasspath += main.output + test.output srcDir file('src/integration-test/java') } + resources { + srcDirs "src/integration-test/resources" + } } smokeTest { kotlin { diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 8777d76303..60271d97a3 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -1,13 +1,22 @@ package net.corda.client.rpc -import net.corda.core.context.* +import net.corda.core.CordaRuntimeException +import net.corda.core.context.Actor +import net.corda.core.context.AuthServiceId +import net.corda.core.context.InvocationContext +import net.corda.core.context.InvocationOrigin +import net.corda.core.context.Trace import net.corda.core.contracts.FungibleAsset import net.corda.core.crypto.random63BitValue import net.corda.core.identity.Party import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.location import net.corda.core.internal.toPath -import net.corda.core.messaging.* +import net.corda.core.messaging.FlowProgressHandle +import net.corda.core.messaging.StateMachineInfo +import net.corda.core.messaging.StateMachineUpdate +import net.corda.core.messaging.startFlow +import net.corda.core.messaging.startTrackedFlow import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.contextLogger @@ -22,8 +31,14 @@ import net.corda.finance.workflows.getCashBalance import net.corda.finance.workflows.getCashBalances import net.corda.node.internal.NodeWithInfo import net.corda.node.services.Permissions.Companion.all +import net.corda.nodeapi.exceptions.DuplicateAttachmentException import net.corda.testing.common.internal.checkNotOnClasspath -import net.corda.testing.core.* +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.expect +import net.corda.testing.core.expectEvents +import net.corda.testing.core.sequence import net.corda.testing.node.User import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.internal.ProcessUtilities @@ -31,6 +46,7 @@ import net.corda.testing.node.internal.poll import org.apache.activemq.artemis.api.core.ActiveMQSecurityException import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After import org.junit.Before import org.junit.Test @@ -38,7 +54,10 @@ import rx.subjects.PublishSubject import java.net.URLClassLoader import java.nio.file.Paths import java.util.* -import java.util.concurrent.* +import java.util.concurrent.CountDownLatch +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -47,6 +66,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance"), notaries = companion object { val rpcUser = User("user1", "test", permissions = setOf(all())) val log = contextLogger() + const val testJar = "net/corda/client/rpc/test.jar" } private lateinit var node: NodeWithInfo @@ -244,6 +264,29 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance"), notaries = assertThat(outOfProcessRpc.waitFor()).isZero() // i.e. no exceptions were thrown } + @Test + fun `nonspecific reconnect errors dont trigger graceful reconnect`() { + val inputJar1 = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar)!! + val inputJar2 = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar)!! + var disconnects = 0 + var reconnects = 0 + val gracefulReconnect = GracefulReconnect(onDisconnect = {++disconnects}, onReconnect = {++reconnects}) + + // This just recreates the original issue which allowed us to fix this. Any non-rpc exception would do + // https://r3-cev.atlassian.net/browse/CORDA-3572 + assertThatThrownBy { + client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect).use { + val rpc = it.proxy + rpc.uploadAttachment(inputJar1) + rpc.uploadAttachment(inputJar2) + } + }.isInstanceOf(CordaRuntimeException::class.java) + .hasMessageContaining(DuplicateAttachmentException::class.java.name) + + assertThat(disconnects).isEqualTo(0) + assertThat(reconnects).isEqualTo(0) + } + private fun checkShellNotification(info: StateMachineInfo) { val context = info.invocationContext assertThat(context.origin).isInstanceOf(InvocationOrigin.Shell::class.java) diff --git a/client/rpc/src/integration-test/resources/net/corda/client/rpc/test.jar b/client/rpc/src/integration-test/resources/net/corda/client/rpc/test.jar new file mode 100644 index 0000000000000000000000000000000000000000..9ce0b7d2d27e81efebcd7706b39b8ceaf4a0e813 GIT binary patch literal 161 zcmWIWW@h1H00Fni0I#r(;$GZ9HVAV7aY<@%iC#%XiAQQqPQF5Teo;<|7#SoOqRXspT{|u@*4=$%flLQ@v$BD-F#=%}kTwLH0RVFl9+v?, maxNumberOfAttempts: Int): Any? { checkIfClosed() var remainingAttempts = maxNumberOfAttempts @@ -338,9 +340,8 @@ class ReconnectingCordaRPCOps private constructor( throw RPCException("User does not have permission to perform operation ${method.name}.", e) } else -> { - log.warn("Failed to perform operation ${method.name}. Unknown error. Retrying....", e) - reconnectingRPCConnection.reconnectOnError(e) - checkIfIsStartFlow(method, e) + log.warn("Failed to perform operation ${method.name}.", e) + throw e.targetException } } lastException = e.targetException From d419faa18598062eb116be3bde5c9f01bed83dd2 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 13 Feb 2020 13:01:21 +0000 Subject: [PATCH 23/83] ENT-4652: Tighten Groovy code for writing Cordapp-Dependencies. --- samples/simm-valuation-demo/contracts-states/build.gradle | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/samples/simm-valuation-demo/contracts-states/build.gradle b/samples/simm-valuation-demo/contracts-states/build.gradle index 4d2b36cae2..3bb992cf4b 100644 --- a/samples/simm-valuation-demo/contracts-states/build.gradle +++ b/samples/simm-valuation-demo/contracts-states/build.gradle @@ -63,10 +63,13 @@ dependencies { def cordappDependencies = file("${sourceSets['main'].output.resourcesDir}/META-INF/Cordapp-Dependencies") task generateDependencies { dependsOn project(':finance:contracts').tasks.jar + inputs.files(configurations.cordapp) outputs.files(cordappDependencies) doLast { - configurations.cordapp.forEach { cordapp -> - cordappDependencies << sha256(cordapp) << System.lineSeparator() + cordappDependencies.newWriter().withWriter { writer -> + configurations.cordapp.forEach { cordapp -> + writer << sha256(cordapp) << System.lineSeparator() + } } } } From 70e8e69a873d684bb1010de3b3f5caabf141d486 Mon Sep 17 00:00:00 2001 From: Dimos Raptis Date: Fri, 14 Feb 2020 09:58:32 +0000 Subject: [PATCH 24/83] NOTICK - Add tests for handling of user errors in reconnecting observables (#5932) * Add tests for handling of user errors in reconnecting observables * detekt --- .../CordaRPCClientReconnectionTest.kt | 42 +++++++++++++++++++ .../rpc/internal/ReconnectingObservable.kt | 29 +++++++++---- detekt-baseline.xml | 1 + 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpcreconnect/CordaRPCClientReconnectionTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpcreconnect/CordaRPCClientReconnectionTest.kt index 18e4f878cb..031b990b0a 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpcreconnect/CordaRPCClientReconnectionTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpcreconnect/CordaRPCClientReconnectionTest.kt @@ -26,6 +26,7 @@ import net.corda.testing.node.internal.rpcDriver import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test +import java.lang.RuntimeException import java.lang.Thread.sleep import java.time.Duration import java.util.concurrent.CountDownLatch @@ -163,6 +164,47 @@ class CordaRPCClientReconnectionTest { } } + @Test(timeout=300_000) + fun `when user code throws an error on a reconnecting observable, then onError is invoked and observable is unsubscribed successfully`() { + driver(DriverParameters(cordappsForAllNodes = FINANCE_CORDAPPS)) { + val normalLatch = CountDownLatch(1) + val errorLatch = CountDownLatch(1) + var observedEvents = 0 + + fun startNode(address: NetworkHostAndPort): NodeHandle { + return startNode( + providedName = CHARLIE_NAME, + rpcUsers = listOf(CordaRPCClientTest.rpcUser), + customOverrides = mapOf("rpcSettings.address" to address.toString()) + ).getOrThrow() + } + + val addresses = listOf(NetworkHostAndPort("localhost", portAllocator.nextPort()), NetworkHostAndPort("localhost", portAllocator.nextPort())) + + startNode(addresses[0]) + val client = CordaRPCClient(addresses) + + (client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use { + val rpcOps = it.proxy as ReconnectingCordaRPCOps + val cashStatesFeed = rpcOps.vaultTrack(Cash.State::class.java) + val subscription = cashStatesFeed.updates.subscribe ({ + normalLatch.countDown() + observedEvents++ + throw RuntimeException() + }, { + errorLatch.countDown() + }) + rpcOps.startTrackedFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue.get() + rpcOps.startTrackedFlow(::CashIssueFlow, 10.DOLLARS, OpaqueBytes.of(0), defaultNotaryIdentity).returnValue.get() + + assertTrue { normalLatch.await(2, TimeUnit.SECONDS) } + assertTrue { errorLatch.await(2, TimeUnit.SECONDS) } + assertThat(subscription.isUnsubscribed).isTrue() + assertThat(observedEvents).isEqualTo(1) + } + } + } + @Test(timeout=300_000) fun `an RPC call fails, when the maximum number of attempts is exceeded`() { driver(DriverParameters(cordappsForAllNodes = emptyList())) { diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/ReconnectingObservable.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/ReconnectingObservable.kt index b5052d9bbf..fe3c03f453 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/ReconnectingObservable.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/ReconnectingObservable.kt @@ -1,5 +1,6 @@ package net.corda.client.rpc.internal +import net.corda.client.rpc.ConnectionFailureException import net.corda.core.messaging.DataFeed import rx.Observable import rx.Subscriber @@ -54,16 +55,30 @@ class ReconnectingObservable private constructor(subscriber: ReconnectingSubs } } + /** + * Depending on the type of error, the reaction is different: + * - If the error is coming from a connection disruption, we establish a new connection and re-wire the observable + * without letting the client notice at all. + * - In any other case, we let the error propagate to the client's observable. Both of the observables + * (this one and the client's one) will be automatically unsubscribed, since that's the semantics of onError. + */ private fun scheduleResubscribe(error: Throwable) { if (unsubscribed) return - reconnectingRPCConnection.observersPool.execute { - if (unsubscribed || reconnectingRPCConnection.isClosed()) return@execute - reconnectingRPCConnection.reconnectOnError(error) - // It can take a while to reconnect so we might find that we've shutdown in in the meantime - if (unsubscribed || reconnectingRPCConnection.isClosed()) return@execute - val newDataFeed = createDataFeed() - subscribeImmediately(newDataFeed) + + if (error is ConnectionFailureException) { + reconnectingRPCConnection.observersPool.execute { + if (unsubscribed || reconnectingRPCConnection.isClosed()) return@execute + reconnectingRPCConnection.reconnectOnError(error) + // It can take a while to reconnect so we might find that we've shutdown in in the meantime + if (unsubscribed || reconnectingRPCConnection.isClosed()) return@execute + val newDataFeed = createDataFeed() + subscribeImmediately(newDataFeed) + } + } else { + val subscriber = checkNotNull(this.subscriber.get()) + subscriber.onError(error) } + } } } \ No newline at end of file diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 2c6cf777c8..39017a0c2b 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -1656,6 +1656,7 @@ TooGenericExceptionThrown:ClassLoadingUtilsTest.kt$ClassLoadingUtilsTest$throw RuntimeException() TooGenericExceptionThrown:CommandParsers.kt$AzureParser.RegionConverter$throw Error("Unknown azure region: $value") TooGenericExceptionThrown:ContractHierarchyTest.kt$ContractHierarchyTest.IndirectContractParent$throw RuntimeException("Boom!") + TooGenericExceptionThrown:CordaRPCClientReconnectionTest.kt$CordaRPCClientReconnectionTest$throw RuntimeException() TooGenericExceptionThrown:CrossCashTest.kt$throw Exception( "Generated exit of ${request.amount} from $issuer, however there is no cash to exit!" ) TooGenericExceptionThrown:CrossCashTest.kt$throw Exception( "Generated payment of ${request.amount} from $issuer, " + "however they only have $issuerQuantity!" ) TooGenericExceptionThrown:CrossCashTest.kt$throw Exception( "Generated payment of ${request.amount} from ${node.mainIdentity}, " + "however there is no cash from $issuer!" ) From da192bcf0d992e8ac64dfc8a2131a2f49b064a20 Mon Sep 17 00:00:00 2001 From: James Higgs <45565019+JamesHR3@users.noreply.github.com> Date: Fri, 14 Feb 2020 11:18:21 +0000 Subject: [PATCH 25/83] [EG-140] Allow system property paths with multiple keys to be specified in node.conf (#5963) * [EG-140] Allow system property paths with multiple keys to be specified in node.conf * [EG-140] Split property paths to remove quotes * [EG-140] Quote system properties in docs * [EG-140] Rename path to key --- docs/source/corda-configuration-file.rst | 2 +- node/capsule/src/main/java/CordaCaplet.java | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index bfe4c781ce..a8f80f2834 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -643,7 +643,7 @@ sshd systemProperties An optional map of additional system properties to be set when launching via ``corda.jar`` only. - Keys and values of the map should be strings. e.g. ``systemProperties = { visualvm.display.name = FooBar }`` + Keys and values of the map should be strings. e.g. ``systemProperties = { "visualvm.display.name" = FooBar }`` *Default:* not defined diff --git a/node/capsule/src/main/java/CordaCaplet.java b/node/capsule/src/main/java/CordaCaplet.java index b366b3aef9..00264b99fe 100644 --- a/node/capsule/src/main/java/CordaCaplet.java +++ b/node/capsule/src/main/java/CordaCaplet.java @@ -17,6 +17,7 @@ import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.stream.Stream; +import static com.typesafe.config.ConfigUtil.splitPath; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.util.stream.Collectors.toMap; @@ -233,7 +234,6 @@ public class CordaCaplet extends Capsule { try { Map overrideSystemProps = nodeConfig.getConfig("systemProperties").entrySet().stream() .map(Property::create) - .filter(Property::isValid) .collect(toMap(Property::getKey, Property::getValue)); log(LOG_VERBOSE, "Configured system properties = " + overrideSystemProps); for (Map.Entry entry : overrideSystemProps.entrySet()) { @@ -321,20 +321,16 @@ public class CordaCaplet extends Capsule { * Helper class so that we can parse the "systemProperties" element of node.conf. */ private static class Property { - private final List path; + private final String key; private final Object value; - Property(List path, Object value) { - this.path = path; + Property(String key, Object value) { + this.key = key; this.value = value; } - boolean isValid() { - return path.size() == 1; - } - String getKey() { - return path.get(0); + return key; } Object getValue() { @@ -342,7 +338,12 @@ public class CordaCaplet extends Capsule { } static Property create(Map.Entry entry) { - return new Property(ConfigUtil.splitPath(entry.getKey()), entry.getValue().unwrapped()); + // String.join is preferred here over Typesafe's joinPath method, as the joinPath method would put quotes around the system + // property key which is undesirable here. + return new Property( + String.join(".", splitPath(entry.getKey())), + entry.getValue().unwrapped() + ); } } } From 27bd9f561b466d20206c8ccf6216e51974648d29 Mon Sep 17 00:00:00 2001 From: Valerio Campanella Date: Fri, 14 Feb 2020 13:50:37 +0000 Subject: [PATCH 26/83] Corda 4.4 release notes --- docs/source/release-notes.rst | 63 ++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 8c427ed816..c13838693c 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -4,7 +4,68 @@ Release notes .. contents:: :depth: 2 -Welcome to the Corda 4.3 release notes. Please read these carefully to understand what’s new in this release and how the features can help you. Just as prior releases have brought with them commitments to wire and API stability, Corda 4.3 comes with those same guarantees. States and apps valid in Corda 3.0 are transparently usable in Corda 4.3. +Welcome to the Corda 4.4 release notes. Please read these carefully to understand what’s new in this release and how the features can help you. Just as prior releases have brought with them commitments to wire and API stability, Corda 4.4 comes with those same guarantees. States and apps valid in Corda 3.0 are transparently usable in Corda 4.4. + +.. _release_notes_v4_4: + +Corda 4.4 +========= + +Corda 4.4 lays the foundation of a new open-core approach for the Corda codebase. This involved a refactoring of the main functional components of Corda. Please consult :doc:`cordapp-overview.rst` to get an overview of the practical impact on CorDapp development. + +Furthermore, Corda 4.4 introduces improvements to the flow framework API, a new diagnostic ``ServiceHub`` call and includes a number of security enhancements. + +Changes for developers in Corda 4.4 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Flows API improvements ++++++++++++++++++++++++ + +Corda 4.4 introduces a new ``FlowLogic.await`` API that allows a CorDapp developers to suspend their flow when executing user-defined long-running operations (e.g. call-outs to external services, long-running DB operations). This prevents these long-running operations from blocking the flow thread, allowing other flows to progress in the interim. Previously, these operations had to be executed synchronously, blocking the flow thread. + +The CorDapp developer can decide whether to run these asynchronous flow operations on a dedicated node pool, or to handle the threading themselves directly. + +Note that as before, the flow framework suspends automatically for certain operations (e.g. when waiting to receive a message from a counterparty). These suspensions do not have to be triggered explicitly. + +The node operator can configure the number of threads to dedicate to external operations. + +Corda 4.4 also introduces a new ``HospitalizeFlowException`` exception type that, when thrown, causes a flow to halt execution and send itself to the flow hospital for observation. The flow will automatically be retried on the next node start. + +This exception gives user code a way to retry a flow from its last checkpoint if a known intermittent failure occurred. + + +Diagnostic API ++++++++++++++++++++++++ + +Corda 4.4 introduces a ``ServiceHub`` call available to CorDapp developers that allows them to access: + +* The edition of Corda being run (e.g. Open Source, Enterprise) +* The version of Corda being run including the patch number (eg. 3.2.20190215) + + +Security enhancements ++++++++++++++++++++++++ + +* The SSH server in the :doc:`shell` has been updated to remove outdated weak ciphers and algorithms. +* The ability to SSH into the standalone shell has been removed +* A new read-only RPC user role template has been documented in :doc:`shell` + + +Platform version change +~~~~~~~~~~~~~~~~~~~~~~~ + +Given the addition of new APIs, the platform version of Corda 4.4 has been bumped up from 5 to 6. This is to prevent CorDapps that use it being deployed onto nodes unable to host them. Note that the minimum platform version has not been changed - this means that older Corda nodes can still interoperate with Corda 4.4 nodes. Since the APIs added do not affect the wire protocol or have other zone-level implications, applications can take advantage of these new platform version 6 features even if the Corda 4.4 node is running on a network whose minimum platform version is 4. + +For more information on platform version, please see :doc:`versioning`. For more details on upgrading a CorDapp to use platform version 5, please see :doc:`app-upgrade-notes`. + + +Deprecations +~~~~~~~~~~~~ + + +Issues Fixed +~~~~~~~~~~~~ + .. _release_notes_v4_3: From c745f139135a67ae4c954145ca108df5d70521b7 Mon Sep 17 00:00:00 2001 From: Valerio Campanella Date: Fri, 14 Feb 2020 14:42:00 +0000 Subject: [PATCH 27/83] OS 4.4 release notes: fix incorrect info in flow api improvements section --- docs/source/release-notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index c13838693c..dbe5964db2 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -23,7 +23,7 @@ Flows API improvements Corda 4.4 introduces a new ``FlowLogic.await`` API that allows a CorDapp developers to suspend their flow when executing user-defined long-running operations (e.g. call-outs to external services, long-running DB operations). This prevents these long-running operations from blocking the flow thread, allowing other flows to progress in the interim. Previously, these operations had to be executed synchronously, blocking the flow thread. -The CorDapp developer can decide whether to run these asynchronous flow operations on a dedicated node pool, or to handle the threading themselves directly. +The CorDapp developer can decide whether to run these asynchronous flow operations in a dedicated thread pool, or to handle the threading themselves directly. Note that as before, the flow framework suspends automatically for certain operations (e.g. when waiting to receive a message from a counterparty). These suspensions do not have to be triggered explicitly. From 3547b629c36fe226cbb193ef9734ef2922ada746 Mon Sep 17 00:00:00 2001 From: James Higgs <45565019+JamesHR3@users.noreply.github.com> Date: Fri, 14 Feb 2020 17:31:02 +0000 Subject: [PATCH 28/83] [NOTICK] Add a detekt rule to catch tests with no timeout (#5959) * [NOTICK] Add a custom detekt rule for tests with no timeout, and fix remaining missing timeouts * [NOTICK] Add a test for custom detekt rules and tidying * add timeout annotation to new test Co-authored-by: Stefano Franz --- build.gradle | 6 +- .../corda/client/rpc/CordaRPCClientTest.kt | 2 +- .../client/rpc/RpcCustomSerializersTest.kt | 2 +- constants.properties | 1 + .../corda/deterministic/data/GenerateData.kt | 2 +- .../coretests/contracts/ContractsDSLTests.kt | 2 +- .../coretests/crypto/CompositeKeyTests.kt | 4 +- .../coretests/crypto/PartialMerkleTreeTest.kt | 2 +- .../corda/coretests/crypto/SignedDataTest.kt | 2 +- .../crypto/TransactionSignatureTest.kt | 2 +- .../flows/CollectSignaturesFlowTests.kt | 2 +- .../net/corda/core/crypto/CryptoUtilsTest.kt | 2 +- .../core/internal/ClassLoadingUtilsTest.kt | 2 +- detekt-plugins/build.gradle | 26 +++++++ .../detekt/plugins/CordaDetektProvider.kt | 19 +++++ .../plugins/rules/TestWithMissingTimeout.kt | 38 +++++++++ ...tlab.arturbosch.detekt.api.RuleSetProvider | 1 + .../rules/TestWithMissingTimeoutTest.kt | 77 +++++++++++++++++++ .../tutorial/testdsl/TutorialTestDSL.kt | 4 +- .../finance/contracts/universal/Caplet.kt | 2 +- .../finance/contracts/universal/Examples.kt | 2 +- .../contracts/universal/FXFwdTimeOption.kt | 2 +- .../finance/contracts/universal/FXSwap.kt | 2 +- .../corda/finance/contracts/universal/IRS.kt | 2 +- .../finance/contracts/asset/CashTests.kt | 8 +- .../contracts/asset/ObligationTests.kt | 4 +- .../node/internal/NodeFlowManagerTest.kt | 6 +- .../cordapp/CordappConfigFileProviderTests.kt | 2 +- .../cordapp/TypesafeCordappConfigTests.kt | 2 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 2 +- .../FlowLogicRefFactoryImplTest.kt | 2 +- .../internal/CordaClassResolverTests.kt | 18 ++--- .../internal/SerializationTokenTest.kt | 10 +-- .../internal/amqp/SerializationOutputTests.kt | 10 +-- .../amqp/AMQPTypeIdentifierParserTests.kt | 22 +++--- .../internal/amqp/DeserializeMapTests.kt | 4 +- .../serialization/internal/amqp/EnumTests.kt | 4 +- .../internal/amqp/EvolvabilityTests.kt | 4 +- ...ticInitialisationOfSerializedObjectTest.kt | 2 +- .../internal/carpenter/ClassCarpenterTest.kt | 10 +-- settings.gradle | 2 + .../corda/testing/node/CustomNotaryTest.kt | 2 +- .../tools/shell/CustomTypeJsonParsingTests.kt | 4 +- .../corda/tools/shell/InteractiveShellTest.kt | 6 +- 44 files changed, 248 insertions(+), 82 deletions(-) create mode 100644 detekt-plugins/build.gradle create mode 100644 detekt-plugins/src/main/kotlin/net/corda/detekt/plugins/CordaDetektProvider.kt create mode 100644 detekt-plugins/src/main/kotlin/net/corda/detekt/plugins/rules/TestWithMissingTimeout.kt create mode 100644 detekt-plugins/src/main/resources/META-INF/services/io.gitlab.arturbosch.detekt.api.RuleSetProvider create mode 100644 detekt-plugins/src/test/kotlin/net/corda/detekt/plugins/rules/TestWithMissingTimeoutTest.kt diff --git a/build.gradle b/build.gradle index b3ed0c016d..43e567dd0b 100644 --- a/build.gradle +++ b/build.gradle @@ -125,6 +125,7 @@ buildscript { ext.commons_lang_version = '3.9' ext.commons_io_version = '2.6' ext.controlsfx_version = '8.40.15' + ext.detekt_version = constants.getProperty('detektVersion') if (JavaVersion.current() == JavaVersion.VERSION_11) { // has been compiled by a more recent version of the Java Runtime (class file version 55.0) ext.fontawesomefx_commons_version = '11.0' @@ -464,13 +465,14 @@ task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { } } -task detekt(type: JavaExec) { +task detekt(type: JavaExec, dependsOn: ":detekt-plugins:jar") { main = "io.gitlab.arturbosch.detekt.cli.Main" classpath = configurations.detekt def input = "$projectDir" def config = "$projectDir/detekt-config.yml" def baseline = "$projectDir/detekt-baseline.xml" - def params = ['-i', input, '-c', config, '-b', baseline] + def plugins = "$projectDir/detekt-plugins/build/libs/detekt-plugins-${version}.jar" + def params = ['-i', input, '-c', config, '-b', baseline, '--plugins', plugins] args(params) } diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt index 60271d97a3..59f05cb8e2 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/CordaRPCClientTest.kt @@ -264,7 +264,7 @@ class CordaRPCClientTest : NodeBasedTest(listOf("net.corda.finance"), notaries = assertThat(outOfProcessRpc.waitFor()).isZero() // i.e. no exceptions were thrown } - @Test + @Test(timeout=300_000) fun `nonspecific reconnect errors dont trigger graceful reconnect`() { val inputJar1 = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar)!! val inputJar2 = Thread.currentThread().contextClassLoader.getResourceAsStream(testJar)!! diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RpcCustomSerializersTest.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RpcCustomSerializersTest.kt index a66052e814..5e3c50f976 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RpcCustomSerializersTest.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RpcCustomSerializersTest.kt @@ -72,7 +72,7 @@ class RpcCustomSerializersTest { } } - @Test + @Test(timeout=300_000) fun `when a custom serializer is missing from the rpc client the resulting exception progagtes and client does not reconnect`() { driver(DriverParameters(startNodesInProcess = false, cordappsForAllNodes = listOf(enclosedCordapp()))) { val server = startNode(providedName = ALICE_NAME).get() diff --git a/constants.properties b/constants.properties index 2d7ecca94b..d43a8afbe8 100644 --- a/constants.properties +++ b/constants.properties @@ -35,4 +35,5 @@ deterministicRtVersion=1.0-RC02 openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4 openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4 jolokiaAgentVersion=1.6.1 +detektVersion=1.0.1 diff --git a/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/GenerateData.kt b/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/GenerateData.kt index 559530278f..4612f19c7a 100644 --- a/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/GenerateData.kt +++ b/core-deterministic/testing/data/src/test/kotlin/net/corda/deterministic/data/GenerateData.kt @@ -71,7 +71,7 @@ class GenerateData { testSerialization.reset() } - @Test + @Test(timeout = 300_000) fun verifyTransactions() { URLClassLoader(arrayOf(TEST_DATA.toUri().toURL())).use { cl -> cl.loadResource("txverify/tx-success.bin") diff --git a/core-tests/src/test/kotlin/net/corda/coretests/contracts/ContractsDSLTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/contracts/ContractsDSLTests.kt index 52e86e1336..4768cf9681 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/contracts/ContractsDSLTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/contracts/ContractsDSLTests.kt @@ -47,7 +47,7 @@ class RequireSingleCommandTests(private val testFunction: (Collection { + override fun apply(instant: Instant): String { + return instant.toString() + } + } + } + """.trimIndent() + + @Test(timeout = 300_000) + fun `catches missing timeout for junit 4 tests`() { + val rule = TestWithMissingTimeout() + val findings = rule.lint(junit4Code) + assertThat(findings).hasSize(1) + } + + @Test(timeout = 300_000) + fun `does not warn for junit 5 tests`() { + val rule = TestWithMissingTimeout() + val findings = rule.lint(junit5Code) + assertThat(findings).hasSize(0) + } +} diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt index a981ad2467..b5670ef17c 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt @@ -77,7 +77,7 @@ class TutorialTestDSL { // DOCSTART 2 // This example test will fail with this exception. - @Test(expected = IllegalStateException::class) + @Test(expected = IllegalStateException::class, timeout=300_000) fun simpleCP() { val inState = getPaper() ledgerServices.ledger(dummyNotary.party) { @@ -92,7 +92,7 @@ class TutorialTestDSL { // DOCSTART 3 // This example test will fail with this exception. - @Test(expected = TransactionVerificationException.ContractRejection::class) + @Test(expected = TransactionVerificationException.ContractRejection::class, timeout=300_000) fun simpleCPMove() { val inState = getPaper() ledgerServices.ledger(dummyNotary.party) { diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt index 61e3039a39..3565f6aee2 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Caplet.kt @@ -121,7 +121,7 @@ class Caplet { } } - @Test @Ignore + @Test(timeout=300_000) @Ignore fun `pretty print`() { println ( prettyPrint(contract) ) diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Examples.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Examples.kt index e4b989a6bf..6e3f27dea5 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Examples.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/Examples.kt @@ -117,7 +117,7 @@ class Examples { } } - @Test @Ignore + @Test(timeout=300_000) @Ignore fun `pretty print`() { println ( prettyPrint(cds_contract) ) diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt index 57e1a04e7a..f78dae7eed 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXFwdTimeOption.kt @@ -125,7 +125,7 @@ class FXFwdTimeOption { } } - @Test @Ignore + @Test(timeout=300_000) @Ignore fun `pretty print`() { println ( prettyPrint(initialContract) ) diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt index c7458e355b..ca86d9a3a4 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/FXSwap.kt @@ -164,7 +164,7 @@ class FXSwap { } } - @Test @Ignore + @Test(timeout=300_000) @Ignore fun `pretty print`() { println ( prettyPrint(contract) ) } diff --git a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt index c7d8895502..8e7dc274df 100644 --- a/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt +++ b/experimental/src/test/kotlin/net/corda/finance/contracts/universal/IRS.kt @@ -202,7 +202,7 @@ class IRS { } } - @Test @Ignore + @Test(timeout=300_000) @Ignore fun `pretty print`() { println ( prettyPrint(contractInitial) ) diff --git a/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt b/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt index 4f1ce792ff..bb9356a8c4 100644 --- a/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt +++ b/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/CashTests.kt @@ -300,7 +300,7 @@ class CashTests { * Test that the issuance builder rejects building into a transaction with existing * cash inputs. */ - @Test(expected = IllegalStateException::class) + @Test(expected = IllegalStateException::class, timeout=300_000) fun `reject issuance with inputs`() { // Issue some cash var ptx = TransactionBuilder(dummyNotary.party) @@ -762,7 +762,7 @@ class CashTests { assertEquals(6000.DOLLARS `issued by` defaultIssuer, states.sumCashBy(megaCorp.party)) } - @Test(expected = UnsupportedOperationException::class) + @Test(expected = UnsupportedOperationException::class, timeout=300_000) fun `summing by owner throws`() { val states = listOf( Cash.State(2000.DOLLARS `issued by` defaultIssuer, megaCorp.party), @@ -778,7 +778,7 @@ class CashTests { assertNull(states.sumCashOrNull()) } - @Test(expected = UnsupportedOperationException::class) + @Test(expected = UnsupportedOperationException::class, timeout=300_000) fun `summing no currencies throws`() { val states = emptyList() states.sumCash() @@ -797,7 +797,7 @@ class CashTests { assertEquals(expected, actual) } - @Test(expected = IllegalArgumentException::class) + @Test(expected = IllegalArgumentException::class, timeout=300_000) fun `summing multiple currencies`() { val states = listOf( Cash.State(1000.DOLLARS `issued by` defaultIssuer, megaCorp.party), diff --git a/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt b/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt index 3cc9dd479c..9f417b9a35 100644 --- a/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt +++ b/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt @@ -253,7 +253,7 @@ class ObligationTests { * Test that the issuance builder rejects building into a transaction with existing * cash inputs. */ - @Test(expected = IllegalStateException::class) + @Test(expected = IllegalStateException::class, timeout=300_000) fun `reject issuance with inputs`() { // Issue some obligation val tx = TransactionBuilder(DUMMY_NOTARY).apply { @@ -854,7 +854,7 @@ class ObligationTests { fiveKDollarsFromMegaToMega.copy(template = megaCorpDollarSettlement.copy(acceptableIssuedProducts = miniCorpIssuer)).bilateralNetState) } - @Test(expected = IllegalStateException::class) + @Test(expected = IllegalStateException::class, timeout=300_000) fun `states cannot be netted if not in the normal state`() { inState.copy(lifecycle = Lifecycle.DEFAULTED).bilateralNetState } diff --git a/node/src/test/kotlin/net/corda/node/internal/NodeFlowManagerTest.kt b/node/src/test/kotlin/net/corda/node/internal/NodeFlowManagerTest.kt index ffab3c8e01..4a417f368a 100644 --- a/node/src/test/kotlin/net/corda/node/internal/NodeFlowManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/NodeFlowManagerTest.kt @@ -57,7 +57,7 @@ class NodeFlowManagerTest { } - @Test(expected = IllegalStateException::class) + @Test(expected = IllegalStateException::class, timeout = 300_000) fun `should fail to validate if more than one registration with equal weight`() { val nodeFlowManager = NodeFlowManager() nodeFlowManager.registerInitiatedFlow(Init::class.java, Resp::class.java) @@ -65,7 +65,7 @@ class NodeFlowManagerTest { nodeFlowManager.validateRegistrations() } - @Test() + @Test(timeout = 300_000) fun `should allow registration of flows with different weights`() { val nodeFlowManager = NodeFlowManager() nodeFlowManager.registerInitiatedFlow(Init::class.java, Resp::class.java) @@ -76,7 +76,7 @@ class NodeFlowManagerTest { Assert.assertThat(flow, `is`(instanceOf(RespSub::class.java))) } - @Test() + @Test(timeout = 300_000) fun `should allow updating of registered responder at runtime`() { val nodeFlowManager = NodeFlowManager() nodeFlowManager.registerInitiatedFlow(Init::class.java, Resp::class.java) diff --git a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappConfigFileProviderTests.kt b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappConfigFileProviderTests.kt index b9633d3c0b..5dad12f765 100644 --- a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappConfigFileProviderTests.kt +++ b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappConfigFileProviderTests.kt @@ -45,7 +45,7 @@ class CordappConfigFileProviderTests { assertThat(provider.getConfigByName(cordappName)).isEqualTo(alternateValidConfig) } - @Test(expected = ConfigException.Parse::class) + @Test(expected = ConfigException.Parse::class, timeout=300_000) fun `an invalid config throws an exception`() { cordappConfFile.writeText(invalidConfig) provider.getConfigByName(cordappName) diff --git a/node/src/test/kotlin/net/corda/node/internal/cordapp/TypesafeCordappConfigTests.kt b/node/src/test/kotlin/net/corda/node/internal/cordapp/TypesafeCordappConfigTests.kt index a69d29abbe..18f20d6ad2 100644 --- a/node/src/test/kotlin/net/corda/node/internal/cordapp/TypesafeCordappConfigTests.kt +++ b/node/src/test/kotlin/net/corda/node/internal/cordapp/TypesafeCordappConfigTests.kt @@ -37,7 +37,7 @@ class TypesafeCordappConfigTests { assertThat(cordappConf.exists("notexists")).isFalse() } - @Test(expected = CordappConfigException::class) + @Test(expected = CordappConfigException::class, timeout=300_000) fun `test that an exception is thrown when trying to access a non-extant field`() { val config = ConfigFactory.empty() val cordappConf = TypesafeCordappConfig(config) diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index e25308e5ec..200b60742a 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -145,7 +145,7 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { } } - @Test(expected = InsufficientBalanceException::class) + @Test(expected = InsufficientBalanceException::class, timeout=300_000) fun `trade cash for commercial paper fails using soft locking`() { mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP), threadPerNode = true) val notaryNode = mockNet.defaultNotaryNode diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt index 7d6e8734a4..a780bfbf2f 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowLogicRefFactoryImplTest.kt @@ -77,7 +77,7 @@ class FlowLogicRefFactoryImplTest { flowLogicRefFactory.createKotlin(KotlinFlowLogic::class.java, args) } - @Test(expected = IllegalFlowLogicException::class) + @Test(expected = IllegalFlowLogicException::class, timeout=300_000) fun `create for non-schedulable flow logic`() { flowLogicRefFactory.create(NonSchedulableFlow::class.jvmName) } diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt index a7945ab61b..5208324e7d 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt @@ -125,7 +125,7 @@ class CordaClassResolverTests { CordaClassResolver(emptyWhitelistContext).getRegistration(Foo.Bar::class.java) } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `Unannotated specialised enum does not work`() { CordaClassResolver(emptyWhitelistContext).getRegistration(BadFood.Mud::class.java) } @@ -135,7 +135,7 @@ class CordaClassResolverTests { CordaClassResolver(emptyWhitelistContext).getRegistration(Simple.Easy::class.java) } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `Unannotated simple enum does not work`() { CordaClassResolver(emptyWhitelistContext).getRegistration(BadSimple.Nasty::class.java) } @@ -146,7 +146,7 @@ class CordaClassResolverTests { CordaClassResolver(emptyWhitelistContext).getRegistration(values.javaClass) } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `Unannotated array elements do not work`() { val values = arrayOf(NotSerializable()) CordaClassResolver(emptyWhitelistContext).getRegistration(values.javaClass) @@ -168,13 +168,13 @@ class CordaClassResolverTests { kryo.register(NotSerializable::class.java) } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `Calling register method on unmodified Kryo does consult the whitelist`() { val kryo = Kryo(CordaClassResolver(emptyWhitelistContext), MapReferenceResolver()) kryo.register(NotSerializable::class.java) } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `Annotation is needed without whitelisting`() { CordaClassResolver(emptyWhitelistContext).getRegistration(NotSerializable::class.java) } @@ -195,19 +195,19 @@ class CordaClassResolverTests { CordaClassResolver(emptyWhitelistContext).getRegistration(Integer.TYPE) } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `Annotation does not work for custom serializable`() { CordaClassResolver(emptyWhitelistContext).getRegistration(CustomSerializable::class.java) } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `Annotation does not work in conjunction with Kryo annotation`() { CordaClassResolver(emptyWhitelistContext).getRegistration(DefaultSerializable::class.java) } private fun importJar(storage: AttachmentStorage, uploader: String = DEPLOYED_CORDAPP_UPLOADER) = ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it, uploader, "") } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `Annotation does not work in conjunction with AttachmentClassLoader annotation`() { val storage = InternalMockAttachmentStorage(MockAttachmentStorage()) val attachmentTrustCalculator = NodeAttachmentTrustCalculator(storage, TestingNamedCacheFactory()) @@ -217,7 +217,7 @@ class CordaClassResolverTests { CordaClassResolver(emptyWhitelistContext).getRegistration(attachedClass) } - @Test(expected = TransactionVerificationException.UntrustedAttachmentsException::class) + @Test(expected = TransactionVerificationException.UntrustedAttachmentsException::class, timeout=300_000) fun `Attempt to load contract attachment with untrusted uploader should fail with UntrustedAttachmentsException`() { val storage = InternalMockAttachmentStorage(MockAttachmentStorage()) val attachmentTrustCalculator = NodeAttachmentTrustCalculator(storage, TestingNamedCacheFactory()) diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt index 1533d3d6a4..fbe7537199 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt @@ -70,7 +70,7 @@ class SerializationTokenTest { assertThat(tokenizableAfter).isSameAs(tokenizableBefore) } - @Test(expected = UnsupportedOperationException::class) + @Test(expected = UnsupportedOperationException::class, timeout=300_000) fun `new token encountered after context init`() { val tokenizableBefore = UnitSerializeAsToken() val context = serializeAsTokenContext(emptyList()) @@ -78,7 +78,7 @@ class SerializationTokenTest { tokenizableBefore.checkpointSerialize(testContext) } - @Test(expected = UnsupportedOperationException::class) + @Test(expected = UnsupportedOperationException::class, timeout=300_000) fun `deserialize unregistered token`() { val tokenizableBefore = UnitSerializeAsToken() val context = serializeAsTokenContext(emptyList()) @@ -87,13 +87,13 @@ class SerializationTokenTest { serializedBytes.checkpointDeserialize(testContext) } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `no context set`() { val tokenizableBefore = UnitSerializeAsToken() tokenizableBefore.checkpointSerialize(context) } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `deserialize non-token`() { val tokenizableBefore = UnitSerializeAsToken() val context = serializeAsTokenContext(tokenizableBefore) @@ -119,7 +119,7 @@ class SerializationTokenTest { override fun toToken(context: SerializeAsTokenContext): SerializationToken = UnitSerializationToken } - @Test(expected = KryoException::class) + @Test(expected = KryoException::class, timeout=300_000) fun `token returns unexpected type`() { val tokenizableBefore = WrongTypeSerializeAsToken() val context = serializeAsTokenContext(tokenizableBefore) diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt index d13502075f..dade6b0f50 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt @@ -322,7 +322,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi serdes(obj) } - @Test(expected = IllegalArgumentException::class) + @Test(expected = IllegalArgumentException::class, timeout=300_000) fun `test dislike of HashMap`() { val obj = WrapHashMap(HashMap()) serdes(obj) @@ -364,7 +364,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi serdes(obj) } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout=300_000) fun `test whitelist`() { val obj = Woo2(4) serdes(obj, SerializerFactoryBuilder.build(EmptyWhitelist, @@ -380,7 +380,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi )) } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout=300_000) fun `test generic list subclass is not supported`() { val obj = FooList() serdes(obj) @@ -458,13 +458,13 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi serdes(obj) } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout=300_000) fun `test mismatched property and constructor naming`() { val obj = Mismatch(456) serdes(obj) } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout=300_000) fun `test mismatched property and constructor type`() { val obj = MismatchType(456) serdes(obj) diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParserTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParserTests.kt index b4bdbad2e1..ff7825da1d 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParserTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/AMQPTypeIdentifierParserTests.kt @@ -100,47 +100,47 @@ class AMQPTypeIdentifierParserTests { verify("java.util.List>>") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test trailing text`() { verify("java.util.Mapfoo") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test trailing comma`() { verify("java.util.Map") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test leading comma`() { verify("java.util.Map<,java.lang.String, java.lang.Integer>") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test middle comma`() { verify("java.util.Map<,java.lang.String,, java.lang.Integer>") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test trailing close`() { verify("java.util.Map>") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test empty params`() { verify("java.util.Map<>") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test mid whitespace`() { verify("java.u til.List") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test mid whitespace2`() { verify("java.util.List") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test wrong number of parameters`() { verify("java.util.List") } @@ -150,12 +150,12 @@ class AMQPTypeIdentifierParserTests { verify("java.lang.String") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test parameters on non-generic type`() { verify("java.lang.String") } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout = 300_000) fun `test excessive nesting`() { var nested = "java.lang.Integer" for (i in 1..MAX_TYPE_PARAM_DEPTH) { diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeMapTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeMapTests.kt index 431c438de3..f40e776cd8 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeMapTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeMapTests.kt @@ -28,7 +28,7 @@ class DeserializeMapTests { DeserializationInput(sf).deserialize(serialisedBytes) } - @Test(expected = java.io.NotSerializableException::class) + @Test(expected = java.io.NotSerializableException::class, timeout=300_000) fun abstractMapFromMapOf() { data class C(val c: AbstractMap) @@ -38,7 +38,7 @@ class DeserializeMapTests { DeserializationInput(sf).deserialize(serialisedBytes) } - @Test(expected = java.io.NotSerializableException::class) + @Test(expected = java.io.NotSerializableException::class, timeout=300_000) fun abstractMapFromTreeMap() { data class C(val c: AbstractMap) diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumTests.kt index 51f58c614c..c2cda2c1cf 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EnumTests.kt @@ -155,7 +155,7 @@ class EnumTests { assertEquals(c.c, obj.c) } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout=300_000) fun changedEnum1() { val url = EnumTests::class.java.getResource("EnumTests.changedEnum1") @@ -174,7 +174,7 @@ class EnumTests { DeserializationInput(sf1).deserialize(SerializedBytes(sc2)) } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout=300_000) fun changedEnum2() { val url = EnumTests::class.java.getResource("EnumTests.changedEnum2") diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt index 39d76fa62f..d565e34777 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/EvolvabilityTests.kt @@ -113,7 +113,7 @@ class EvolvabilityTests { assertEquals(null, deserializedC.b) } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout=300_000) fun addAdditionalParam() { val sf = testDefaultFactory() val url = EvolvabilityTests::class.java.getResource("EvolvabilityTests.addAdditionalParam") @@ -321,7 +321,7 @@ class EvolvabilityTests { DeserializationInput(factory).deserialize(SerializedBytes(url.readBytes())) } - @Test(expected = NotSerializableException::class) + @Test(expected = NotSerializableException::class, timeout=300_000) @Suppress("UNUSED") fun addMandatoryFieldWithAltConstructorUnAnnotated() { val sf = testDefaultFactory() diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/StaticInitialisationOfSerializedObjectTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/StaticInitialisationOfSerializedObjectTest.kt index 0d1b2d9cdc..bcdf0ee1e4 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/StaticInitialisationOfSerializedObjectTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/StaticInitialisationOfSerializedObjectTest.kt @@ -40,7 +40,7 @@ class C2(var b: Int) { } class StaticInitialisationOfSerializedObjectTest { - @Test(expected = java.lang.ExceptionInInitializerError::class) + @Test(expected = java.lang.ExceptionInInitializerError::class, timeout=300_000) fun itBlowsUp() { C() } diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTest.kt index 84c04c8baf..1c315f1c5f 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenterTest.kt @@ -95,7 +95,7 @@ class ClassCarpenterTest { assertEquals("Person{age=32, name=Mike}", i.toString()) } - @Test(expected = DuplicateNameException::class) + @Test(expected = DuplicateNameException::class, timeout=300_000) fun duplicates() { cc.build(ClassSchema("gen.EmptyClass", emptyMap())) cc.build(ClassSchema("gen.EmptyClass", emptyMap())) @@ -301,7 +301,7 @@ class ClassCarpenterTest { assertEquals(testD, i["d"]) } - @Test(expected = java.lang.IllegalArgumentException::class) + @Test(expected = java.lang.IllegalArgumentException::class, timeout=300_000) fun `null parameter small int`() { val className = "iEnjoySwede" val schema = ClassSchema( @@ -313,7 +313,7 @@ class ClassCarpenterTest { clazz.constructors[0].newInstance(a) } - @Test(expected = NullablePrimitiveException::class) + @Test(expected = NullablePrimitiveException::class, timeout=300_000) fun `nullable parameter small int`() { val className = "iEnjoySwede" val schema = ClassSchema( @@ -351,7 +351,7 @@ class ClassCarpenterTest { clazz.constructors[0].newInstance(a) } - @Test(expected = java.lang.reflect.InvocationTargetException::class) + @Test(expected = java.lang.reflect.InvocationTargetException::class, timeout=300_000) fun `non nullable parameter integer with null`() { val className = "iEnjoyWibble" val schema = ClassSchema( @@ -383,7 +383,7 @@ class ClassCarpenterTest { assertEquals("$className{a=[1, 2, 3]}", i.toString()) } - @Test(expected = java.lang.reflect.InvocationTargetException::class) + @Test(expected = java.lang.reflect.InvocationTargetException::class, timeout=300_000) fun `nullable int array throws`() { val className = "iEnjoySwede" val schema = ClassSchema( diff --git a/settings.gradle b/settings.gradle index 6e8ff4ba1f..78f158b144 100644 --- a/settings.gradle +++ b/settings.gradle @@ -105,3 +105,5 @@ include 'core-deterministic:testing:verifier' include 'serialization-deterministic' include 'tools:checkpoint-agent' +include 'detekt-plugins' + diff --git a/testing/node-driver/src/test/kotlin/net/corda/testing/node/CustomNotaryTest.kt b/testing/node-driver/src/test/kotlin/net/corda/testing/node/CustomNotaryTest.kt index 502700a3dc..91ccdd05c5 100644 --- a/testing/node-driver/src/test/kotlin/net/corda/testing/node/CustomNotaryTest.kt +++ b/testing/node-driver/src/test/kotlin/net/corda/testing/node/CustomNotaryTest.kt @@ -50,7 +50,7 @@ class CustomNotaryTest { mockNet.stopNodes() } - @Test(expected = CustomNotaryException::class) + @Test(expected = CustomNotaryException::class, timeout=300_000) fun `custom notary service is active`() { val tx = DummyContract.generateInitial(Random().nextInt(), notary, alice.ref(0)) val stx = aliceNode.services.signInitialTransaction(tx) diff --git a/tools/shell/src/test/kotlin/net/corda/tools/shell/CustomTypeJsonParsingTests.kt b/tools/shell/src/test/kotlin/net/corda/tools/shell/CustomTypeJsonParsingTests.kt index 5d291987b0..60823aebd4 100644 --- a/tools/shell/src/test/kotlin/net/corda/tools/shell/CustomTypeJsonParsingTests.kt +++ b/tools/shell/src/test/kotlin/net/corda/tools/shell/CustomTypeJsonParsingTests.kt @@ -49,7 +49,7 @@ class CustomTypeJsonParsingTests { assertEquals("26b37265-a1fd-4c77-b2e0-715917ef619f", state.linearId.id.toString()) } - @Test(expected = JsonMappingException::class) + @Test(expected = JsonMappingException::class, timeout=300_000) fun `Deserializing by parsing string contain invalid uuid with underscore`() { val json = """{"linearId":"extkey564_26b37265-a1fd-4c77-b2e0"}""" objectMapper.readValue(json) @@ -63,7 +63,7 @@ class CustomTypeJsonParsingTests { assertEquals("26b37265-a1fd-4c77-b2e0-715917ef619f", state.uuid.toString()) } - @Test(expected = JsonMappingException::class) + @Test(expected = JsonMappingException::class, timeout=300_000) fun `Deserializing UUID by parsing invalid uuid string`() { val json = """{"uuid":"26b37265-a1fd-4c77-b2e0"}""" objectMapper.readValue(json) diff --git a/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt b/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt index 9bfb1d465d..4d4a49c169 100644 --- a/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt +++ b/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt @@ -171,13 +171,13 @@ class InteractiveShellTest { expected = "10 (1)++200 (2)" ) - @Test(expected = InteractiveShell.NoApplicableConstructor::class) + @Test(expected = InteractiveShell.NoApplicableConstructor::class, timeout=300_000) fun flowStartNoArgs() = check("", "") - @Test(expected = InteractiveShell.NoApplicableConstructor::class) + @Test(expected = InteractiveShell.NoApplicableConstructor::class, timeout=300_000) fun flowMissingParam() = check("d: Yo", "") - @Test(expected = InteractiveShell.NoApplicableConstructor::class) + @Test(expected = InteractiveShell.NoApplicableConstructor::class, timeout=300_000) fun flowTooManyParams() = check("b: 12, c: Yo, d: Bar", "") @Test(timeout=300_000) From dec366148bdfd1ffbc11c844f6239bfd46b7bd76 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Fri, 14 Feb 2020 17:32:18 +0000 Subject: [PATCH 29/83] NOTICK: Add timeouts to serialization-djvm tests too. (#5967) --- .../net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt | 2 +- .../src/test/kotlin/net/corda/serialization/djvm/TestBase.kt | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt index 5d12059e0e..c23fe08ebe 100644 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt +++ b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeOffsetTimeTest.kt @@ -13,7 +13,7 @@ import java.util.function.Function @ExtendWith(LocalSerialization::class) class DeserializeOffsetTimeTest : TestBase(KOTLIN) { @Test - fun `test deserializing instant`() { + fun `test deserializing offset time`() { val time = OffsetTime.now() val data = time.serialize() diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestBase.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestBase.kt index 8533ce52f7..ae50b57eae 100644 --- a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestBase.kt +++ b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/TestBase.kt @@ -13,16 +13,19 @@ import net.corda.djvm.source.BootstrapClassLoader import net.corda.djvm.source.UserPathSource import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Timeout import org.junit.jupiter.api.fail import java.io.File import java.nio.file.Files.exists import java.nio.file.Files.isDirectory import java.nio.file.Path import java.nio.file.Paths +import java.util.concurrent.TimeUnit.MINUTES import java.util.function.Consumer import kotlin.concurrent.thread @Suppress("unused", "MemberVisibilityCanBePrivate") +@Timeout(5, unit = MINUTES) abstract class TestBase(type: SandboxType) { companion object { const val SANDBOX_STRING = "sandbox.java.lang.String" From 21325dfa4fb8151e77f368e9356c16ae89c70aa3 Mon Sep 17 00:00:00 2001 From: Stefano Franz Date: Fri, 14 Feb 2020 17:40:30 +0000 Subject: [PATCH 30/83] delete version from detekt plugins project --- detekt-plugins/build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/detekt-plugins/build.gradle b/detekt-plugins/build.gradle index 5595633bb5..20c6e8aa28 100644 --- a/detekt-plugins/build.gradle +++ b/detekt-plugins/build.gradle @@ -4,9 +4,6 @@ plugins { id 'kotlin-jpa' } -group 'net.corda' -version '4.4-SNAPSHOT' - sourceCompatibility = 1.8 repositories { From 872d6ef4df7d9150007a3b929483450f2af1a334 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Sun, 16 Feb 2020 08:21:05 +0000 Subject: [PATCH 31/83] NOTICK: Update Gradle files for DeteKt plugins. (#5970) --- build.gradle | 12 +++++++----- detekt-plugins/build.gradle | 18 +++--------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index 43e567dd0b..23b522603a 100644 --- a/build.gradle +++ b/build.gradle @@ -465,18 +465,20 @@ task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { } } -task detekt(type: JavaExec, dependsOn: ":detekt-plugins:jar") { - main = "io.gitlab.arturbosch.detekt.cli.Main" - classpath = configurations.detekt +tasks.register('detekt', JavaExec) { def input = "$projectDir" def config = "$projectDir/detekt-config.yml" def baseline = "$projectDir/detekt-baseline.xml" - def plugins = "$projectDir/detekt-plugins/build/libs/detekt-plugins-${version}.jar" + def detektPluginsJar = project(':detekt-plugins').tasks.jar + def plugins = detektPluginsJar.outputs.files.singleFile def params = ['-i', input, '-c', config, '-b', baseline, '--plugins', plugins] + inputs.files(detektPluginsJar, config, baseline) + main = "io.gitlab.arturbosch.detekt.cli.Main" + classpath = configurations.detekt args(params) } -task detektBaseline(type: JavaExec) { +tasks.register('detektBaseline', JavaExec) { main = "io.gitlab.arturbosch.detekt.cli.Main" classpath = configurations.detekt def input = "$projectDir" diff --git a/detekt-plugins/build.gradle b/detekt-plugins/build.gradle index 20c6e8aa28..4657d00954 100644 --- a/detekt-plugins/build.gradle +++ b/detekt-plugins/build.gradle @@ -1,23 +1,11 @@ plugins { - id 'java' id 'kotlin' - id 'kotlin-jpa' -} - -sourceCompatibility = 1.8 - -repositories { - mavenCentral() } dependencies { - testCompile group: 'junit', name: 'junit', version: '4.12' + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + implementation "io.gitlab.arturbosch.detekt:detekt-api:$detekt_version" + testImplementation "junit:junit:$junit_version" testImplementation "io.gitlab.arturbosch.detekt:detekt-test:$detekt_version" testImplementation "org.assertj:assertj-core:$assertj_version" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation "io.gitlab.arturbosch.detekt:detekt-api:$detekt_version" -} - -publish { - name "corda-detekt-plugins" } From 98e9b1caa6bbaf05508c79329d4e78db3e9dcd4b Mon Sep 17 00:00:00 2001 From: Walter Oggioni <6357328+woggioni@users.noreply.github.com> Date: Mon, 17 Feb 2020 11:34:01 +0000 Subject: [PATCH 32/83] EG-65 - Improved error reporting on stdout (#5962) I modified the `ErrorCodeRewritePolicy` to concatenate all the error messages of the exception's cause chain. The code will start from the throwable being passed to the logger and concatenate its error message with the one in its cause and proceed recursively until it finds an exception with no cause or it detects a loop (an exception already encountered in the traversal). This should provide customers with more information about errors without having to look at the logs (that should still remain the primary source of information) --- .../logging/ExceptionsErrorCodeFunctions.kt | 28 ++++++++- .../logging/WalkExceptionCausedByListTest.kt | 59 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 common/logging/src/test/kotlin/net/corda/commmon/logging/WalkExceptionCausedByListTest.kt diff --git a/common/logging/src/main/kotlin/net/corda/common/logging/ExceptionsErrorCodeFunctions.kt b/common/logging/src/main/kotlin/net/corda/common/logging/ExceptionsErrorCodeFunctions.kt index 5fea6054e4..a34e436bb8 100644 --- a/common/logging/src/main/kotlin/net/corda/common/logging/ExceptionsErrorCodeFunctions.kt +++ b/common/logging/src/main/kotlin/net/corda/common/logging/ExceptionsErrorCodeFunctions.kt @@ -4,11 +4,37 @@ import org.apache.logging.log4j.Level import org.apache.logging.log4j.message.Message import org.apache.logging.log4j.message.SimpleMessage import java.util.* +//Returns an iterator that traverses all the exception's cause chain stopping in case of loops (an exception caused by itself) +fun Throwable.walkExceptionCausedByList() : Iterator { + val self = this + return object : Iterator { + private var cursor : Throwable? = self + private val knownThrowables = mutableSetOf() + + override fun hasNext(): Boolean { + return cursor != null + } + + override fun next(): Throwable { + val result = cursor + val cause = cursor?.cause + cursor = if(cause != null && knownThrowables.add(cause)) { + cause + } else { + null + } + return result!! + } + } +} 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) + error != null && level.isInRange(Level.FATAL, Level.WARN) -> { + val message = error.walkExceptionCausedByList().asSequence().mapNotNull(Throwable::message).joinToString(" - ") + CompositeMessage("$message [errorCode=${error.errorCode()}, moreInformationAt=${error.errorCodeLocationUrl()}]", format, parameters, throwable) + } else -> this } } diff --git a/common/logging/src/test/kotlin/net/corda/commmon/logging/WalkExceptionCausedByListTest.kt b/common/logging/src/test/kotlin/net/corda/commmon/logging/WalkExceptionCausedByListTest.kt new file mode 100644 index 0000000000..371deef20d --- /dev/null +++ b/common/logging/src/test/kotlin/net/corda/commmon/logging/WalkExceptionCausedByListTest.kt @@ -0,0 +1,59 @@ +package net.corda.commmon.logging + +import net.corda.common.logging.walkExceptionCausedByList +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +@RunWith(Parameterized::class) +class WalkExceptionCausedByListTest(@Suppress("UNUSED_PARAMETER") testTitle: String, private val e: Throwable, private val expectedExceptionSequence: List) { + + private class TestThrowable(val id : Int, cause : Throwable?) : Throwable(cause) { + override fun toString(): String { + return "${this.javaClass.simpleName}(${this.id})" + } + } + + companion object { + @JvmStatic + @Parameterized.Parameters(name = "{0}") + fun data(): Collection> { + val field = Throwable::class.java.getDeclaredField("cause") + field.isAccessible = true + return listOf( + run { + val e = TestThrowable(0, null) + arrayOf("Simple exception with no cause", e, listOf(e)) + }, + run { + var e: TestThrowable? = null + val exceptions = (0 until 10).map { + e = TestThrowable(it, e) + e + } + arrayOf("Exception with cause list", e!!, exceptions.asReversed()) + }, + run { + val e = TestThrowable(0, null) + field.set(e, e) + arrayOf("Exception caused by itself", e, listOf(e)) + }, + run { + val stack = mutableListOf() + var e: TestThrowable? = null + for(i in 0 until 10) { + e = TestThrowable(i, stack.lastOrNull()) + stack.add(e!!) + } + field.set(stack[0], stack[4]) + arrayOf("Exception with loop in cause list", e!!, stack.asReversed()) + }) + } + } + + @Test(timeout = 1000) + fun test() { + Assert.assertEquals(expectedExceptionSequence, e.walkExceptionCausedByList().asSequence().toList()) + } +} \ No newline at end of file From 12f59e197844f292760f946dea3572eb17c0a1a8 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 17 Feb 2020 12:29:52 +0000 Subject: [PATCH 33/83] CORDA-3618: Upgrade to Corda Gradle plugins 5.0.8. --- constants.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index a7f20294d5..c1185338a9 100644 --- a/constants.properties +++ b/constants.properties @@ -4,7 +4,7 @@ cordaVersion=4.5 versionSuffix=SNAPSHOT -gradlePluginsVersion=5.0.6 +gradlePluginsVersion=5.0.8 kotlinVersion=1.2.71 java8MinUpdateVersion=171 # ***************************************************************# From 9b5080251dd125a105dd7d3af4c7f60265121f8c Mon Sep 17 00:00:00 2001 From: Ryan Fowler Date: Mon, 17 Feb 2020 11:08:09 +0000 Subject: [PATCH 34/83] CORDA-3593: exit the InteractiveShell on shutdown command --- .../src/main/kotlin/net/corda/tools/shell/InteractiveShell.kt | 4 ++++ 1 file changed, 4 insertions(+) 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 27c8f3472f..ee26e6016b 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 @@ -634,6 +634,10 @@ object InteractiveShell { InputStreamSerializer.invokeContext = null InputStreamDeserializer.closeAll() } + if (cmd == "shutdown") { + out.println("Called 'shutdown' on the node.\nQuitting the shell now.").also { out.flush() } + onExit.invoke() + } return result } From a18caef56b68dce3a8220dd375630b87d464d01d Mon Sep 17 00:00:00 2001 From: Thomas Schroeter Date: Mon, 17 Feb 2020 16:42:39 +0000 Subject: [PATCH 35/83] CORDA-3495: fix link to network builder (#5977) --- docs/source/network-builder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/network-builder.rst b/docs/source/network-builder.rst index 168888ee21..1a4e5e7f35 100644 --- a/docs/source/network-builder.rst +++ b/docs/source/network-builder.rst @@ -16,7 +16,7 @@ Unlike the official image, a `node.conf` file and CorDapps are embedded into the 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: From c23deee3fb4b793cc3d27a582df6374ea91d64ac Mon Sep 17 00:00:00 2001 From: Valerio Campanella Date: Mon, 17 Feb 2020 17:51:03 +0000 Subject: [PATCH 36/83] OS 4.4 release notes: address comments --- docs/source/release-notes.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index dbe5964db2..df6a62cfd0 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -4,7 +4,7 @@ Release notes .. contents:: :depth: 2 -Welcome to the Corda 4.4 release notes. Please read these carefully to understand what’s new in this release and how the features can help you. Just as prior releases have brought with them commitments to wire and API stability, Corda 4.4 comes with those same guarantees. States and apps valid in Corda 3.0 are transparently usable in Corda 4.4. +Welcome to the Corda 4.4 release notes. Please read these carefully to understand what’s new in this release and how the features can help you. Just as prior releases have brought with them commitments to wire and API stability, Corda 4.4 comes with those same guarantees. States and apps valid in Corda 3.0 are usable in Corda 4.4. .. _release_notes_v4_4: @@ -21,13 +21,13 @@ Changes for developers in Corda 4.4 Flows API improvements +++++++++++++++++++++++ -Corda 4.4 introduces a new ``FlowLogic.await`` API that allows a CorDapp developers to suspend their flow when executing user-defined long-running operations (e.g. call-outs to external services, long-running DB operations). This prevents these long-running operations from blocking the flow thread, allowing other flows to progress in the interim. Previously, these operations had to be executed synchronously, blocking the flow thread. +Corda 4.4 introduces a new ``FlowLogic.await`` API that allows a CorDapp developer to suspend their flow when executing user-defined long-running operations (e.g. call-outs to external services). This prevents these long-running operations from blocking the flow thread, allowing other flows to progress in the interim. Previously, these operations had to be executed synchronously, blocking the flow thread. The CorDapp developer can decide whether to run these asynchronous flow operations in a dedicated thread pool, or to handle the threading themselves directly. Note that as before, the flow framework suspends automatically for certain operations (e.g. when waiting to receive a message from a counterparty). These suspensions do not have to be triggered explicitly. -The node operator can configure the number of threads to dedicate to external operations. +The node operator can configure the number of threads in the threadpool to dedicate to external operations. Corda 4.4 also introduces a new ``HospitalizeFlowException`` exception type that, when thrown, causes a flow to halt execution and send itself to the flow hospital for observation. The flow will automatically be retried on the next node start. @@ -507,7 +507,7 @@ 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 -Corda 3 are transparently usable in Corda 4. +Corda 3 are usable in Corda 4. 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 From 396a579c8b4dc8a04f8326153c754f7df473ffec Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 18 Feb 2020 09:09:18 +0000 Subject: [PATCH 37/83] NOTICK: Always use configured version of deterministic-rt.jar. (#5979) --- serialization-djvm/build.gradle | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/serialization-djvm/build.gradle b/serialization-djvm/build.gradle index 476c0c8e9b..a082370d25 100644 --- a/serialization-djvm/build.gradle +++ b/serialization-djvm/build.gradle @@ -12,10 +12,7 @@ description 'Serialization support for the DJVM' configurations { sandboxTesting - jdkRt.resolutionStrategy { - // Always check the repository for a newer SNAPSHOT. - cacheChangingModulesFor 0, 'seconds' - } + jdkRt } dependencies { @@ -35,7 +32,7 @@ dependencies { // Test utilities testImplementation "org.assertj:assertj-core:$assertj_version" testRuntimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" - jdkRt "net.corda:deterministic-rt:latest.integration" + jdkRt "net.corda:deterministic-rt:$deterministic_rt_version" // The DJVM will need this classpath to run the unit tests. sandboxTesting files(sourceSets.getByName("test").output) From 17f5bfc53b770ed0a3cf6cbc866f4fc82472f7f3 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 18 Feb 2020 09:10:19 +0000 Subject: [PATCH 38/83] Remove Schedulable states from core-deterministic. (#5975) --- core/src/main/kotlin/net/corda/core/contracts/Structures.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt index 74a1de3149..16d2c8cd85 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt @@ -134,8 +134,7 @@ interface LinearState : ContractState { val linearId: UniqueIdentifier } // DOCEND 2 - -@KeepForDJVM +@DeleteForDJVM interface SchedulableState : ContractState { /** * Indicate whether there is some activity to be performed at some future point in time with respect to this @@ -146,7 +145,6 @@ interface SchedulableState : ContractState { * * @return null if there is no activity to schedule. */ - @DeleteForDJVM fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? } @@ -176,6 +174,7 @@ data class StateAndRef(val state: TransactionState, va // DOCEND 7 /** A wrapper for a [StateAndRef] indicating that it should be added to a transaction as a reference input state. */ +@KeepForDJVM data class ReferencedStateAndRef(val stateAndRef: StateAndRef) /** Filters a list of [StateAndRef] objects according to the type of the states */ From 20d10376105a1d7cc4f1919593ad94eb3463d789 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 18 Feb 2020 09:12:19 +0000 Subject: [PATCH 39/83] NOTICK: Update Gradle scripts for deterministic modules. (#5978) --- core-deterministic/build.gradle | 36 ++++++++++--------- core-deterministic/testing/build.gradle | 24 +++++++------ core-deterministic/testing/data/build.gradle | 20 ++++++----- .../testing/verifier/build.gradle | 12 ++++--- serialization-deterministic/build.gradle | 33 +++++++++-------- serialization-djvm/deserializers/build.gradle | 6 +++- 6 files changed, 77 insertions(+), 54 deletions(-) diff --git a/core-deterministic/build.gradle b/core-deterministic/build.gradle index 5bf7b7afc2..de7c5d9a4d 100644 --- a/core-deterministic/build.gradle +++ b/core-deterministic/build.gradle @@ -1,10 +1,17 @@ -description 'Corda core (deterministic)' +import net.corda.gradle.jarfilter.JarFilterTask +import net.corda.gradle.jarfilter.MetaFixerTask +import proguard.gradle.ProGuardTask +import static org.gradle.api.JavaVersion.VERSION_1_8 -apply from: '../deterministic.gradle' -apply plugin: 'com.jfrog.artifactory' -apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'java-library' -apply plugin: 'idea' +plugins { + id 'net.corda.plugins.publish-utils' + id 'com.jfrog.artifactory' + id 'java-library' + id 'idea' +} +apply from: "${rootProject.projectDir}/deterministic.gradle" + +description 'Corda core (deterministic)' evaluationDependsOn(":core") @@ -39,7 +46,7 @@ dependencies { deterministicLibraries "net.i2p.crypto:eddsa:$eddsa_version" } -jar { +tasks.getByName('jar') { archiveBaseName = 'DOES-NOT-EXIST' // Don't build a jar here because it would be the wrong one. // The jar we really want will be built by the metafix task. @@ -49,7 +56,8 @@ jar { def coreJarTask = tasks.getByPath(':core:jar') def originalJar = coreJarTask.outputs.files.singleFile -task patchCore(type: Zip, dependsOn: coreJarTask) { +def patchCore = tasks.register('patchCore', Zip) { + dependsOn coreJarTask destinationDirectory = file("$buildDir/source-libs") metadataCharset 'UTF-8' archiveClassifier = 'transient' @@ -70,8 +78,7 @@ task patchCore(type: Zip, dependsOn: coreJarTask) { includeEmptyDirs = false } -import proguard.gradle.ProGuardTask -task predeterminise(type: ProGuardTask) { +def predeterminise = tasks.register('predeterminise', ProGuardTask) { injars patchCore outjars file("$buildDir/proguard/pre-deterministic-${project.version}.jar") @@ -104,8 +111,7 @@ task predeterminise(type: ProGuardTask) { keepclassmembers 'class net.corda.core.** { public synthetic ; }' } -import net.corda.gradle.jarfilter.JarFilterTask -task jarFilter(type: JarFilterTask) { +def jarFilter = tasks.register('jarFilter', JarFilterTask) { jars predeterminise annotations { forDelete = [ @@ -158,9 +164,6 @@ task determinise(type: ProGuardTask) { keepclassmembers 'class net.corda.core.** { public synthetic ; }' } -import net.corda.gradle.jarfilter.MetaFixerTask -import static org.gradle.api.JavaVersion.VERSION_1_8 - task metafix(type: MetaFixerTask) { outputDir file("$buildDir/libs") jars determinise @@ -171,7 +174,8 @@ task metafix(type: MetaFixerTask) { } // DOCSTART 01 -task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) { +def checkDeterminism = tasks.register('checkDeterminism', ProGuardTask) { + dependsOn jdkTask injars metafix libraryjars deterministic_rt_jar diff --git a/core-deterministic/testing/build.gradle b/core-deterministic/testing/build.gradle index 2dcc5ea415..b56b4e7615 100644 --- a/core-deterministic/testing/build.gradle +++ b/core-deterministic/testing/build.gradle @@ -1,25 +1,29 @@ -apply plugin: 'kotlin' +plugins { + id 'org.jetbrains.kotlin.jvm' +} dependencies { - testCompile project(path: ':core-deterministic', configuration: 'deterministicArtifacts') - testCompile project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts') - testCompile project(path: ':core-deterministic:testing:verifier', configuration: 'deterministicArtifacts') - testCompile project(path: ':core-deterministic:testing:data', configuration: 'testData') - testCompile(project(':finance:contracts')) { + testImplementation project(path: ':core-deterministic', configuration: 'deterministicArtifacts') + testImplementation project(path: ':serialization-deterministic', configuration: 'deterministicArtifacts') + testImplementation project(path: ':core-deterministic:testing:verifier', configuration: 'deterministicArtifacts') + testImplementation project(path: ':core-deterministic:testing:data', configuration: 'testData') + testImplementation(project(':finance:contracts')) { transitive = false } - testCompile(project(':finance:workflows')) { + testImplementation(project(':finance:workflows')) { transitive = false } testImplementation "org.slf4j:slf4j-api:$slf4j_version" testRuntimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" - testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - testCompile "org.assertj:assertj-core:$assertj_version" + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + testImplementation "org.assertj:assertj-core:$assertj_version" testImplementation "junit:junit:$junit_version" testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}" testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" } // This module has no artifact and only contains tests. -jar.enabled = false +tasks.getByName('jar') { + enabled = false +} diff --git a/core-deterministic/testing/data/build.gradle b/core-deterministic/testing/data/build.gradle index a8525056e3..d4f6ca3bbe 100644 --- a/core-deterministic/testing/data/build.gradle +++ b/core-deterministic/testing/data/build.gradle @@ -1,17 +1,19 @@ -apply plugin: 'kotlin' +plugins { + id 'org.jetbrains.kotlin.jvm' +} configurations { testData } dependencies { - testCompile project(':core') - testCompile project(':finance:workflows') - testCompile project(':node-driver') - testCompile project(path: ':core-deterministic:testing:verifier', configuration: 'runtimeArtifacts') + testImplementation project(':core') + testImplementation project(':finance:workflows') + testImplementation project(':node-driver') + testImplementation project(path: ':core-deterministic:testing:verifier', configuration: 'runtimeArtifacts') - testCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - testCompile "org.jetbrains.kotlin:kotlin-reflect" + testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + testImplementation "org.jetbrains.kotlin:kotlin-reflect" testImplementation "junit:junit:$junit_version" @@ -19,7 +21,9 @@ dependencies { testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" } -jar.enabled = false +tasks.getByName('jar') { + enabled = false +} test { ext { diff --git a/core-deterministic/testing/verifier/build.gradle b/core-deterministic/testing/verifier/build.gradle index 51727d2d3e..774592c6a4 100644 --- a/core-deterministic/testing/verifier/build.gradle +++ b/core-deterministic/testing/verifier/build.gradle @@ -1,8 +1,10 @@ -apply plugin: 'java-library' -apply from: '../../../deterministic.gradle' -apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'com.jfrog.artifactory' -apply plugin: 'idea' +plugins { + id 'java-library' + id 'net.corda.plugins.publish-utils' + id 'com.jfrog.artifactory' + id 'idea' +} +apply from: "${rootProject.projectDir}/deterministic.gradle" description 'Test utilities for deterministic contract verification' diff --git a/serialization-deterministic/build.gradle b/serialization-deterministic/build.gradle index 8bd7dc47a2..0b25629076 100644 --- a/serialization-deterministic/build.gradle +++ b/serialization-deterministic/build.gradle @@ -1,12 +1,17 @@ +import net.corda.gradle.jarfilter.JarFilterTask +import net.corda.gradle.jarfilter.MetaFixerTask +import proguard.gradle.ProGuardTask import static org.gradle.api.JavaVersion.VERSION_1_8 -description 'Corda serialization (deterministic)' +plugins { + id 'net.corda.plugins.publish-utils' + id 'com.jfrog.artifactory' + id 'java-library' + id 'idea' +} +apply from: "${rootProject.projectDir}/deterministic.gradle" -apply from: '../deterministic.gradle' -apply plugin: 'com.jfrog.artifactory' -apply plugin: 'net.corda.plugins.publish-utils' -apply plugin: 'java-library' -apply plugin: 'idea' +description 'Corda serialization (deterministic)' evaluationDependsOn(":serialization") @@ -37,7 +42,7 @@ dependencies { implementation "com.google.guava:guava:$guava_version" } -jar { +tasks.getByName('jar') { archiveBaseName = 'DOES-NOT-EXIST' // Don't build a jar here because it would be the wrong one. // The jar we really want will be built by the metafix task. @@ -47,7 +52,8 @@ jar { def serializationJarTask = tasks.getByPath(':serialization:jar') def originalJar = serializationJarTask.outputs.files.singleFile -task patchSerialization(type: Zip, dependsOn: serializationJarTask) { +def patchSerialization = tasks.register('patchSerialization', Zip) { + dependsOn serializationJarTask destinationDirectory = file("$buildDir/source-libs") metadataCharset 'UTF-8' archiveClassifier = 'transient' @@ -69,8 +75,8 @@ task patchSerialization(type: Zip, dependsOn: serializationJarTask) { includeEmptyDirs = false } -import proguard.gradle.ProGuardTask -task predeterminise(type: ProGuardTask, dependsOn: project(':core-deterministic').assemble) { +def predeterminise = tasks.register('predeterminise', ProGuardTask) { + dependsOn project(':core-deterministic').assemble injars patchSerialization outjars file("$buildDir/proguard/pre-deterministic-${project.version}.jar") @@ -100,8 +106,7 @@ task predeterminise(type: ProGuardTask, dependsOn: project(':core-deterministic' keepclassmembers 'class net.corda.serialization.** { public synthetic ; }' } -import net.corda.gradle.jarfilter.JarFilterTask -task jarFilter(type: JarFilterTask) { +def jarFilter = tasks.register('jarFilter', JarFilterTask) { jars predeterminise annotations { forDelete = [ @@ -148,7 +153,6 @@ task determinise(type: ProGuardTask) { keepclassmembers 'class net.corda.serialization.** { public synthetic ; }' } -import net.corda.gradle.jarfilter.MetaFixerTask task metafix(type: MetaFixerTask) { outputDir file("$buildDir/libs") jars determinise @@ -158,7 +162,8 @@ task metafix(type: MetaFixerTask) { preserveTimestamps = false } -task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) { +def checkDeterminism = tasks.register('checkDeterminism', ProGuardTask) { + dependsOn jdkTask injars metafix libraryjars deterministic_rt_jar diff --git a/serialization-djvm/deserializers/build.gradle b/serialization-djvm/deserializers/build.gradle index 8c19a9c351..9c1b211150 100644 --- a/serialization-djvm/deserializers/build.gradle +++ b/serialization-djvm/deserializers/build.gradle @@ -5,7 +5,7 @@ plugins { id 'java-library' id 'idea' } -apply from: '../../deterministic.gradle' +apply from: "${rootProject.projectDir}/deterministic.gradle" description 'Deserializers for the DJVM' @@ -31,5 +31,9 @@ idea { module { downloadJavadoc = true downloadSources = true + + if (project.hasProperty("deterministic_idea_sdk")) { + jdkName project.property("deterministic_idea_sdk") as String + } } } From ae478cc8ad30d086e6748ba5f35433202d74aeba Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 18 Feb 2020 09:13:50 +0000 Subject: [PATCH 40/83] NOTICK: Add missing test for deserializing Duration for DJVM. (#5971) --- .../djvm/DeserializeDurationTest.kt | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt diff --git a/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt new file mode 100644 index 0000000000..7a2c6a4db8 --- /dev/null +++ b/serialization-djvm/src/test/kotlin/net/corda/serialization/djvm/DeserializeDurationTest.kt @@ -0,0 +1,39 @@ +package net.corda.serialization.djvm + +import net.corda.core.serialization.internal._contextSerializationEnv +import net.corda.core.serialization.serialize +import net.corda.serialization.djvm.SandboxType.KOTLIN +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.fail +import java.time.Duration +import java.util.function.Function + +@ExtendWith(LocalSerialization::class) +class DeserializeDurationTest : TestBase(KOTLIN) { + @Test + fun `test deserializing duration`() { + val duration = Duration.ofSeconds(12345, 6789) + val data = duration.serialize() + + sandbox { + _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) + + val sandboxDuration = data.deserializeFor(classLoader) + + val taskFactory = classLoader.createRawTaskFactory() + val showDuration = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowDuration::class.java) + val result = showDuration.apply(sandboxDuration) ?: fail("Result cannot be null") + + assertEquals(duration.toString(), result.toString()) + assertEquals(SANDBOX_STRING, result::class.java.name) + } + } + + class ShowDuration : Function { + override fun apply(duration: Duration): String { + return duration.toString() + } + } +} From 3eb88bca7dad0c78256d9ba43ca13bdb5439ba2d Mon Sep 17 00:00:00 2001 From: Valerio Campanella Date: Tue, 18 Feb 2020 17:37:41 +0000 Subject: [PATCH 41/83] OS 4.4 release notes: added pargraph on bonus AppServiceHub call, revised section title, removed deprecations section --- docs/source/release-notes.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index df6a62cfd0..ff9c00a567 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -34,14 +34,15 @@ Corda 4.4 also introduces a new ``HospitalizeFlowException`` exception type that This exception gives user code a way to retry a flow from its last checkpoint if a known intermittent failure occurred. -Diagnostic API +New utility APIs +++++++++++++++++++++++ -Corda 4.4 introduces a ``ServiceHub`` call available to CorDapp developers that allows them to access: +Corda 4.4 introduces a new call (``ServiceHub.DiagnosticsService``) available to CorDapp developers that allows them to access: * The edition of Corda being run (e.g. Open Source, Enterprise) * The version of Corda being run including the patch number (eg. 3.2.20190215) +Corda 4.4 also provides a callback (``AppServiceHub.register``) to allow Corda services to register custom actions to be performed once the node is fully started-up. This pattern prevents issues caused by the service trying to immediately access a part of the node that hadn't yet been initialised . Security enhancements +++++++++++++++++++++++ @@ -59,10 +60,6 @@ Given the addition of new APIs, the platform version of Corda 4.4 has been bumpe For more information on platform version, please see :doc:`versioning`. For more details on upgrading a CorDapp to use platform version 5, please see :doc:`app-upgrade-notes`. -Deprecations -~~~~~~~~~~~~ - - Issues Fixed ~~~~~~~~~~~~ From d89ce6608add763f7a448a18b4fff1abac03621c Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 5 Feb 2020 10:46:42 +0000 Subject: [PATCH 42/83] Create a unit test for serialization whitelists via driver. --- .../corda/core/internal/ClassLoadingUtils.kt | 1 + .../whitelist/WhitelistContract.kt | 38 ++++++++ .../serialization/whitelist/WhitelistData.kt | 15 +++ .../djvm/attachment/SandboxAttachmentFlow.kt | 2 - .../flows/djvm/broken/NonDeterministicFlow.kt | 2 - .../djvm/crypto/DeterministicCryptoFlow.kt | 2 - .../whitelist/DeterministicWhitelistFlow.kt | 2 - .../net/corda/flows/fixup/CordappFixupFlow.kt | 2 - .../custom/CustomSerializerFlow.kt | 2 - .../missing/MissingSerializerBuilderFlow.kt | 2 - .../missing/MissingSerializerFlow.kt | 2 - .../serialization/whitelist/WhitelistFlow.kt | 26 +++++ .../ContractWithSerializationWhitelistTest.kt | 96 +++++++++++++++++++ .../cordapp/JarScanningCordappLoader.kt | 31 +++++- .../testing/node/internal/DriverDSLImpl.kt | 21 ++-- 15 files changed, 212 insertions(+), 32 deletions(-) create mode 100644 node/src/integration-test/kotlin/net/corda/contracts/serialization/whitelist/WhitelistContract.kt create mode 100644 node/src/integration-test/kotlin/net/corda/contracts/serialization/whitelist/WhitelistData.kt create mode 100644 node/src/integration-test/kotlin/net/corda/flows/serialization/whitelist/WhitelistFlow.kt create mode 100644 node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt diff --git a/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt index fbe359c55d..72070a46ed 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt @@ -36,6 +36,7 @@ fun getNamesOfClassesImplementing(classloader: ClassLoader, clazz: Clas return ClassGraph().overrideClassLoaders(classloader) .enableURLScheme(attachmentScheme) .ignoreParentClassLoaders() + .disableDirScanning() .enableClassInfo() .pooledScan() .use { result -> diff --git a/node/src/integration-test/kotlin/net/corda/contracts/serialization/whitelist/WhitelistContract.kt b/node/src/integration-test/kotlin/net/corda/contracts/serialization/whitelist/WhitelistContract.kt new file mode 100644 index 0000000000..b67d2134c9 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/contracts/serialization/whitelist/WhitelistContract.kt @@ -0,0 +1,38 @@ +package net.corda.contracts.serialization.whitelist + +import net.corda.core.contracts.CommandData +import net.corda.core.contracts.Contract +import net.corda.core.contracts.ContractState +import net.corda.core.identity.AbstractParty +import net.corda.core.transactions.LedgerTransaction + +class WhitelistContract : Contract { + companion object { + const val MAX_VALUE = 2000L + } + + override fun verify(tx: LedgerTransaction) { + val states = tx.outputsOfType() + require(states.isNotEmpty()) { + "Requires at least one data state" + } + + states.forEach { + require(it.whitelistData in WhitelistData(0)..WhitelistData(MAX_VALUE)) { + "WhitelistData $it exceeds maximum value!" + } + } + } + + @Suppress("CanBeParameter", "MemberVisibilityCanBePrivate") + class State(val owner: AbstractParty, val whitelistData: WhitelistData) : ContractState { + override val participants: List = listOf(owner) + + @Override + override fun toString(): String { + return whitelistData.toString() + } + } + + class Operate : CommandData +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/contracts/serialization/whitelist/WhitelistData.kt b/node/src/integration-test/kotlin/net/corda/contracts/serialization/whitelist/WhitelistData.kt new file mode 100644 index 0000000000..42c3e1e5c7 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/contracts/serialization/whitelist/WhitelistData.kt @@ -0,0 +1,15 @@ +package net.corda.contracts.serialization.whitelist + +import net.corda.core.serialization.SerializationWhitelist + +data class WhitelistData(val value: Long) : Comparable { + override fun compareTo(other: WhitelistData): Int { + return value.compareTo(other.value) + } + + override fun toString(): String = "$value things" +} + +class Whitelist : SerializationWhitelist { + override val whitelist = listOf(WhitelistData::class.java) +} diff --git a/node/src/integration-test/kotlin/net/corda/flows/djvm/attachment/SandboxAttachmentFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/djvm/attachment/SandboxAttachmentFlow.kt index e51313602e..ae277dfda2 100644 --- a/node/src/integration-test/kotlin/net/corda/flows/djvm/attachment/SandboxAttachmentFlow.kt +++ b/node/src/integration-test/kotlin/net/corda/flows/djvm/attachment/SandboxAttachmentFlow.kt @@ -6,11 +6,9 @@ import net.corda.core.contracts.Command import net.corda.core.contracts.CommandData import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.transactions.TransactionBuilder -@InitiatingFlow @StartableByRPC class SandboxAttachmentFlow(private val command: CommandData) : FlowLogic() { @Suspendable diff --git a/node/src/integration-test/kotlin/net/corda/flows/djvm/broken/NonDeterministicFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/djvm/broken/NonDeterministicFlow.kt index ca9ddad1ef..076f17bdff 100644 --- a/node/src/integration-test/kotlin/net/corda/flows/djvm/broken/NonDeterministicFlow.kt +++ b/node/src/integration-test/kotlin/net/corda/flows/djvm/broken/NonDeterministicFlow.kt @@ -6,11 +6,9 @@ import net.corda.core.contracts.Command import net.corda.core.contracts.TypeOnlyCommandData import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.transactions.TransactionBuilder -@InitiatingFlow @StartableByRPC class NonDeterministicFlow(private val trouble: TypeOnlyCommandData) : FlowLogic() { @Suspendable diff --git a/node/src/integration-test/kotlin/net/corda/flows/djvm/crypto/DeterministicCryptoFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/djvm/crypto/DeterministicCryptoFlow.kt index 18fb73c380..65e0065eb1 100644 --- a/node/src/integration-test/kotlin/net/corda/flows/djvm/crypto/DeterministicCryptoFlow.kt +++ b/node/src/integration-test/kotlin/net/corda/flows/djvm/crypto/DeterministicCryptoFlow.kt @@ -6,12 +6,10 @@ import net.corda.core.contracts.Command import net.corda.core.contracts.CommandData import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.OpaqueBytes -@InitiatingFlow @StartableByRPC class DeterministicCryptoFlow( private val command: CommandData, diff --git a/node/src/integration-test/kotlin/net/corda/flows/djvm/whitelist/DeterministicWhitelistFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/djvm/whitelist/DeterministicWhitelistFlow.kt index 8a5573f030..fe8824e03c 100644 --- a/node/src/integration-test/kotlin/net/corda/flows/djvm/whitelist/DeterministicWhitelistFlow.kt +++ b/node/src/integration-test/kotlin/net/corda/flows/djvm/whitelist/DeterministicWhitelistFlow.kt @@ -7,11 +7,9 @@ import net.corda.contracts.djvm.whitelist.WhitelistData import net.corda.core.contracts.Command import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.transactions.TransactionBuilder -@InitiatingFlow @StartableByRPC class DeterministicWhitelistFlow(private val data: WhitelistData) : FlowLogic() { @Suspendable diff --git a/node/src/integration-test/kotlin/net/corda/flows/fixup/CordappFixupFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/fixup/CordappFixupFlow.kt index c6731ecd50..d35c556dc9 100644 --- a/node/src/integration-test/kotlin/net/corda/flows/fixup/CordappFixupFlow.kt +++ b/node/src/integration-test/kotlin/net/corda/flows/fixup/CordappFixupFlow.kt @@ -7,11 +7,9 @@ import net.corda.contracts.fixup.dependent.DependentData import net.corda.core.contracts.Command import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.transactions.TransactionBuilder -@InitiatingFlow @StartableByRPC class CordappFixupFlow(private val data: DependentData) : FlowLogic() { @Suspendable diff --git a/node/src/integration-test/kotlin/net/corda/flows/serialization/custom/CustomSerializerFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/serialization/custom/CustomSerializerFlow.kt index e3491748e6..4435418bf9 100644 --- a/node/src/integration-test/kotlin/net/corda/flows/serialization/custom/CustomSerializerFlow.kt +++ b/node/src/integration-test/kotlin/net/corda/flows/serialization/custom/CustomSerializerFlow.kt @@ -7,11 +7,9 @@ import net.corda.contracts.serialization.custom.CustomSerializerContract.Purchas import net.corda.core.contracts.Command import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.transactions.TransactionBuilder -@InitiatingFlow @StartableByRPC class CustomSerializerFlow( private val purchase: Currantsy diff --git a/node/src/integration-test/kotlin/net/corda/flows/serialization/missing/MissingSerializerBuilderFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/serialization/missing/MissingSerializerBuilderFlow.kt index 8353ee6bc5..b5ad46d01a 100644 --- a/node/src/integration-test/kotlin/net/corda/flows/serialization/missing/MissingSerializerBuilderFlow.kt +++ b/node/src/integration-test/kotlin/net/corda/flows/serialization/missing/MissingSerializerBuilderFlow.kt @@ -7,11 +7,9 @@ import net.corda.contracts.serialization.missing.MissingSerializerContract.Opera import net.corda.core.contracts.Command import net.corda.core.crypto.SecureHash import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.transactions.TransactionBuilder -@InitiatingFlow @StartableByRPC class MissingSerializerBuilderFlow(private val value: Long) : FlowLogic() { @Suspendable diff --git a/node/src/integration-test/kotlin/net/corda/flows/serialization/missing/MissingSerializerFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/serialization/missing/MissingSerializerFlow.kt index 8f38f85f72..6bef6b071f 100644 --- a/node/src/integration-test/kotlin/net/corda/flows/serialization/missing/MissingSerializerFlow.kt +++ b/node/src/integration-test/kotlin/net/corda/flows/serialization/missing/MissingSerializerFlow.kt @@ -12,14 +12,12 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SignableData import net.corda.core.crypto.SignatureMetadata import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC import net.corda.core.internal.createComponentGroups import net.corda.core.internal.requiredContractClassName import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction -@InitiatingFlow @StartableByRPC class MissingSerializerFlow(private val value: Long) : FlowLogic() { @Suspendable diff --git a/node/src/integration-test/kotlin/net/corda/flows/serialization/whitelist/WhitelistFlow.kt b/node/src/integration-test/kotlin/net/corda/flows/serialization/whitelist/WhitelistFlow.kt new file mode 100644 index 0000000000..e623d7a00e --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/flows/serialization/whitelist/WhitelistFlow.kt @@ -0,0 +1,26 @@ +package net.corda.flows.serialization.whitelist + +import co.paralleluniverse.fibers.Suspendable +import net.corda.contracts.serialization.whitelist.WhitelistContract +import net.corda.contracts.serialization.whitelist.WhitelistContract.State +import net.corda.contracts.serialization.whitelist.WhitelistData +import net.corda.core.contracts.Command +import net.corda.core.crypto.SecureHash +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.StartableByRPC +import net.corda.core.transactions.TransactionBuilder + +@StartableByRPC +class WhitelistFlow(private val data: WhitelistData) : FlowLogic() { + @Suspendable + override fun call(): SecureHash { + val notary = serviceHub.networkMapCache.notaryIdentities[0] + val stx = serviceHub.signInitialTransaction( + TransactionBuilder(notary) + .addOutputState(State(ourIdentity, data)) + .addCommand(Command(WhitelistContract.Operate(), ourIdentity.owningKey)) + ) + stx.verify(serviceHub, checkSufficientSignatures = false) + return stx.id + } +} diff --git a/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt b/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt new file mode 100644 index 0000000000..e031a046ab --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt @@ -0,0 +1,96 @@ +package net.corda.node + +import net.corda.client.rpc.CordaRPCClient +import net.corda.contracts.serialization.whitelist.WhitelistData +import net.corda.core.contracts.TransactionVerificationException.ContractRejection +import net.corda.core.messaging.startFlow +import net.corda.core.utilities.getOrThrow +import net.corda.flows.serialization.whitelist.WhitelistFlow +import net.corda.node.services.Permissions +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.driver +import net.corda.testing.driver.internal.incrementalPortAllocation +import net.corda.testing.node.NotarySpec +import net.corda.testing.node.User +import net.corda.testing.node.internal.cordappWithPackages +import org.assertj.core.api.Assertions.assertThat +import org.junit.BeforeClass +import org.junit.Test +import kotlin.test.assertFailsWith + +@Suppress("FunctionName") +class ContractWithSerializationWhitelistTest { + companion object { + const val DATA = 123456L + + @JvmField + val contractCordapp = cordappWithPackages("net.corda.contracts.serialization.whitelist").signed() + + @JvmField + val workflowCordapp = cordappWithPackages("net.corda.flows.serialization.whitelist").signed() + + fun parametersFor(runInProcess: Boolean): DriverParameters { + return DriverParameters( + portAllocation = incrementalPortAllocation(), + startNodesInProcess = runInProcess, + notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)), + cordappsForAllNodes = listOf(contractCordapp, workflowCordapp) + ) + } + + @BeforeClass + @JvmStatic + fun checkData() { + assertNotCordaSerializable() + } + } + + @Test + fun `test serialization whitelist out-of-process`() { + val user = User("u", "p", setOf(Permissions.all())) + driver(parametersFor(runInProcess = false)) { + val badData = WhitelistData(DATA) + val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + val ex = assertFailsWith { + CordaRPCClient(hostAndPort = alice.rpcAddress) + .start(user.username, user.password) + .use { client -> + client.proxy.startFlow(::WhitelistFlow, badData) + .returnValue + .getOrThrow() + } + } + assertThat(ex) + .hasMessageContaining("WhitelistData $badData exceeds maximum value!") + } + } + + @Test + fun `test serialization whitelist in-process`() { + val user = User("u", "p", setOf(Permissions.all())) + driver(parametersFor(runInProcess = true)) { + val badData = WhitelistData(DATA) + val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + val ex = assertFailsWith { + CordaRPCClient(hostAndPort = alice.rpcAddress) + .start(user.username, user.password) + .use { client -> + client.proxy.startFlow(::WhitelistFlow, badData) + .returnValue + .getOrThrow() + } + } + assertThat(ex) + .hasMessageContaining("WhitelistData $badData exceeds maximum value!") + } + } + +// @Test +// fun `test serialization whitelist in-process`() { +// assertFailsWith { +// driver(parametersFor(runInProcess = true)) {} +// } +// } +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt index 345876741e..ce2a6544fe 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt @@ -219,7 +219,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: // present in the CorDapp. val result = scanResult.getClassesWithSuperclass(NotaryService::class) + scanResult.getClassesWithSuperclass(SinglePartyNotaryService::class) - if(!result.isEmpty()) { + if (result.isNotEmpty()) { logger.info("Found notary service CorDapp implementations: " + result.joinToString(", ")) } return result.firstOrNull() @@ -270,10 +270,26 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: private fun findWhitelists(cordappJarPath: RestrictedURL): List { val whitelists = URLClassLoader(arrayOf(cordappJarPath.url)).use { ServiceLoader.load(SerializationWhitelist::class.java, it).toList() + }.filter { + it.javaClass.name.startsWith(cordappJarPath.qualifiedNamePrefix) && it.javaClass.location == cordappJarPath.url } - return whitelists.filter { - it.javaClass.location == cordappJarPath.url && it.javaClass.name.startsWith(cordappJarPath.qualifiedNamePrefix) - } + DefaultWhitelist // Always add the DefaultWhitelist to the whitelist for an app. + + whitelists.filterNot { + it.javaClass.location == cordappJarPath.url + }.apply { + if (isNotEmpty()) { + throw NotCordappWhitelist("Whitelists ${showClasses(this)} not found within ${cordappJarPath.url}") + } + } + return whitelists + DefaultWhitelist // Always add the DefaultWhitelist to the whitelist for an app. + } + + private fun showClasses(items: Iterable): String { + return items.map { + it::class.java + }.map { + "${it.name} in ${it.protectionDomain.codeSource.location}" + }.toString() } private fun findSerializers(scanResult: RestrictedScanResult): List> { @@ -381,6 +397,13 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: */ class MultipleCordappsForFlowException(message: String) : Exception(message) +/** + * Thrown when a [SerializationWhitelist] is loaded from outside the CorDapp. + * Most likely because you are testing with node-driver and "in-process" nodes. + * Try using "out-of-process" driver nodes instead. + */ +class NotCordappWhitelist(message: String) : Exception(message) + /** * Thrown if an exception occurs whilst parsing version identifiers within cordapp configuration */ 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 cacaaac303..48c602d359 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 @@ -142,11 +142,7 @@ class DriverDSLImpl( private lateinit var _notaries: CordaFuture> override val notaryHandles: List get() = _notaries.getOrThrow() - override val cordappsClassLoader: ClassLoader? = if (!startNodesInProcess) { - createCordappsClassLoader(cordappsForAllNodes) - } else { - null - } + override val cordappsClassLoader: URLClassLoader? = createCordappsClassLoader(cordappsForAllNodes) interface Waitable { @Throws(InterruptedException::class) @@ -195,14 +191,15 @@ class DriverDSLImpl( } override fun shutdown() { - if (waitForAllNodesToFinish) { - state.locked { - processes.forEach { it.waitFor() } + cordappsClassLoader.use { _ -> + if (waitForAllNodesToFinish) { + state.locked { + processes.forEach { it.waitFor() } + } } + _shutdownManager?.shutdown() + _executorService?.shutdownNow() } - _shutdownManager?.shutdown() - _executorService?.shutdownNow() - (cordappsClassLoader as? AutoCloseable)?.close() } private fun establishRpc(config: NodeConfig, processDeathFuture: CordaFuture): CordaFuture { @@ -992,7 +989,7 @@ class DriverDSLImpl( return config } - private fun createCordappsClassLoader(cordapps: Collection?): ClassLoader? { + private fun createCordappsClassLoader(cordapps: Collection?): URLClassLoader? { if (cordapps == null || cordapps.isEmpty()) { return null } From afcab51fe3dee766831cd912d08b5429fbf0d3ab Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 11 Feb 2020 22:59:53 +0000 Subject: [PATCH 43/83] Update "in-process" test on basis it can be made to work. --- .../corda/core/internal/ClassLoadingUtils.kt | 1 - .../ContractWithSerializationWhitelistTest.kt | 51 +++++++------------ .../cordapp/JarScanningCordappLoader.kt | 29 ++--------- 3 files changed, 22 insertions(+), 59 deletions(-) diff --git a/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt b/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt index 72070a46ed..fbe359c55d 100644 --- a/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/ClassLoadingUtils.kt @@ -36,7 +36,6 @@ fun getNamesOfClassesImplementing(classloader: ClassLoader, clazz: Clas return ClassGraph().overrideClassLoaders(classloader) .enableURLScheme(attachmentScheme) .ignoreParentClassLoaders() - .disableDirScanning() .enableClassInfo() .pooledScan() .use { result -> diff --git a/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt b/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt index e031a046ab..2a9ae80195 100644 --- a/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/ContractWithSerializationWhitelistTest.kt @@ -5,6 +5,7 @@ import net.corda.contracts.serialization.whitelist.WhitelistData import net.corda.core.contracts.TransactionVerificationException.ContractRejection import net.corda.core.messaging.startFlow import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.loggerFor import net.corda.flows.serialization.whitelist.WhitelistFlow import net.corda.node.services.Permissions import net.corda.testing.core.ALICE_NAME @@ -18,13 +19,20 @@ import net.corda.testing.node.internal.cordappWithPackages import org.assertj.core.api.Assertions.assertThat import org.junit.BeforeClass import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameters import kotlin.test.assertFailsWith +@RunWith(Parameterized::class) @Suppress("FunctionName") -class ContractWithSerializationWhitelistTest { +class ContractWithSerializationWhitelistTest(private val runInProcess: Boolean) { companion object { const val DATA = 123456L + @JvmField + val logger = loggerFor() + @JvmField val contractCordapp = cordappWithPackages("net.corda.contracts.serialization.whitelist").signed() @@ -40,6 +48,10 @@ class ContractWithSerializationWhitelistTest { ) } + @Parameters + @JvmStatic + fun modes(): List> = listOf(Array(1) { true }, Array(1) { false }) + @BeforeClass @JvmStatic fun checkData() { @@ -47,10 +59,12 @@ class ContractWithSerializationWhitelistTest { } } - @Test - fun `test serialization whitelist out-of-process`() { + @Test(timeout = 300_000) + fun `test serialization whitelist`() { + logger.info("RUN-IN-PROCESS=$runInProcess") + val user = User("u", "p", setOf(Permissions.all())) - driver(parametersFor(runInProcess = false)) { + driver(parametersFor(runInProcess = runInProcess)) { val badData = WhitelistData(DATA) val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() val ex = assertFailsWith { @@ -66,31 +80,4 @@ class ContractWithSerializationWhitelistTest { .hasMessageContaining("WhitelistData $badData exceeds maximum value!") } } - - @Test - fun `test serialization whitelist in-process`() { - val user = User("u", "p", setOf(Permissions.all())) - driver(parametersFor(runInProcess = true)) { - val badData = WhitelistData(DATA) - val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() - val ex = assertFailsWith { - CordaRPCClient(hostAndPort = alice.rpcAddress) - .start(user.username, user.password) - .use { client -> - client.proxy.startFlow(::WhitelistFlow, badData) - .returnValue - .getOrThrow() - } - } - assertThat(ex) - .hasMessageContaining("WhitelistData $badData exceeds maximum value!") - } - } - -// @Test -// fun `test serialization whitelist in-process`() { -// assertFailsWith { -// driver(parametersFor(runInProcess = true)) {} -// } -// } -} \ No newline at end of file +} diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt index ce2a6544fe..0eba7d18ad 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt @@ -270,26 +270,10 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: private fun findWhitelists(cordappJarPath: RestrictedURL): List { val whitelists = URLClassLoader(arrayOf(cordappJarPath.url)).use { ServiceLoader.load(SerializationWhitelist::class.java, it).toList() - }.filter { - it.javaClass.name.startsWith(cordappJarPath.qualifiedNamePrefix) && it.javaClass.location == cordappJarPath.url } - - whitelists.filterNot { - it.javaClass.location == cordappJarPath.url - }.apply { - if (isNotEmpty()) { - throw NotCordappWhitelist("Whitelists ${showClasses(this)} not found within ${cordappJarPath.url}") - } - } - return whitelists + DefaultWhitelist // Always add the DefaultWhitelist to the whitelist for an app. - } - - private fun showClasses(items: Iterable): String { - return items.map { - it::class.java - }.map { - "${it.name} in ${it.protectionDomain.codeSource.location}" - }.toString() + return whitelists.filter { + it.javaClass.location == cordappJarPath.url && it.javaClass.name.startsWith(cordappJarPath.qualifiedNamePrefix) + } + DefaultWhitelist // Always add the DefaultWhitelist to the whitelist for an app. } private fun findSerializers(scanResult: RestrictedScanResult): List> { @@ -397,13 +381,6 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: */ class MultipleCordappsForFlowException(message: String) : Exception(message) -/** - * Thrown when a [SerializationWhitelist] is loaded from outside the CorDapp. - * Most likely because you are testing with node-driver and "in-process" nodes. - * Try using "out-of-process" driver nodes instead. - */ -class NotCordappWhitelist(message: String) : Exception(message) - /** * Thrown if an exception occurs whilst parsing version identifiers within cordapp configuration */ From e9c2af661ddccd9cb6334594cb1f0f6a6acadad7 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 19 Feb 2020 15:26:01 +0000 Subject: [PATCH 44/83] NOTICK: Tidy up driver shutdown so that resources are always released on error. --- .../testing/node/internal/DriverDSLImpl.kt | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) 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 48c602d359..b7e5c9123f 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 @@ -1129,18 +1129,18 @@ fun genericDriver( coerce: (D) -> DI, dsl: DI.() -> A ): A { - val serializationEnv = setDriverSerialization(driverDsl.cordappsClassLoader) - val shutdownHook = addShutdownHook(driverDsl::shutdown) - try { - driverDsl.start() - return dsl(coerce(driverDsl)) - } catch (exception: Throwable) { - DriverDSLImpl.log.error("Driver shutting down because of exception", exception) - throw exception - } finally { - driverDsl.shutdown() - shutdownHook.cancel() - serializationEnv?.close() + setDriverSerialization(driverDsl.cordappsClassLoader).use { _ -> + val shutdownHook = addShutdownHook(driverDsl::shutdown) + try { + driverDsl.start() + return dsl(coerce(driverDsl)) + } catch (exception: Throwable) { + DriverDSLImpl.log.error("Driver shutting down because of exception", exception) + throw exception + } finally { + driverDsl.shutdown() + shutdownHook.cancel() + } } } @@ -1157,8 +1157,8 @@ fun genericDriver( driverDslWrapper: (DriverDSLImpl) -> D, coerce: (D) -> DI, dsl: DI.() -> A ): A { - val serializationEnv = setDriverSerialization() - val driverDsl = driverDslWrapper( + setDriverSerialization().use { _ -> + val driverDsl = driverDslWrapper( DriverDSLImpl( portAllocation = defaultParameters.portAllocation, debugPortAllocation = defaultParameters.debugPortAllocation, @@ -1180,18 +1180,18 @@ fun genericDriver( djvmCordaSource = defaultParameters.djvmCordaSource, environmentVariables = defaultParameters.environmentVariables ) - ) - val shutdownHook = addShutdownHook(driverDsl::shutdown) - try { - driverDsl.start() - return dsl(coerce(driverDsl)) - } catch (exception: Throwable) { - DriverDSLImpl.log.error("Driver shutting down because of exception", exception) - throw exception - } finally { - driverDsl.shutdown() - shutdownHook.cancel() - serializationEnv?.close() + ) + val shutdownHook = addShutdownHook(driverDsl::shutdown) + try { + driverDsl.start() + return dsl(coerce(driverDsl)) + } catch (exception: Throwable) { + DriverDSLImpl.log.error("Driver shutting down because of exception", exception) + throw exception + } finally { + driverDsl.shutdown() + shutdownHook.cancel() + } } } From b3ca72041228bf812270f82cb03b62a6c34e7909 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Thu, 20 Feb 2020 17:01:24 +0000 Subject: [PATCH 45/83] NOTICK: Fix more Gradle technical debt. (#5989) * NOTICK: Use Gradle's "lazy" task API. * Make Java8 vs Java11 check consisent. --- build.gradle | 32 ++++++++------------ core-deterministic/build.gradle | 3 +- core-deterministic/testing/build.gradle | 2 +- core-deterministic/testing/data/build.gradle | 2 +- serialization-deterministic/build.gradle | 3 +- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/build.gradle b/build.gradle index 23b522603a..3db5d81af9 100644 --- a/build.gradle +++ b/build.gradle @@ -25,14 +25,14 @@ buildscript { ext.quasar_group = 'co.paralleluniverse' // Set version of Quasar according to version of Java used: - if (JavaVersion.current() == JavaVersion.VERSION_11) { - ext.quasar_version = constants.getProperty("quasarVersion11") - ext.quasar_classifier = constants.getProperty("quasarClassifier11") - ext.jdkClassifier = constants.getProperty("jdkClassifier11") - } else { + if (JavaVersion.current().isJava8()) { ext.quasar_version = constants.getProperty("quasarVersion") ext.quasar_classifier = constants.getProperty("quasarClassifier") ext.jdkClassifier = constants.getProperty("jdkClassifier") + } else { + ext.quasar_version = constants.getProperty("quasarVersion11") + ext.quasar_classifier = constants.getProperty("quasarClassifier11") + ext.jdkClassifier = constants.getProperty("jdkClassifier11") } ext.cordaScanApiClassifier = jdkClassifier ext.quasar_exclusions = [ @@ -126,13 +126,13 @@ buildscript { ext.commons_io_version = '2.6' ext.controlsfx_version = '8.40.15' ext.detekt_version = constants.getProperty('detektVersion') - if (JavaVersion.current() == JavaVersion.VERSION_11) { + if (JavaVersion.current().isJava8()) { + ext.fontawesomefx_commons_version = '8.15' + ext.fontawesomefx_fontawesome_version = '4.7.0-5' + } else { // has been compiled by a more recent version of the Java Runtime (class file version 55.0) ext.fontawesomefx_commons_version = '11.0' ext.fontawesomefx_fontawesome_version = '4.7.0-11' - } else { - ext.fontawesomefx_commons_version = '8.15' - ext.fontawesomefx_fontawesome_version = '4.7.0-5' } // Name of the IntelliJ SDK created for the deterministic Java rt.jar. @@ -225,11 +225,7 @@ apply plugin: 'java' logger.lifecycle("Java version: {}", JavaVersion.current()) sourceCompatibility = VERSION_1_8 -if (JavaVersion.current() == JavaVersion.VERSION_1_8) { - targetCompatibility = VERSION_1_8 -} else { - targetCompatibility = VERSION_11 -} +targetCompatibility = JavaVersion.current().isJava8() ? VERSION_1_8 : VERSION_11 logger.lifecycle("Java source compatibility: {}", sourceCompatibility) logger.lifecycle("Java target compatibility: {}", targetCompatibility) logger.lifecycle("Quasar version: {}", quasar_version) @@ -265,11 +261,7 @@ allprojects { } } sourceCompatibility = VERSION_1_8 - if (JavaVersion.current() == JavaVersion.VERSION_1_8) { - targetCompatibility = VERSION_1_8 - } else { - targetCompatibility = VERSION_11 - } + targetCompatibility = JavaVersion.current().isJava8() ? VERSION_1_8 : VERSION_11 jacoco { // JDK11 official support (https://github.com/jacoco/jacoco/releases/tag/v0.8.3) @@ -292,7 +284,7 @@ allprojects { kotlinOptions { languageVersion = "1.2" apiVersion = "1.2" - jvmTarget = "1.8" + jvmTarget = VERSION_1_8 javaParameters = true // Useful for reflection. freeCompilerArgs = ['-Xjvm-default=compatibility'] allWarningsAsErrors = warnings_as_errors diff --git a/core-deterministic/build.gradle b/core-deterministic/build.gradle index de7c5d9a4d..73209915d2 100644 --- a/core-deterministic/build.gradle +++ b/core-deterministic/build.gradle @@ -4,6 +4,7 @@ import proguard.gradle.ProGuardTask import static org.gradle.api.JavaVersion.VERSION_1_8 plugins { + id 'org.jetbrains.kotlin.jvm' id 'net.corda.plugins.publish-utils' id 'com.jfrog.artifactory' id 'java-library' @@ -46,7 +47,7 @@ dependencies { deterministicLibraries "net.i2p.crypto:eddsa:$eddsa_version" } -tasks.getByName('jar') { +tasks.named('jar', Jar) { archiveBaseName = 'DOES-NOT-EXIST' // Don't build a jar here because it would be the wrong one. // The jar we really want will be built by the metafix task. diff --git a/core-deterministic/testing/build.gradle b/core-deterministic/testing/build.gradle index b56b4e7615..61abf977af 100644 --- a/core-deterministic/testing/build.gradle +++ b/core-deterministic/testing/build.gradle @@ -24,6 +24,6 @@ dependencies { } // This module has no artifact and only contains tests. -tasks.getByName('jar') { +tasks.named('jar', Jar) { enabled = false } diff --git a/core-deterministic/testing/data/build.gradle b/core-deterministic/testing/data/build.gradle index d4f6ca3bbe..912f7d7e35 100644 --- a/core-deterministic/testing/data/build.gradle +++ b/core-deterministic/testing/data/build.gradle @@ -21,7 +21,7 @@ dependencies { testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" } -tasks.getByName('jar') { +tasks.named('jar', Jar) { enabled = false } diff --git a/serialization-deterministic/build.gradle b/serialization-deterministic/build.gradle index 0b25629076..773522460d 100644 --- a/serialization-deterministic/build.gradle +++ b/serialization-deterministic/build.gradle @@ -4,6 +4,7 @@ import proguard.gradle.ProGuardTask import static org.gradle.api.JavaVersion.VERSION_1_8 plugins { + id 'org.jetbrains.kotlin.jvm' id 'net.corda.plugins.publish-utils' id 'com.jfrog.artifactory' id 'java-library' @@ -42,7 +43,7 @@ dependencies { implementation "com.google.guava:guava:$guava_version" } -tasks.getByName('jar') { +tasks.named('jar', Jar) { archiveBaseName = 'DOES-NOT-EXIST' // Don't build a jar here because it would be the wrong one. // The jar we really want will be built by the metafix task. From 2c9c2985c021502433ac659bf0a795824a174581 Mon Sep 17 00:00:00 2001 From: Kyriakos Tharrouniatis Date: Fri, 21 Feb 2020 10:02:34 +0000 Subject: [PATCH 46/83] CORDA-3381: Errors in vault updates publisher are unsubscribing stopping observers from working (#5912) * Throw SQLException or PersistenceException plain, that may come out of an unsafe subscriber * Add explanatory comment about why we changed Observer.tee to use unsafe subscribe * Introducing not unsubscribing version of Rx.Subscriber * Wrap PublishSubjects with FlowSafeSubjects in all tests that test Observer.tee * Minor code formatting * Make rawUpdates Rx.Observers not unsubscribe when accessed from CordaServices - Do not allow rawUpdates subscribing from flows * Warning fix: Add else block to when statement * Revert "Wrap PublishSubjects with FlowSafeSubjects in all tests that test Observer.tee" This reverts commit e419af86 * Correcting log message * Improve log message * Add fiber's id to log message and exception message * Added test, asserting FlowSafeSubscriber is alive and re-accessed upon flow retry * Logging flow name instead of flow id at VaultService.rawUpdates subscribing error * Add kdoc to OnNextFailedException * Minor text correction * Update kdocs of FlowSafeSubject/ PreventSubscriptionsSubject * Moved FlowSafeSubject under package node.internal as it is only used by NodeVaultService * Add comment and update kdoc explaining how to subscribe with SafeSubscriber to FlowSafeSubject * Change PreventSubscriptionsSubject#errorAction to be more specific; to return an Exception * Minor text update * Update messy comment * Replace assertThat with assertEquals * Splitting heartBeat to heartBeat1 and hearBeat2 for more clear asserting * Correcting comment * Update messy comment * Splitting heartBeat into heartBeatOnNext and heartBeatOnError * Update test name * Add explanatory comment to test * Update test name * Update test and add test comment * Moving NotarisedTxs from SendStateFlow to VaultObserverExceptionTest inside NodeHandle.getNotarisedTransactionIds * Moving SubscribingRawUpdatesFlow from ErrorHandling to VaultObserverExceptionTest * Update kdoc of FlowSafeSubscriber and FlowSafeSubscriber.onNext * Make kdoc more clear * Throw exception upon accessing VaultService.rawUpdates from within a flow * Changing exception thrown when accessing VaultService.rawUpdates from within a flow to a CordaRuntimeException * Minor kdoc update * Update test comment * Update kdoc of FlowSafeSubscriber * Introducing Observable.flowSafeSubscribe public API method to subscribe with -non unsubscribing- Rx.Subscribers to Observables. It also replaced FlowSafeSubject * Move CustomSafeSubscriber outside test methods * Minor text update * Add timeout to tests * Update kdoc of flowSafeSubscribe * Update kdoc of flowSafeSubscribe * Update kdoc of flowSafeSubscribe * Move FlowSafeSubscriber and flowSafeSubscribe under their own package * Fix detekt issue * Update Detekt baseline * Revert "Update Detekt baseline" This reverts commit 793a8ed9 * Fix Detekt issue * Moved strictMode flag from flowSafeSubscribe to OnFlowSafeSubscribe Moved OnFlowSafeSubscribe into internal package Integration tested flowSafeLooseSubscribe * Suppress Rx Deprecation * Rename flowSafeSubscribe to flowSafeObservable * Renaming flowSafeObservable to continueOnError and FlowSafeSubscriber to ResilientSubscriber --- .../net/corda/core/internal/InternalUtils.kt | 3 + .../net/corda/core/observable/Observables.kt | 13 + .../internal/ResilientSubscriber.kt | 120 ++++ .../vault/VaultObserverExceptionTest.kt | 556 +++++++++++++++++- .../node/services/vault/NodeVaultService.kt | 31 +- .../corda/node/utilities/ObservablesTests.kt | 210 ++++++- .../dbfailure/contracts/DbFailureContract.kt | 22 +- .../r3/dbfailure/workflows/CreateStateFlow.kt | 29 +- .../dbfailure/workflows/DbListenerService.kt | 214 ++++--- .../r3/dbfailure/workflows/SendStateFlow.kt | 88 +++ .../workflows/ErrorHandling.kt | 6 +- 11 files changed, 1166 insertions(+), 126 deletions(-) create mode 100644 core/src/main/kotlin/net/corda/core/observable/Observables.kt create mode 100644 core/src/main/kotlin/net/corda/core/observable/internal/ResilientSubscriber.kt create mode 100644 testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/SendStateFlow.kt diff --git a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt index fcfd9a5986..3d170290b5 100644 --- a/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt +++ b/core/src/main/kotlin/net/corda/core/internal/InternalUtils.kt @@ -192,6 +192,9 @@ fun Observable.bufferUntilSubscribed(): Observable { @DeleteForDJVM fun Observer.tee(vararg teeTo: Observer): Observer { val subject = PublishSubject.create() + // use unsafe subscribe, so that the teed subscribers will not get wrapped with SafeSubscribers, + // therefore a potential raw exception (non Rx) coming from a child -unsafe subscribed- observer + // will not unsubscribe all of the subscribers under the PublishSubject. subject.unsafeSubscribe(Subscribers.from(this)) teeTo.forEach { subject.unsafeSubscribe(Subscribers.from(it)) } return subject diff --git a/core/src/main/kotlin/net/corda/core/observable/Observables.kt b/core/src/main/kotlin/net/corda/core/observable/Observables.kt new file mode 100644 index 0000000000..c299ab8401 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/observable/Observables.kt @@ -0,0 +1,13 @@ +@file:JvmName("Observables") +package net.corda.core.observable + +import net.corda.core.observable.internal.OnResilientSubscribe +import rx.Observable + +/** + * [Observable.continueOnError] is used to return an Observable, through which we can subscribe non unsubscribing [rx.Observer]s + * to the source [Observable]. Namely, it makes the [rx.Observer]s resilient to exceptions coming out of [rx.Observer.onNext]. + * + * [Observable.continueOnError] should be called before every subscribe to have the aforementioned effect. + */ +fun Observable.continueOnError(): Observable = Observable.unsafeCreate(OnResilientSubscribe(this, true)) \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/observable/internal/ResilientSubscriber.kt b/core/src/main/kotlin/net/corda/core/observable/internal/ResilientSubscriber.kt new file mode 100644 index 0000000000..074a17a719 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/observable/internal/ResilientSubscriber.kt @@ -0,0 +1,120 @@ +package net.corda.core.observable.internal + +import net.corda.core.internal.VisibleForTesting +import rx.Observable +import rx.Observer +import rx.Subscriber +import rx.exceptions.CompositeException +import rx.exceptions.Exceptions +import rx.exceptions.OnErrorFailedException +import rx.exceptions.OnErrorNotImplementedException +import rx.internal.util.ActionSubscriber +import rx.observers.SafeSubscriber +import rx.plugins.RxJavaHooks +import rx.plugins.RxJavaPlugins +import rx.subjects.Subject + +/** + * Extends [SafeSubscriber] to override [SafeSubscriber.onNext], [SafeSubscriber.onError] and [SafeSubscriber._onError]. + * + * [ResilientSubscriber] will not set [SafeSubscriber.done] flag to true nor will call [SafeSubscriber.unsubscribe] upon + * error inside [Observer.onNext]. This way, the [ResilientSubscriber] will not get unsubscribed and therefore the underlying [Observer] + * will not get removed. + * + * An [Observer] that will not get removed due to errors in [onNext] events becomes useful when an unsubscribe could + * lead to a malfunctioning CorDapp, due to a single isolated error. If the [Observer] gets removed, + * it will no longer be available the next time any events are pushed from the base [Subject]. + */ +@VisibleForTesting +class ResilientSubscriber(actual: Subscriber) : SafeSubscriber(actual) { + + /** + * Duplicate of [SafeSubscriber.onNext]. However, it ignores [SafeSubscriber.done] flag. + * It only delegates to [SafeSubscriber.onError] if it wraps an [ActionSubscriber] which is + * a leaf in an Subscribers' tree structure. + */ + @Suppress("TooGenericExceptionCaught") + override fun onNext(t: T) { + try { + actual.onNext(t) + } catch (e: Throwable) { + if (actual is ActionSubscriber) { + // this Subscriber wraps an ActionSubscriber which is always a leaf Observer, then call user-defined onError + Exceptions.throwOrReport(e, this) + } else { + // this Subscriber may wrap a non leaf Observer. In case the wrapped Observer is a PublishSubject then we + // should not call onError because PublishSubjectState.onError will shut down all of the Observers under it + throw OnNextFailedException( + "Observer.onNext failed, this is a non leaf ResilientSubscriber, therefore onError will be skipped", e + ) + } + } + } + + /** + * Duplicate of [SafeSubscriber.onError]. However, it will not set [SafeSubscriber.done] flag to true. + */ + override fun onError(e: Throwable) { + Exceptions.throwIfFatal(e) + _onError(e) + } + + /** + * Duplicate of [SafeSubscriber._onError]. However, it will not call [Subscriber.unsubscribe]. + */ + @Suppress("TooGenericExceptionCaught") + override fun _onError(e: Throwable) { + @Suppress("DEPRECATION") + RxJavaPlugins.getInstance().errorHandler.handleError(e) + try { + actual.onError(e) + } catch (e: OnErrorNotImplementedException) { + throw e + } catch (e2: Throwable) { + RxJavaHooks.onError(e2) + throw OnErrorFailedException( + "Error occurred when trying to propagate error to Observer.onError", CompositeException(listOf(e, e2)) + ) + } + } +} + +/** + * We throw [OnNextFailedException] to pass the exception back through the preceding [Subscriber] chain + * without triggering any [SafeSubscriber.onError]s. Since we are extending an [OnErrorNotImplementedException] + * the exception will be re-thrown at [Exceptions.throwOrReport]. + */ +@VisibleForTesting +class OnNextFailedException(message: String, cause: Throwable) : OnErrorNotImplementedException(message, cause) + +/** + * [OnResilientSubscribe] returns an [Observable] holding a reference to the source [Observable]. Upon subscribing to it, + * when reaching [call] method, if the subscriber passed in [isSafeSubscriber] it will unwrap the [Observer] from + * the [SafeSubscriber], re-wrap it with [ResilientSubscriber] and then subscribe it to the source [Observable]. + * + * In case we need to subscribe with a [SafeSubscriber] to the source [Observable] via [OnResilientSubscribe], we have to: + * 1. Declare a custom SafeSubscriber extending [SafeSubscriber]. + * 2. Wrap our [rx.Observer] -to be subscribed to the source [Observable]- with the custom SafeSubscriber. + * 3. Create a [OnResilientSubscribe] object with [strictMode] = false. + * 3. Call [Observable.unsafeCreate] passing in as argument the [OnResilientSubscribe]. + * 4. Subscribe to the returned [Observable] passing in as argument the custom SafeSubscriber. + */ +class OnResilientSubscribe(val source: Observable, private val strictMode: Boolean): Observable.OnSubscribe { + + override fun call(subscriber: Subscriber) { + if (isSafeSubscriber(subscriber)) { + source.unsafeSubscribe(ResilientSubscriber((subscriber as SafeSubscriber).actual)) + } else { + source.unsafeSubscribe(subscriber) + } + } + + private fun isSafeSubscriber(subscriber: Subscriber<*>): Boolean { + return if (strictMode) { + // In strictMode mode we capture SafeSubscriber subclasses as well + SafeSubscriber::class.java.isAssignableFrom(subscriber::class.java) + } else { + subscriber::class == SafeSubscriber::class + } + } +} \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt index ac7bc8383e..5cd4529c6d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt @@ -1,34 +1,49 @@ package net.corda.node.services.vault import co.paralleluniverse.strands.concurrent.Semaphore +import com.r3.dbfailure.contracts.DbFailureContract import com.r3.dbfailure.workflows.CreateStateFlow -import com.r3.dbfailure.workflows.CreateStateFlow.Initiator import com.r3.dbfailure.workflows.CreateStateFlow.errorTargetsToNum import com.r3.dbfailure.workflows.DbListenerService import com.r3.dbfailure.workflows.DbListenerService.MakeServiceThrowErrorFlow +import com.r3.dbfailure.workflows.SendStateFlow import com.r3.transactionfailure.workflows.ErrorHandling import com.r3.transactionfailure.workflows.ErrorHandling.CheckpointAfterErrorFlow +import net.corda.core.CordaRuntimeException +import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.UniqueIdentifier +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.Party import net.corda.core.internal.concurrent.openFuture import net.corda.core.messaging.startFlow +import net.corda.core.node.services.Vault +import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.utilities.contextLogger import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.seconds import net.corda.node.services.Permissions import net.corda.node.services.statemachine.StaffedFlowHospital import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.singleIdentity import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.OutOfProcess import net.corda.testing.driver.driver import net.corda.testing.node.User import net.corda.testing.node.internal.findCordapp +import org.assertj.core.api.Assertions import org.junit.After import org.junit.Assert import org.junit.Test import java.lang.IllegalStateException import java.sql.SQLException +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException import javax.persistence.PersistenceException +import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertTrue @@ -49,6 +64,10 @@ class VaultObserverExceptionTest { StaffedFlowHospital.onFlowKeptForOvernightObservation.clear() StaffedFlowHospital.onFlowAdmitted.clear() DbListenerService.onError = null + DbListenerService.safeSubscription = true + DbListenerService.onNextVisited = {} + DbListenerService.onErrorVisited = null + DbListenerService.withCustomSafeSubscriber = false } /** @@ -74,9 +93,43 @@ class VaultObserverExceptionTest { val aliceUser = User("user", "foo", setOf(Permissions.all())) val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() aliceNode.rpc.startFlow( - ::Initiator, - "Syntax Error in Custom SQL", - CreateStateFlow.errorTargetsToNum(CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError) + CreateStateFlow::Initiator, + "Syntax Error in Custom SQL", + CreateStateFlow.errorTargetsToNum(CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError) + ).returnValue.then { testControlFuture.complete(false) } + val foundExpectedException = testControlFuture.getOrThrow(30.seconds) + + Assert.assertTrue(foundExpectedException) + } + } + + /** + * Causing an SqlException via a syntax error in a vault observer causes the flow to hit the + * DatabsaseEndocrinologist in the FlowHospital and being kept for overnight observation - Unsafe subscribe + */ + @Test(timeout=300_000) + fun unhandledSqlExceptionFromVaultObserverGetsHospitalisedUnsafeSubscription() { + DbListenerService.safeSubscription = false + val testControlFuture = openFuture().toCompletableFuture() + + StaffedFlowHospital.DatabaseEndocrinologist.customConditions.add { + when (it) { + is SQLException -> { + testControlFuture.complete(true) + } + } + false + } + + driver(DriverParameters( + startNodesInProcess = true, + cordappsForAllNodes = testCordapps())) { + val aliceUser = User("user", "foo", setOf(Permissions.all())) + val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() + aliceNode.rpc.startFlow( + CreateStateFlow::Initiator, + "Syntax Error in Custom SQL", + CreateStateFlow.errorTargetsToNum(CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError) ).returnValue.then { testControlFuture.complete(false) } val foundExpectedException = testControlFuture.getOrThrow(30.seconds) @@ -103,7 +156,7 @@ class VaultObserverExceptionTest { cordappsForAllNodes = testCordapps())) { val aliceUser = User("user", "foo", setOf(Permissions.all())) val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() - aliceNode.rpc.startFlow(::Initiator, "Exception", CreateStateFlow.errorTargetsToNum( + aliceNode.rpc.startFlow(CreateStateFlow::Initiator, "Exception", CreateStateFlow.errorTargetsToNum( CreateStateFlow.ErrorTarget.ServiceThrowMotherOfAllExceptions, CreateStateFlow.ErrorTarget.FlowSwallowErrors)) waitUntilHospitalised.acquire() // wait here until flow gets hospitalised @@ -131,9 +184,9 @@ class VaultObserverExceptionTest { cordappsForAllNodes = testCordapps())) { val aliceUser = User("user", "foo", setOf(Permissions.all())) val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() - aliceNode.rpc.startFlow(::Initiator, "InvalidParameterException", CreateStateFlow.errorTargetsToNum( - CreateStateFlow.ErrorTarget.ServiceThrowInvalidParameter, - CreateStateFlow.ErrorTarget.FlowSwallowErrors)) + aliceNode.rpc.startFlow(CreateStateFlow::Initiator, "InvalidParameterException", CreateStateFlow.errorTargetsToNum( + CreateStateFlow.ErrorTarget.ServiceThrowInvalidParameter, + CreateStateFlow.ErrorTarget.FlowSwallowErrors)) waitUntilHospitalised.acquire() // wait here until flow gets hospitalised } @@ -167,7 +220,7 @@ class VaultObserverExceptionTest { val aliceUser = User("user", "foo", setOf(Permissions.all())) val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() assertFailsWith("PersistenceException") { - aliceNode.rpc.startFlow(::Initiator, "EntityManager", errorTargetsToNum( + aliceNode.rpc.startFlow(CreateStateFlow::Initiator, "EntityManager", errorTargetsToNum( CreateStateFlow.ErrorTarget.TxInvalidState)) .returnValue.getOrThrow(30.seconds) } @@ -200,7 +253,7 @@ class VaultObserverExceptionTest { val aliceUser = User("user", "foo", setOf(Permissions.all())) val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() val flowHandle = aliceNode.rpc.startFlow( - ::Initiator, "EntityManager", + CreateStateFlow::Initiator, "EntityManager", CreateStateFlow.errorTargetsToNum( CreateStateFlow.ErrorTarget.TxInvalidState, CreateStateFlow.ErrorTarget.FlowSwallowErrors)) @@ -228,7 +281,7 @@ class VaultObserverExceptionTest { cordappsForAllNodes = testCordapps())) { val aliceUser = User("user", "foo", setOf(Permissions.all())) val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() - val flowHandle = aliceNode.rpc.startFlow(::Initiator, "EntityManager", CreateStateFlow.errorTargetsToNum( + val flowHandle = aliceNode.rpc.startFlow(CreateStateFlow::Initiator, "EntityManager", CreateStateFlow.errorTargetsToNum( CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError, CreateStateFlow.ErrorTarget.FlowSwallowErrors)) val flowResult = flowHandle.returnValue @@ -251,7 +304,7 @@ class VaultObserverExceptionTest { cordappsForAllNodes = testCordapps())) { val aliceUser = User("user", "foo", setOf(Permissions.all())) val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() - val flowHandle = aliceNode.rpc.startFlow(::Initiator, "EntityManager", CreateStateFlow.errorTargetsToNum( + val flowHandle = aliceNode.rpc.startFlow(CreateStateFlow::Initiator, "EntityManager", CreateStateFlow.errorTargetsToNum( CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError, CreateStateFlow.ErrorTarget.ServiceSwallowErrors)) val flowResult = flowHandle.returnValue @@ -281,20 +334,20 @@ class VaultObserverExceptionTest { } driver(DriverParameters( - inMemoryDB = false, - startNodesInProcess = true, - isDebug = true, - cordappsForAllNodes = listOf(findCordapp("com.r3.dbfailure.contracts"), - findCordapp("com.r3.dbfailure.workflows"), - findCordapp("com.r3.transactionfailure.workflows"), - findCordapp("com.r3.dbfailure.schemas")))) { + inMemoryDB = false, + startNodesInProcess = true, + isDebug = true, + cordappsForAllNodes = listOf(findCordapp("com.r3.dbfailure.contracts"), + findCordapp("com.r3.dbfailure.workflows"), + findCordapp("com.r3.transactionfailure.workflows"), + findCordapp("com.r3.dbfailure.schemas")))) { val aliceUser = User("user", "foo", setOf(Permissions.all())) val node = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() node.rpc.startFlow(::CheckpointAfterErrorFlow, CreateStateFlow.errorTargetsToNum( CreateStateFlow.ErrorTarget.ServiceThrowMotherOfAllExceptions, // throw not persistence exception CreateStateFlow.ErrorTarget.FlowSwallowErrors - ) + ) ) waitUntilHospitalised.acquire() @@ -329,9 +382,9 @@ class VaultObserverExceptionTest { cordappsForAllNodes = testCordapps())) { val aliceUser = User("user", "foo", setOf(Permissions.all())) val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser)).getOrThrow() - aliceNode.rpc.startFlow(::Initiator, "Exception", CreateStateFlow.errorTargetsToNum( - CreateStateFlow.ErrorTarget.ServiceThrowInvalidParameter, - CreateStateFlow.ErrorTarget.FlowSwallowErrors)) + aliceNode.rpc.startFlow(CreateStateFlow::Initiator, "Exception", CreateStateFlow.errorTargetsToNum( + CreateStateFlow.ErrorTarget.ServiceThrowInvalidParameter, + CreateStateFlow.ErrorTarget.FlowSwallowErrors)) waitUntilHospitalised.acquire() // wait here until flow gets hospitalised } @@ -344,7 +397,7 @@ class VaultObserverExceptionTest { val aliceUser = User("user", "foo", setOf(Permissions.all())) val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(aliceUser), startInSameProcess = false).getOrThrow() aliceNode.rpc.startFlow(::MakeServiceThrowErrorFlow).returnValue.getOrThrow() - aliceNode.rpc.startFlow(::Initiator, "UnrecoverableError", CreateStateFlow.errorTargetsToNum( + aliceNode.rpc.startFlow(CreateStateFlow::Initiator, "UnrecoverableError", CreateStateFlow.errorTargetsToNum( CreateStateFlow.ErrorTarget.ServiceThrowUnrecoverableError)) val terminated = (aliceNode as OutOfProcess).process.waitFor(30, TimeUnit.SECONDS) @@ -363,4 +416,459 @@ class VaultObserverExceptionTest { } } + /** + * An error is thrown inside of the [VaultService.rawUpdates] observable while recording a transaction inside of the initiating node. + * + * This causes the transaction to not be saved on the local node but the notary still records the transaction as spent. The transaction + * also is not send to the counterparty node since it failed before reaching the send. Therefore no subscriber events occur on the + * counterparty node. + * + * More importantly, the observer listening to the [VaultService.rawUpdates] observable should not unsubscribe. + * + * Check onNext is visited the correct number of times. + * + * This test causes 2 failures inside of the observer to ensure that the observer is still subscribed. + */ + @Test(timeout=300_000) + fun `Throw user error in VaultService rawUpdates during FinalityFlow blows up the flow but does not break the Observer - onNext check`() { + var observationCounter = 0 + StaffedFlowHospital.onFlowKeptForOvernightObservation.add { _, _ -> ++observationCounter } + + val rawUpdatesCount = ConcurrentHashMap() + DbListenerService.onNextVisited = { party -> + if (rawUpdatesCount.putIfAbsent(party, 1) != null) { + rawUpdatesCount.computeIfPresent(party) { _, count -> count + 1 } + } + } + + val user = User("user", "foo", setOf(Permissions.all())) + driver(DriverParameters(startNodesInProcess = true, + cordappsForAllNodes = listOf( + findCordapp("com.r3.dbfailure.contracts"), + findCordapp("com.r3.dbfailure.workflows"), + findCordapp("com.r3.dbfailure.schemas") + ),inMemoryDB = false) + ) { + val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + val bobNode = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)).getOrThrow() + val notary = defaultNotaryHandle.nodeHandles.getOrThrow().first() + + val startErrorInObservableWhenConsumingState = { + + val stateId = aliceNode.rpc.startFlow( + CreateStateFlow::Initiator, + "AllGood", + errorTargetsToNum(CreateStateFlow.ErrorTarget.ServiceSqlSyntaxErrorOnConsumed) + ).returnValue.getOrThrow(30.seconds) + + println("Created new state") + + val flowHandle = aliceNode.rpc.startFlow( + SendStateFlow::PassErroneousOwnableState, // throws at consumed state -> should end up in hospital -> flow should hang + stateId, + errorTargetsToNum(CreateStateFlow.ErrorTarget.NoError), + bobNode.nodeInfo.legalIdentities.first() + ) + + Assertions.assertThatExceptionOfType(TimeoutException::class.java) + .isThrownBy { flowHandle.returnValue.getOrThrow(20.seconds) } + + stateId + } + + assertEquals(0, notary.getNotarisedTransactionIds().size) + + println("First set of flows") + val stateId = startErrorInObservableWhenConsumingState() + assertEquals(0, aliceNode.getStatesById(stateId, Vault.StateStatus.CONSUMED).size) + assertEquals(0, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) + assertEquals(1, notary.getNotarisedTransactionIds().size) + assertEquals(1, observationCounter) + assertEquals(2, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) + assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + + println("Second set of flows") + val stateId2 = startErrorInObservableWhenConsumingState() + assertEquals(0, aliceNode.getStatesById(stateId2, Vault.StateStatus.CONSUMED).size) + assertEquals(0, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) + assertEquals(2, notary.getNotarisedTransactionIds().size) + assertEquals(2, observationCounter) + assertEquals(4, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) + assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + } + } + + /** + * An error is thrown inside of the [VaultService.rawUpdates] observable while recording a transaction inside of the initiating node. + * + * This causes the transaction to not be saved on the local node but the notary still records the transaction as spent. The transaction + * also is not send to the counterparty node since it failed before reaching the send. Therefore no subscriber events occur on the + * counterparty node. + * + * More importantly, the observer listening to the [VaultService.rawUpdates] observable should not unsubscribe. + * + * Check onNext and onError are visited the correct number of times. + * + * This test causes 2 failures inside of the observer to ensure that the observer is still subscribed. + */ + @Test(timeout=300_000) + fun `Throw user error in VaultService rawUpdates during FinalityFlow blows up the flow but does not break the Observer - onNext and onError check`() { + var observationCounter = 0 + StaffedFlowHospital.onFlowKeptForOvernightObservation.add { _, _ -> ++observationCounter } + + val rawUpdatesCount = ConcurrentHashMap() + DbListenerService.onNextVisited = { party -> + if (rawUpdatesCount.putIfAbsent(party, 1) != null) { + rawUpdatesCount.computeIfPresent(party) { _, count -> count + 1 } + } + } + + DbListenerService.onError = {/*just rethrow - we just want to check that onError gets visited by parties*/ throw it} + DbListenerService.onErrorVisited = { party -> + if (rawUpdatesCount.putIfAbsent(party, 1) != null) { + rawUpdatesCount.computeIfPresent(party) { _, count -> count + 1 } + } + } + + val user = User("user", "foo", setOf(Permissions.all())) + driver(DriverParameters(startNodesInProcess = true, + cordappsForAllNodes = listOf( + findCordapp("com.r3.dbfailure.contracts"), + findCordapp("com.r3.dbfailure.workflows"), + findCordapp("com.r3.dbfailure.schemas") + ), + inMemoryDB = false) + ) { + val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + val bobNode = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)).getOrThrow() + val notary = defaultNotaryHandle.nodeHandles.getOrThrow().first() + + val startErrorInObservableWhenConsumingState = { + + val stateId = aliceNode.rpc.startFlow( + CreateStateFlow::Initiator, + "AllGood", + // should be a hospital exception + errorTargetsToNum(CreateStateFlow.ErrorTarget.ServiceSqlSyntaxErrorOnConsumed) + ).returnValue.getOrThrow(30.seconds) + + val flowHandle = aliceNode.rpc.startFlow( + SendStateFlow::PassErroneousOwnableState, + stateId, + errorTargetsToNum(CreateStateFlow.ErrorTarget.NoError), + bobNode.nodeInfo.legalIdentities.first() + ) + + Assertions.assertThatExceptionOfType(TimeoutException::class.java) + .isThrownBy { flowHandle.returnValue.getOrThrow(20.seconds) } + + stateId + } + + assertEquals(0, notary.getNotarisedTransactionIds().size) + + val stateId = startErrorInObservableWhenConsumingState() + assertEquals(0, aliceNode.getStatesById(stateId, Vault.StateStatus.CONSUMED).size) + assertEquals(0, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) + assertEquals(1, notary.getNotarisedTransactionIds().size) + assertEquals(1, observationCounter) + assertEquals(3, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) + assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + + val stateId2 = startErrorInObservableWhenConsumingState() + assertEquals(0, aliceNode.getStatesById(stateId2, Vault.StateStatus.CONSUMED).size) + assertEquals(0, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) + assertEquals(2, notary.getNotarisedTransactionIds().size) + assertEquals(2, observationCounter) + assertEquals(6, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) + assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + } + } + + /** + * An error is thrown inside of the [VaultService.rawUpdates] observable while recording a transaction inside of the counterparty node. + * + * This causes the transaction to not be saved on the local node but the notary still records the transaction as spent. + * Observer events are recorded on both the initiating node and the counterparty node. + * + * More importantly, the observer listening to the [VaultService.rawUpdates] observable should not unsubscribe. + * + * This test causes 2 failures inside of the observer to ensure that the observer is still subscribed. + */ + @Test(timeout=300_000) + fun `Throw user error in VaultService rawUpdates during counterparty FinalityFlow blows up the flow but does not break the Observer`() { + var observationCounter = 0 + StaffedFlowHospital.onFlowKeptForOvernightObservation.add { _, _ -> ++observationCounter } + + val rawUpdatesCount = ConcurrentHashMap() + DbListenerService.onNextVisited = { party -> + if (rawUpdatesCount.putIfAbsent(party, 1) != null) { + rawUpdatesCount.computeIfPresent(party) { _, count -> count + 1 } + } + } + + val user = User("user", "foo", setOf(Permissions.all())) + driver(DriverParameters(startNodesInProcess = true, + cordappsForAllNodes = listOf( + findCordapp("com.r3.dbfailure.contracts"), + findCordapp("com.r3.dbfailure.workflows"), + findCordapp("com.r3.dbfailure.schemas") + ), + inMemoryDB = false) + ) { + val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + val bobNode = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)).getOrThrow() + val notary = defaultNotaryHandle.nodeHandles.getOrThrow().first() + + val startErrorInObservableWhenCreatingSecondState = { + + val stateId = aliceNode.rpc.startFlow( + CreateStateFlow::Initiator, + "AllGood", + errorTargetsToNum(CreateStateFlow.ErrorTarget.NoError) + ).returnValue.getOrThrow(30.seconds) + + aliceNode.rpc.startFlow( + SendStateFlow::PassErroneousOwnableState, + stateId, + errorTargetsToNum(CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError), + bobNode.nodeInfo.legalIdentities.first() + ).returnValue.getOrThrow(20.seconds) + + stateId + } + + assertEquals(0, notary.getNotarisedTransactionIds().size) + + val stateId = startErrorInObservableWhenCreatingSecondState() + assertEquals(1, aliceNode.getStatesById(stateId, Vault.StateStatus.CONSUMED).size) + assertEquals(0, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) + assertEquals(1, notary.getNotarisedTransactionIds().size) + assertEquals(1, observationCounter) + assertEquals(2, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) + assertEquals(1, rawUpdatesCount[bobNode.nodeInfo.singleIdentity()]) + + val stateId2 = startErrorInObservableWhenCreatingSecondState() + assertEquals(1, aliceNode.getStatesById(stateId2, Vault.StateStatus.CONSUMED).size) + assertEquals(2, aliceNode.getAllStates(Vault.StateStatus.CONSUMED).size) + assertEquals(0, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) + assertEquals(2, notary.getNotarisedTransactionIds().size) + assertEquals(2, observationCounter) + assertEquals(4, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) + assertEquals(2, rawUpdatesCount[bobNode.nodeInfo.singleIdentity()]) + } + } + + /** + * An error is thrown inside of the [VaultService.updates] observable while recording a transaction inside of the initiating node. + * + * This causes the transaction to not be saved on the local node but the notary still records the transaction as spent. The transaction + * also is not send to the counterparty node since it failed before reaching the send. Therefore no subscriber events occur on the + * counterparty node. + * + * More importantly, the observer listening to the [VaultService.updates] observable should not unsubscribe. + * + * This test causes 2 failures inside of the [rx.Observer] to ensure that the Observer is still subscribed. + */ + @Test(timeout=300_000) + fun `Throw user error in VaultService rawUpdates during FinalityFlow blows up the flow but does not break the Observer`() { + var observationCounter = 0 + StaffedFlowHospital.onFlowKeptForOvernightObservation.add { _, _ -> ++observationCounter } + + val rawUpdatesCount = ConcurrentHashMap() + DbListenerService.onNextVisited = { party -> + if (rawUpdatesCount.putIfAbsent(party, 1) != null) { + rawUpdatesCount.computeIfPresent(party) { _, count -> count + 1 } + } + } + + val user = User("user", "foo", setOf(Permissions.all())) + driver(DriverParameters(startNodesInProcess = true, + cordappsForAllNodes = listOf( + findCordapp("com.r3.dbfailure.contracts"), + findCordapp("com.r3.dbfailure.workflows"), + findCordapp("com.r3.dbfailure.schemas") + ), + inMemoryDB = false) + ) { + val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + val bobNode = startNode(providedName = BOB_NAME, rpcUsers = listOf(user)).getOrThrow() + val notary = defaultNotaryHandle.nodeHandles.getOrThrow().first() + + val startErrorInObservableWhenConsumingState = { + + val stateId = aliceNode.rpc.startFlow( + CreateStateFlow::Initiator, + "AllGood", + errorTargetsToNum(CreateStateFlow.ErrorTarget.ServiceSqlSyntaxErrorOnConsumed) + ).returnValue.getOrThrow(30.seconds) + + val flowHandle = aliceNode.rpc.startFlow( + SendStateFlow::PassErroneousOwnableState, + stateId, + errorTargetsToNum(CreateStateFlow.ErrorTarget.NoError), + bobNode.nodeInfo.legalIdentities.first() + ) + + Assertions.assertThatExceptionOfType(TimeoutException::class.java) + .isThrownBy { flowHandle.returnValue.getOrThrow(20.seconds) } + + stateId + } + + assertEquals(0, notary.getNotarisedTransactionIds().size) + + val stateId = startErrorInObservableWhenConsumingState() + assertEquals(0, aliceNode.getStatesById(stateId, Vault.StateStatus.CONSUMED).size) + assertEquals(1, aliceNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) + assertEquals(0, bobNode.getStatesById(stateId, Vault.StateStatus.UNCONSUMED).size) + assertEquals(1, notary.getNotarisedTransactionIds().size) + assertEquals(1, observationCounter) + assertEquals(2, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) + assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + + val stateId2 = startErrorInObservableWhenConsumingState() + assertEquals(0, aliceNode.getStatesById(stateId2, Vault.StateStatus.CONSUMED).size) + assertEquals(2, aliceNode.getAllStates(Vault.StateStatus.UNCONSUMED).size) + assertEquals(0, bobNode.getStatesById(stateId2, Vault.StateStatus.UNCONSUMED).size) + assertEquals(2, notary.getNotarisedTransactionIds().size) + assertEquals(4, rawUpdatesCount[aliceNode.nodeInfo.singleIdentity()]) + assertEquals(0, rawUpdatesCount.getOrDefault(bobNode.nodeInfo.singleIdentity(), 0)) + } + } + + @Test(timeout=300_000) + fun `Accessing NodeVaultService rawUpdates from a flow is not allowed` () { + val user = User("user", "foo", setOf(Permissions.all())) + driver(DriverParameters(startNodesInProcess = true, + cordappsForAllNodes = listOf( + findCordapp("com.r3.dbfailure.contracts"), + findCordapp("com.r3.dbfailure.workflows"), + findCordapp("com.r3.dbfailure.schemas") + ), + inMemoryDB = false) + ) { + val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + + val flowHandle = aliceNode.rpc.startFlow(::SubscribingRawUpdatesFlow) + + assertFailsWith( + "Flow ${SubscribingRawUpdatesFlow::class.java.name} tried to access VaultService.rawUpdates " + + "- Rx.Observables should only be accessed outside the context of a flow " + ) { + flowHandle.returnValue.getOrThrow(30.seconds) + } + } + } + + @Test(timeout=300_000) + fun `Failing Observer wrapped with ResilientSubscriber will survive and be re-called upon flow retry`() { + var onNextCount = 0 + var onErrorCount = 0 + DbListenerService.onNextVisited = { _ -> onNextCount++ } + DbListenerService.onError = {/*just rethrow - we just want to check that onError gets visited by parties*/ throw it} + DbListenerService.onErrorVisited = { _ -> onErrorCount++ } + + val user = User("user", "foo", setOf(Permissions.all())) + driver(DriverParameters(startNodesInProcess = true, + cordappsForAllNodes = listOf( + findCordapp("com.r3.dbfailure.contracts"), + findCordapp("com.r3.dbfailure.workflows"), + findCordapp("com.r3.transactionfailure.workflows"), + findCordapp("com.r3.dbfailure.schemas")), + inMemoryDB = false) + ) { + val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + + assertFailsWith { + aliceNode.rpc.startFlow( + ErrorHandling::CheckpointAfterErrorFlow, + CreateStateFlow.errorTargetsToNum( + CreateStateFlow.ErrorTarget.ServiceConstraintViolationException, + CreateStateFlow.ErrorTarget.FlowSwallowErrors + ) + ).returnValue.getOrThrow(20.seconds) + } + + assertEquals(4, onNextCount) + assertEquals(4, onErrorCount) + } + } + + @Test(timeout=300_000) + fun `Users may subscribe to NodeVaultService rawUpdates with their own custom SafeSubscribers`() { + var onNextCount = 0 + DbListenerService.onNextVisited = { _ -> onNextCount++ } + + val user = User("user", "foo", setOf(Permissions.all())) + driver(DriverParameters(startNodesInProcess = true, + cordappsForAllNodes = listOf( + findCordapp("com.r3.dbfailure.contracts"), + findCordapp("com.r3.dbfailure.workflows"), + findCordapp("com.r3.transactionfailure.workflows"), + findCordapp("com.r3.dbfailure.schemas")), + inMemoryDB = false) + ) { + // Subscribing with custom SafeSubscriber; the custom SafeSubscriber will not get replaced by a ResilientSubscriber + // meaning that it will behave as a SafeSubscriber; it will get unsubscribed upon throwing an error. + // Because we throw a ConstraintViolationException, the Rx Observer will get unsubscribed but the flow will retry + // from previous checkpoint, however the Observer will no longer be there. + DbListenerService.withCustomSafeSubscriber = true + val aliceNode = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user)).getOrThrow() + + aliceNode.rpc.startFlow( + ErrorHandling::CheckpointAfterErrorFlow, + CreateStateFlow.errorTargetsToNum( + CreateStateFlow.ErrorTarget.ServiceConstraintViolationException, + CreateStateFlow.ErrorTarget.FlowSwallowErrors + ) + ).returnValue.getOrThrow(20.seconds) + + assertEquals(1, onNextCount) + } + } + + private fun NodeHandle.getNotarisedTransactionIds(): List { + + @StartableByRPC + class NotarisedTxs : FlowLogic>() { + override fun call(): List { + val session = serviceHub.jdbcSession() + val statement = session.createStatement() + statement.execute("SELECT TRANSACTION_ID FROM NODE_NOTARY_COMMITTED_TXS;") + val result = mutableListOf() + while (statement.resultSet.next()) { + result.add(statement.resultSet.getString(1)) + } + return result + } + } + + return rpc.startFlowDynamic(NotarisedTxs::class.java).returnValue.getOrThrow() + } + + private fun NodeHandle.getStatesById(id: UniqueIdentifier?, status: Vault.StateStatus): List> { + return rpc.vaultQueryByCriteria( + QueryCriteria.LinearStateQueryCriteria( + linearId = if (id != null) listOf(id) else null, + status = status + ), DbFailureContract.TestState::class.java + ).states + } + + private fun NodeHandle.getAllStates(status: Vault.StateStatus): List> { + return getStatesById(null, status) + } + + @StartableByRPC + class SubscribingRawUpdatesFlow: FlowLogic() { + override fun call() { + logger.info("Accessing rawUpdates within a flow will throw! ") + val rawUpdates = serviceHub.vaultService.rawUpdates // throws + logger.info("Code flow should never reach this logging or the following segment! ") + rawUpdates.subscribe { + println("Code flow should never get in here!") + } + } + } } \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index e60fd10cb4..438e2c8dec 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -2,6 +2,7 @@ package net.corda.node.services.vault import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.strands.Strand +import net.corda.core.CordaRuntimeException import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash import net.corda.core.crypto.containsAny @@ -13,6 +14,7 @@ import net.corda.core.node.StatesToRecord import net.corda.core.node.services.* import net.corda.core.node.services.Vault.ConstraintInfo.Companion.constraintInfo import net.corda.core.node.services.vault.* +import net.corda.core.observable.internal.OnResilientSubscribe import net.corda.core.schemas.PersistentStateRef import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.transactions.* @@ -209,7 +211,27 @@ class NodeVaultService( } override val rawUpdates: Observable> - get() = mutex.locked { _rawUpdatesPublisher } + get() = mutex.locked { + FlowStateMachineImpl.currentStateMachine()?.let { + // we are inside a flow; we cannot allow flows to subscribe Rx Observers, + // because the Observer could reference flow's properties, essentially fiber's properties then, + // since it does not unsubscribe on flow's/ fiber's completion, + // it could prevent the flow/ fiber -object- get garbage collected. + log.error( + "Flow ${it.logic::class.java.name} tried to access VaultService.rawUpdates " + + "- Rx.Observables should only be accessed outside the context of a flow " + + "- aborting the flow " + ) + + throw CordaRuntimeException( + "Flow ${it.logic::class.java.name} tried to access VaultService.rawUpdates " + + "- Rx.Observables should only be accessed outside the context of a flow " + ) + } + // we are not inside a flow, we are most likely inside a CordaService; + // we will expose, by default, subscribing of -non unsubscribing- rx.Observers to rawUpdates. + return _rawUpdatesPublisher.resilientOnError() + } override val updates: Observable> get() = mutex.locked { _updatesInDbTx } @@ -414,7 +436,7 @@ class NodeVaultService( HospitalizeFlowException(wrapped) } } - } ?: HospitalizeFlowException(e) + } ?: (e as? SQLException ?: (e as? PersistenceException ?: HospitalizeFlowException(e))) } } } @@ -795,4 +817,7 @@ class NodeVaultService( } return myTypes } -} \ No newline at end of file +} + +/** The Observable returned allows subscribing with custom SafeSubscribers to source [Observable]. */ +internal fun Observable.resilientOnError(): Observable = Observable.unsafeCreate(OnResilientSubscribe(this, false)) \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt index e9817111d3..13f92edb18 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt @@ -3,6 +3,10 @@ package net.corda.node.utilities import com.google.common.util.concurrent.SettableFuture import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.tee +import net.corda.core.observable.internal.ResilientSubscriber +import net.corda.core.observable.internal.OnNextFailedException +import net.corda.core.observable.continueOnError +import net.corda.node.services.vault.resilientOnError import net.corda.nodeapi.internal.persistence.* import net.corda.testing.internal.configureDatabase import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties @@ -10,9 +14,17 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.After import org.junit.Test import rx.Observable +import rx.Subscriber +import rx.exceptions.CompositeException +import rx.exceptions.OnErrorFailedException +import rx.exceptions.OnErrorNotImplementedException +import rx.internal.util.ActionSubscriber +import rx.observers.SafeSubscriber import rx.observers.Subscribers import rx.subjects.PublishSubject import java.io.Closeable +import java.lang.IllegalArgumentException +import java.lang.IllegalStateException import java.lang.RuntimeException import java.util.* import kotlin.test.assertEquals @@ -194,7 +206,7 @@ class ObservablesTests { * tee combines [PublishSubject]s under one PublishSubject. We need to make sure that they are not wrapped with a [SafeSubscriber]. * Otherwise, if a non Rx exception gets thrown from a subscriber under one of the PublishSubject it will get caught by the * SafeSubscriber wrapping that PublishSubject and will call [PublishSubject.PublishSubjectState.onError], which will - * eventually shut down all of the subscribers under that PublishSubjectState. + * eventually shut down all of the subscribers under that PublishSubject. */ @Test(timeout=300_000) fun `error in unsafe subscriber won't shutdown subscribers under same publish subject, after tee`() { @@ -214,6 +226,200 @@ class ObservablesTests { assertEquals(2, count) } + @Test(timeout=300_000) + fun `continueOnError subscribes ResilientSubscribers, wrapped Observers will survive errors from onNext`() { + var heartBeat1 = 0 + var heartBeat2 = 0 + val source = PublishSubject.create() + val continueOnError = source.continueOnError() + continueOnError.subscribe { runNo -> + // subscribes with a ResilientSubscriber + heartBeat1++ + if (runNo == 1) { + throw IllegalStateException() + } + } + continueOnError.subscribe { runNo -> + // subscribes with a ResilientSubscriber + heartBeat2++ + if (runNo == 2) { + throw IllegalStateException() + } + } + + assertFailsWith { + source.onNext(1) // first observer only will run and throw + } + assertFailsWith { + source.onNext(2) // first observer will run, second observer will run and throw + } + source.onNext(3) // both observers will run + assertEquals(3, heartBeat1) + assertEquals(2, heartBeat2) + } + + @Test(timeout=300_000) + fun `PublishSubject unsubscribes ResilientSubscribers only upon explicitly calling onError`() { + var heartBeat = 0 + val source = PublishSubject.create() + source.continueOnError().subscribe { heartBeat += it } + source.continueOnError().subscribe { heartBeat += it } + source.onNext(1) + // send an onError event + assertFailsWith { + source.onError(IllegalStateException()) // all ResilientSubscribers under PublishSubject get unsubscribed here + } + source.onNext(1) + assertEquals(2, heartBeat) + } + + @Test(timeout=300_000) + fun `PublishSubject wrapped with a SafeSubscriber shuts down the whole structure, if one of them is unsafe and it throws`() { + var heartBeat = 0 + val source = PublishSubject.create() + source.unsafeSubscribe(Subscribers.create { runNo -> // subscribes unsafe; It does not wrap with ResilientSubscriber + heartBeat++ + if (runNo == 1) { + throw IllegalStateException() + } + }) + source.continueOnError().subscribe { heartBeat += it } + // wrapping PublishSubject with a SafeSubscriber + val sourceWrapper = SafeSubscriber(Subscribers.from(source)) + assertFailsWith { + sourceWrapper.onNext(1) + } + sourceWrapper.onNext(2) + assertEquals(1, heartBeat) + } + + /** + * A [ResilientSubscriber] that is NOT a leaf in a subscribers structure will not call [onError] + * if an error occurs during its [onNext] event processing. + * + * The reason why it should not call its onError is: if it wraps a [PublishSubject], calling [ResilientSubscriber.onError] + * will then call [PublishSubject.onError] which will shut down all the subscribers under the [PublishSubject]. + */ + @Test(timeout=300_000) + fun `PublishSubject wrapped with a ResilientSubscriber will preserve the structure, if one of its children subscribers is unsafe and it throws`() { + var heartBeat = 0 + val source = PublishSubject.create() + source.unsafeSubscribe(Subscribers.create { runNo -> + heartBeat++ + if (runNo == 1) { + throw IllegalStateException() + } + }) + source.continueOnError().subscribe { heartBeat++ } + // wrap PublishSubject with a ResilientSubscriber + val sourceWrapper = ResilientSubscriber(Subscribers.from(source)) + assertFailsWith("Observer.onNext failed, this is a non leaf ResilientSubscriber, therefore onError will be skipped") { + sourceWrapper.onNext(1) + } + sourceWrapper.onNext(2) + assertEquals(3, heartBeat) + } + + @Test(timeout=300_000) + fun `throwing inside onNext of a ResilientSubscriber leaf subscriber will call onError`() { + var heartBeatOnNext = 0 + var heartBeatOnError = 0 + val source = PublishSubject.create() + // add a leaf ResilientSubscriber + source.continueOnError().subscribe({ + heartBeatOnNext++ + throw IllegalStateException() + }, { + heartBeatOnError++ + }) + + source.onNext(1) + source.onNext(1) + assertEquals(2, heartBeatOnNext) + assertEquals(2, heartBeatOnError) + } + + /** + * In this test ResilientSubscriber throws an OnNextFailedException which is a OnErrorNotImplementedException. + * Because its underlying subscriber is not an ActionSubscriber, it will not be considered as a leaf ResilientSubscriber. + */ + @Test(timeout=300_000) + fun `throwing ResilientSubscriber at onNext will wrap with a Rx OnErrorNotImplementedException`() { + val resilientSubscriber = ResilientSubscriber(Subscribers.create { throw IllegalStateException() }) + assertFailsWith { // actually fails with an OnNextFailedException + resilientSubscriber.onNext(1) + } + } + + @Test(timeout=300_000) + fun `throwing inside ResilientSubscriber onError will wrap with a Rx OnErrorFailedException`() { + val resilientSubscriber = ResilientSubscriber( + ActionSubscriber( + { throw IllegalStateException() }, + { throw IllegalStateException() }, + null + ) + ) + assertFailsWith { + resilientSubscriber.onNext(1) + } + } + + /** + * In this test we create a chain of Subscribers with this the following order: + * ResilientSubscriber_X -> PublishSubject -> ResilientSubscriber_Y + * + * ResilientSubscriber_Y.onNext throws an error, since ResilientSubscriber_Y.onError is not defined, + * it will throw a OnErrorNotImplementedException. Then it will be propagated back until ResilientSubscriber_X. + * ResilientSubscriber_X will identify it is a not leaf subscriber and therefore will rethrow it as OnNextFailedException. + */ + @Test(timeout=300_000) + fun `propagated Rx exception will be rethrown at ResilientSubscriber onError`() { + val source = PublishSubject.create() + source.continueOnError().subscribe { throw IllegalStateException("123") } // will give a leaf ResilientSubscriber + val sourceWrapper = ResilientSubscriber(Subscribers.from(source)) // will give an inner ResilientSubscriber + + assertFailsWith("Observer.onNext failed, this is a non leaf ResilientSubscriber, therefore onError will be skipped") { + // IllegalStateException will be wrapped and rethrown as a OnErrorNotImplementedException in leaf ResilientSubscriber, + // will be caught by inner ResilientSubscriber and just be rethrown + sourceWrapper.onNext(1) + } + } + + @Test(timeout=300_000) + fun `test OnResilientSubscribe strictMode = true replaces SafeSubscriber subclass`() { + var heartBeat = 0 + val customSafeSubscriber = CustomSafeSubscriber( + Subscribers.create { + heartBeat++ + throw IllegalArgumentException() + }) + + val source = PublishSubject.create() + source.continueOnError().subscribe(customSafeSubscriber) // it should replace CustomSafeSubscriber with ResilientSubscriber + + assertFailsWith { source.onNext(1) } + assertFailsWith { source.onNext(1) } + assertEquals(2, heartBeat) + } + + @Test(timeout=300_000) + fun `test OnResilientSubscribe strictMode = false will not replace SafeSubscriber subclass`() { + var heartBeat = 0 + val customSafeSubscriber = CustomSafeSubscriber( + Subscribers.create { + heartBeat++ + throw IllegalArgumentException() + }) + + val source = PublishSubject.create() + source.resilientOnError().subscribe(customSafeSubscriber) // it should not replace CustomSafeSubscriber with ResilientSubscriber + + assertFailsWith { source.onNext(1) } + source.onNext(1) + assertEquals(1, heartBeat) + } + @Test(timeout=300_000) fun `combine tee and bufferUntilDatabaseCommit`() { val database = createDatabase() @@ -359,4 +565,6 @@ class ObservablesTests { subscription3.unsubscribe() } + + class CustomSafeSubscriber(actual: Subscriber): SafeSubscriber(actual) } \ No newline at end of file diff --git a/testing/cordapps/dbfailure/dbfcontracts/src/main/kotlin/com/r3/dbfailure/contracts/DbFailureContract.kt b/testing/cordapps/dbfailure/dbfcontracts/src/main/kotlin/com/r3/dbfailure/contracts/DbFailureContract.kt index c344badebb..a9b3b45dce 100644 --- a/testing/cordapps/dbfailure/dbfcontracts/src/main/kotlin/com/r3/dbfailure/contracts/DbFailureContract.kt +++ b/testing/cordapps/dbfailure/dbfcontracts/src/main/kotlin/com/r3/dbfailure/contracts/DbFailureContract.kt @@ -1,12 +1,13 @@ package com.r3.dbfailure.contracts import com.r3.dbfailure.schemas.DbFailureSchemaV1 +import net.corda.core.contracts.CommandAndState import net.corda.core.contracts.CommandData import net.corda.core.contracts.Contract import net.corda.core.contracts.LinearState +import net.corda.core.contracts.OwnableState import net.corda.core.contracts.UniqueIdentifier import net.corda.core.identity.AbstractParty -import net.corda.core.identity.Party import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState @@ -21,23 +22,31 @@ class DbFailureContract : Contract { class TestState( override val linearId: UniqueIdentifier, - val particpant: Party, + override val participants: List, val randomValue: String?, - val errorTarget: Int = 0 - ) : LinearState, QueryableState { + val errorTarget: Int = 0, + override val owner: AbstractParty + ) : LinearState, QueryableState, OwnableState { - override val participants: List = listOf(particpant) override fun supportedSchemas(): Iterable = listOf(DbFailureSchemaV1) override fun generateMappedObject(schema: MappedSchema): PersistentState { return if (schema is DbFailureSchemaV1){ - DbFailureSchemaV1.PersistentTestState( particpant.name.toString(), randomValue, errorTarget, linearId.id) + DbFailureSchemaV1.PersistentTestState( participants.toString(), randomValue, errorTarget, linearId.id) } else { throw IllegalArgumentException("Unsupported schema $schema") } } + + override fun withNewOwner(newOwner: AbstractParty): CommandAndState { + return CommandAndState(Commands.Send(), TestState(this.linearId, this.participants.plus(newOwner).toSet().toList(), this.randomValue, this.errorTarget, newOwner)) + } + + fun withNewOwnerAndErrorTarget(newOwner: AbstractParty, errorTarget: Int): CommandAndState { + return CommandAndState(Commands.Send(), TestState(this.linearId, this.participants.plus(newOwner).toSet().toList(), this.randomValue, errorTarget, newOwner)) + } } override fun verify(tx: LedgerTransaction) { @@ -46,5 +55,6 @@ class DbFailureContract : Contract { interface Commands : CommandData{ class Create: Commands + class Send : Commands } } \ No newline at end of file diff --git a/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/CreateStateFlow.kt b/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/CreateStateFlow.kt index 98dc5e201d..af1d9a20bd 100644 --- a/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/CreateStateFlow.kt +++ b/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/CreateStateFlow.kt @@ -20,14 +20,16 @@ object CreateStateFlow { // 1000s control exception handlling in the service/vault listener enum class ErrorTarget(val targetNumber: Int) { NoError(0), - ServiceSqlSyntaxError(1), - ServiceNullConstraintViolation(2), - ServiceValidUpdate(3), - ServiceReadState(4), - ServiceCheckForState(5), - ServiceThrowInvalidParameter(6), - ServiceThrowMotherOfAllExceptions(7), - ServiceThrowUnrecoverableError(8), + ServiceSqlSyntaxError(10000), + ServiceNullConstraintViolation(20000), + ServiceValidUpdate(30000), + ServiceReadState(40000), + ServiceCheckForState(50000), + ServiceThrowInvalidParameter(60000), + ServiceThrowMotherOfAllExceptions(70000), + ServiceThrowUnrecoverableError(80000), + ServiceSqlSyntaxErrorOnConsumed(90000), + ServiceConstraintViolationException(1000000), TxInvalidState(10), FlowSwallowErrors(100), ServiceSwallowErrors(1000) @@ -40,7 +42,7 @@ object CreateStateFlow { private val targetMap = ErrorTarget.values().associateBy(ErrorTarget::targetNumber) fun getServiceTarget(target: Int?): ErrorTarget { - return target?.let { targetMap.getValue(it % 10) } ?: CreateStateFlow.ErrorTarget.NoError + return target?.let { targetMap.getValue(((it/10000) % 1000)*10000) } ?: CreateStateFlow.ErrorTarget.NoError } fun getServiceExceptionHandlingTarget(target: Int?): ErrorTarget { @@ -69,10 +71,11 @@ object CreateStateFlow { val txTarget = getTxTarget(errorTarget) logger.info("Test flow: The tx error target is $txTarget") val state = DbFailureContract.TestState( - UniqueIdentifier(), - ourIdentity, - if (txTarget == CreateStateFlow.ErrorTarget.TxInvalidState) null else randomValue, - errorTarget) + UniqueIdentifier(), + listOf(ourIdentity), + if (txTarget == CreateStateFlow.ErrorTarget.TxInvalidState) null else randomValue, + errorTarget, ourIdentity + ) val txCommand = Command(DbFailureContract.Commands.Create(), ourIdentity.owningKey) logger.info("Test flow: tx builder") diff --git a/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/DbListenerService.kt b/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/DbListenerService.kt index af14b50307..d28f9f9bd1 100644 --- a/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/DbListenerService.kt +++ b/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/DbListenerService.kt @@ -4,33 +4,145 @@ import com.r3.dbfailure.contracts.DbFailureContract import net.corda.core.contracts.ContractState import net.corda.core.flows.FlowLogic import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.Party import net.corda.core.node.AppServiceHub import net.corda.core.node.services.CordaService import net.corda.core.node.services.Vault import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.utilities.contextLogger +import org.hibernate.exception.ConstraintViolationException +import rx.Subscriber +import rx.observers.SafeSubscriber +import rx.observers.Subscribers +import java.lang.IllegalStateException import java.security.InvalidParameterException +import java.sql.SQLException @CordaService class DbListenerService(services: AppServiceHub) : SingletonSerializeAsToken() { companion object { val log = contextLogger() + var onError: ((Throwable) -> Unit)? = null // make the service throw an unrecoverable error (should be executed in an outOfProcess node so that it wont halt testing jvm) var throwUnrecoverableError = false + + var safeSubscription = true + var withCustomSafeSubscriber = false + + var onNextVisited: (Party) -> Unit = {} + var onErrorVisited: ((Party) -> Unit)? = null } init { val onNext: (Vault.Update) -> Unit = - { (_, produced) -> - produced.forEach { - val contractState = it.state.data as? DbFailureContract.TestState - @Suppress("TooGenericExceptionCaught") // this is fully intentional here, to allow twiddling with exceptions - try { + { (consumed, produced) -> + + onNextVisited(services.myInfo.legalIdentities.first()) + + produced.forEach { + val contractState = it.state.data as? DbFailureContract.TestState + @Suppress("TooGenericExceptionCaught") // this is fully intentional here, to allow twiddling with exceptions + try { + when (CreateStateFlow.getServiceTarget(contractState?.errorTarget)) { + CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError -> { + log.info("Fail with syntax error on raw statement") + val session = services.jdbcSession() + val statement = session.createStatement() + statement.execute( + "UPDATE FAIL_TEST_STATES \n" + + "BLAAA RANDOM_VALUE = NULL\n" + + "WHERE transaction_id = '${it.ref.txhash}' AND output_index = ${it.ref.index};" + ) + log.info("SQL result: ${statement.resultSet}") + } + CreateStateFlow.ErrorTarget.ServiceNullConstraintViolation -> { + log.info("Fail with null constraint violation on raw statement") + val session = services.jdbcSession() + val statement = session.createStatement() + statement.execute( + "UPDATE FAIL_TEST_STATES \n" + + "SET RANDOM_VALUE = NULL\n" + + "WHERE transaction_id = '${it.ref.txhash}' AND output_index = ${it.ref.index};" + ) + log.info("SQL result: ${statement.resultSet}") + } + CreateStateFlow.ErrorTarget.ServiceValidUpdate -> { + log.info("Update current statement") + val session = services.jdbcSession() + val statement = session.createStatement() + statement.execute( + "UPDATE FAIL_TEST_STATES \n" + + "SET RANDOM_VALUE = '${contractState!!.randomValue} Updated by service'\n" + + "WHERE transaction_id = '${it.ref.txhash}' AND output_index = ${it.ref.index};" + ) + log.info("SQL result: ${statement.resultSet}") + } + CreateStateFlow.ErrorTarget.ServiceReadState -> { + log.info("Read current state from db") + val session = services.jdbcSession() + val statement = session.createStatement() + statement.execute( + "SELECT * FROM FAIL_TEST_STATES \n" + + "WHERE transaction_id = '${it.ref.txhash}' AND output_index = ${it.ref.index};" + ) + log.info("SQL result: ${statement.resultSet}") + } + CreateStateFlow.ErrorTarget.ServiceCheckForState -> { + log.info("Check for currently written state in the db") + val session = services.jdbcSession() + val statement = session.createStatement() + val rs = statement.executeQuery( + "SELECT COUNT(*) FROM FAIL_TEST_STATES \n" + + "WHERE transaction_id = '${it.ref.txhash}' AND output_index = ${it.ref.index};" + ) + val numOfRows = if (rs.next()) rs.getInt("COUNT(*)") else 0 + log.info( + "Found a state with tx:ind ${it.ref.txhash}:${it.ref.index} in " + + "TEST_FAIL_STATES: ${if (numOfRows > 0) "Yes" else "No"}" + ) + } + CreateStateFlow.ErrorTarget.ServiceThrowInvalidParameter -> { + log.info("Throw InvalidParameterException") + throw InvalidParameterException("Toys out of pram") + } + CreateStateFlow.ErrorTarget.ServiceThrowMotherOfAllExceptions -> { + log.info("Throw Exception") + throw Exception("Mother of all exceptions") + } + CreateStateFlow.ErrorTarget.ServiceThrowUnrecoverableError -> { + // this bit of code should only work in a OutOfProcess node, + // otherwise it will kill the testing jvm (including the testing thread) + if (throwUnrecoverableError) { + log.info("Throw Unrecoverable error") + throw OutOfMemoryError("Unrecoverable error") + } + } + CreateStateFlow.ErrorTarget.ServiceConstraintViolationException -> { + log.info("Throw ConstraintViolationException") + throw ConstraintViolationException("Dummy Hibernate Exception ", SQLException(), " Will cause flow retry!") + } + else -> { + // do nothing, everything else must be handled elsewhere + } + } + } catch (t: Throwable) { + if (CreateStateFlow.getServiceExceptionHandlingTarget(contractState?.errorTarget) + == CreateStateFlow.ErrorTarget.ServiceSwallowErrors + ) { + log.warn("Service not letting errors escape", t) + } else { + throw t + } + } + } + consumed.forEach { + val contractState = it.state.data as? DbFailureContract.TestState + log.info("Test Service: Got state ${if (contractState == null) "null" else " test state with error target ${contractState.errorTarget}"}") when (CreateStateFlow.getServiceTarget(contractState?.errorTarget)) { - CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError -> { + CreateStateFlow.ErrorTarget.ServiceSqlSyntaxErrorOnConsumed -> { log.info("Fail with syntax error on raw statement") val session = services.jdbcSession() val statement = session.createStatement() @@ -41,85 +153,33 @@ class DbListenerService(services: AppServiceHub) : SingletonSerializeAsToken() { ) log.info("SQL result: ${statement.resultSet}") } - CreateStateFlow.ErrorTarget.ServiceNullConstraintViolation -> { - log.info("Fail with null constraint violation on raw statement") - val session = services.jdbcSession() - val statement = session.createStatement() - statement.execute( - "UPDATE FAIL_TEST_STATES \n" + - "SET RANDOM_VALUE = NULL\n" + - "WHERE transaction_id = '${it.ref.txhash}' AND output_index = ${it.ref.index};" - ) - log.info("SQL result: ${statement.resultSet}") - } - CreateStateFlow.ErrorTarget.ServiceValidUpdate -> { - log.info("Update current statement") - val session = services.jdbcSession() - val statement = session.createStatement() - statement.execute( - "UPDATE FAIL_TEST_STATES \n" + - "SET RANDOM_VALUE = '${contractState!!.randomValue} Updated by service'\n" + - "WHERE transaction_id = '${it.ref.txhash}' AND output_index = ${it.ref.index};" - ) - log.info("SQL result: ${statement.resultSet}") - } - CreateStateFlow.ErrorTarget.ServiceReadState -> { - log.info("Read current state from db") - val session = services.jdbcSession() - val statement = session.createStatement() - statement.execute( - "SELECT * FROM FAIL_TEST_STATES \n" + - "WHERE transaction_id = '${it.ref.txhash}' AND output_index = ${it.ref.index};" - ) - log.info("SQL result: ${statement.resultSet}") - } - CreateStateFlow.ErrorTarget.ServiceCheckForState -> { - log.info("Check for currently written state in the db") - val session = services.jdbcSession() - val statement = session.createStatement() - val rs = statement.executeQuery( - "SELECT COUNT(*) FROM FAIL_TEST_STATES \n" + - "WHERE transaction_id = '${it.ref.txhash}' AND output_index = ${it.ref.index};" - ) - val numOfRows = if (rs.next()) rs.getInt("COUNT(*)") else 0 - log.info("Found a state with tx:ind ${it.ref.txhash}:${it.ref.index} in " + - "TEST_FAIL_STATES: ${if (numOfRows > 0) "Yes" else "No"}") - } - CreateStateFlow.ErrorTarget.ServiceThrowInvalidParameter -> { - log.info("Throw InvalidParameterException") - throw InvalidParameterException("Toys out of pram") - } - CreateStateFlow.ErrorTarget.ServiceThrowMotherOfAllExceptions -> { - log.info("Throw Exception") - throw Exception("Mother of all exceptions") - } - CreateStateFlow.ErrorTarget.ServiceThrowUnrecoverableError -> { - // this bit of code should only work in a OutOfProcess node, - // otherwise it will kill the testing jvm (including the testing thread) - if (throwUnrecoverableError) { - log.info("Throw Unrecoverable error") - throw OutOfMemoryError("Unrecoverable error") - } - } else -> { // do nothing, everything else must be handled elsewhere } } - } catch (t: Throwable) { - if (CreateStateFlow.getServiceExceptionHandlingTarget(contractState?.errorTarget) - == CreateStateFlow.ErrorTarget.ServiceSwallowErrors) { - log.warn("Service not letting errors escape", t) - } else { - throw t - } } } - } if (onError != null) { - services.vaultService.rawUpdates.subscribe(onNext, onError) // onError is defined + val onErrorWrapper: ((Throwable) -> Unit)? = { + onErrorVisited?.let { + it(services.myInfo.legalIdentities.first()) + } + onError!!(it) + } + services.vaultService.rawUpdates.subscribe(onNext, onErrorWrapper) // onError is defined + } else if (onErrorVisited != null) { + throw IllegalStateException("A DbListenerService.onError needs to be defined!") } else { - services.vaultService.rawUpdates.subscribe(onNext) + if (safeSubscription) { + if (withCustomSafeSubscriber) { + services.vaultService.rawUpdates.subscribe(CustomSafeSubscriber(Subscribers.create(onNext))) + } else { + services.vaultService.rawUpdates.subscribe(onNext) + } + } else { + services.vaultService.rawUpdates.unsafeSubscribe(Subscribers.create(onNext)) + } } } @@ -130,4 +190,6 @@ class DbListenerService(services: AppServiceHub) : SingletonSerializeAsToken() { throwUnrecoverableError = true } } + + class CustomSafeSubscriber(actual: Subscriber): SafeSubscriber(actual) } \ No newline at end of file diff --git a/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/SendStateFlow.kt b/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/SendStateFlow.kt new file mode 100644 index 0000000000..f1b02a9729 --- /dev/null +++ b/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/dbfailure/workflows/SendStateFlow.kt @@ -0,0 +1,88 @@ +package com.r3.dbfailure.workflows + +import co.paralleluniverse.fibers.Suspendable +import com.r3.dbfailure.contracts.DbFailureContract +import net.corda.core.contracts.UniqueIdentifier +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowException +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.ReceiveFinalityFlow +import net.corda.core.flows.StartableByRPC +import net.corda.core.identity.Party +import net.corda.core.node.services.Vault +import net.corda.core.node.services.vault.QueryCriteria +import net.corda.core.transactions.SignedTransaction +import net.corda.core.transactions.TransactionBuilder +import net.corda.core.utilities.unwrap + +object SendStateFlow { + + /** + * Creates a [DbFailureContract.TestState], signs it, collects a signature from a separate node and then calls [FinalityFlow] flow. + * Can throw in various stages + */ + @StartableByRPC + @InitiatingFlow + class PassErroneousOwnableState(private val stateId: UniqueIdentifier, private val errorTarget: Int, private val counterParty: Party) : + FlowLogic() { + + @Suspendable + override fun call() { + logger.info("Test flow: starting") + val notary = serviceHub.networkMapCache.notaryIdentities[0] + logger.info("Test flow: create counterparty session") + val recipientSession = initiateFlow(counterParty) + + val queryCriteria = QueryCriteria.LinearStateQueryCriteria(linearId = listOf(stateId), status = Vault.StateStatus.UNCONSUMED) + val inputState = serviceHub.vaultService.queryBy(DbFailureContract.TestState::class.java, queryCriteria).states.singleOrNull() + ?: throw FlowException("Failed to find single state for linear id $stateId") + + logger.info("Test flow: tx builder") + val commandAndState = inputState.state.data.withNewOwnerAndErrorTarget(counterParty, errorTarget) + val txBuilder = TransactionBuilder(notary) + .addInputState(inputState) + .addOutputState(commandAndState.ownableState) + .addCommand(commandAndState.command, listOf(ourIdentity.owningKey, counterParty.owningKey)) + + + logger.info("Test flow: verify") + txBuilder.verify(serviceHub) + + val signedTx = serviceHub.signInitialTransaction(txBuilder) + + logger.info("Test flow: send for counterparty signing") + recipientSession.send(signedTx) + logger.info("Test flow: Waiting to receive counter signed transaction") + val counterSignedTx = recipientSession.receive().unwrap { it } + logger.info("Test flow: Received counter sigend transaction, invoking finality") + subFlow(FinalityFlow(counterSignedTx, recipientSession)) + + logger.info("Test flow: Finishing") + } + } + + @InitiatedBy(PassErroneousOwnableState::class) + class PassErroneousOwnableStateReceiver(private val otherSide: FlowSession) : FlowLogic() { + @Suspendable + override fun call() { + logger.info("Test flow counterparty: starting") + val signedTx = otherSide.receive().unwrap { it } + logger.info("Test flow counterparty: received TX, signing") + val counterSignedTx = serviceHub.addSignature(signedTx) + logger.info("Test flow counterparty: calling hookBeforeCounterPartyAnswers") + logger.info("Test flow counterparty: Answer with countersigned transaction") + otherSide.send(counterSignedTx) + logger.info("Test flow counterparty: calling hookAfterCounterPartyAnswers") + // Not ideal that we have to do this check, but we must as FinalityFlow does not send locally + if (!serviceHub.myInfo.isLegalIdentity(otherSide.counterparty)) { + logger.info("Test flow counterparty: Waiting for finality") + subFlow(ReceiveFinalityFlow(otherSide)) + } + logger.info("Test flow counterparty: Finishing") + } + } + +} \ No newline at end of file diff --git a/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/transactionfailure/workflows/ErrorHandling.kt b/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/transactionfailure/workflows/ErrorHandling.kt index edabddc0df..7a3ea10d82 100644 --- a/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/transactionfailure/workflows/ErrorHandling.kt +++ b/testing/cordapps/dbfailure/dbfworkflows/src/main/kotlin/com/r3/transactionfailure/workflows/ErrorHandling.kt @@ -30,9 +30,10 @@ object ErrorHandling { val txTarget = CreateStateFlow.getTxTarget(errorTarget) val state = DbFailureContract.TestState( UniqueIdentifier(), - ourIdentity, + listOf(ourIdentity), if (txTarget == CreateStateFlow.ErrorTarget.TxInvalidState) null else "valid hibernate value", - errorTarget) + errorTarget, + ourIdentity) val txCommand = Command(DbFailureContract.Commands.Create(), ourIdentity.owningKey) val txBuilder = TransactionBuilder(notary).addOutputState(state).addCommand(txCommand) val signedTx = serviceHub.signInitialTransaction(txBuilder) @@ -50,5 +51,4 @@ object ErrorHandling { hookAfterSecondCheckpoint.invoke() // should be never executed } } - } \ No newline at end of file From d2568b9077a821d1f5fb591d7997a8f88a7912ae Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 24 Feb 2020 09:36:16 +0000 Subject: [PATCH 47/83] NOTICK: Fix potential infinite loop when a SecurityManager is installed. (#5993) --- .../net/corda/testing/driver/SharedMemoryIncremental.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testing/node-driver/src/main/java/net/corda/testing/driver/SharedMemoryIncremental.java b/testing/node-driver/src/main/java/net/corda/testing/driver/SharedMemoryIncremental.java index fe0d957499..a3ca4e8aef 100644 --- a/testing/node-driver/src/main/java/net/corda/testing/driver/SharedMemoryIncremental.java +++ b/testing/node-driver/src/main/java/net/corda/testing/driver/SharedMemoryIncremental.java @@ -86,7 +86,10 @@ class SharedMemoryIncremental extends PortAllocation { private boolean isLocalPortAvailable(Long portToTest) { try (ServerSocket serverSocket = new ServerSocket(Math.toIntExact(portToTest))) { - } catch (Exception e) { + } catch (IOException e) { + // Don't catch anything other than IOException here in case we + // accidentally create an infinite loop. For example, installing + // a SecurityManager could throw AccessControlException. return false; } return true; From fe625d0f37d37dedd4c21150380f01f83c739877 Mon Sep 17 00:00:00 2001 From: Adel El-Beik <48713346+adelel1@users.noreply.github.com> Date: Tue, 25 Feb 2020 11:18:02 +0000 Subject: [PATCH 48/83] CORDA-3584: Now cope with 2 contract jars with same hash but different name (#5952) * CORDA-3484: Now cope with 2 contract jars with same hash but different name, we just select one and use that. * ENT-3584: Contract jars are now generated on the fly. * CORDA-3584: Reverted changes to CordappProviderImpl. Exception is raised if node started with multiple jars with same hash. * ENT-3584: Fixing test failure. * CORDA-3584: Switch to test extension method instead of reflection to access internal member. * ENT-3584: Address review comment. Dont fully qualify exception. * CORDA-3584: Address review comment and converted lazy to a resettable one. * CORDA-3584: Removed unused logger. * CORDA-3584: Fixed visibility. * CORDA-3584: Removed synchronized * CORDA-3584: Removed CordappResolver * CORDA-3584: Reverted change in gradle file and fixed test. * CORDA-3584: Removed V3 from test description as it wasn't actually V3 specific. * CORDA-3584: Address review comment. Let classes be garbage collected. --- .../corda/confidential/SwapIdentitiesFlow.kt | 3 +- .../coretests/flows/FinalityFlowTests.kt | 11 +- .../net/corda/core/flows/FinalityFlow.kt | 3 +- .../core/internal/cordapp/CordappResolver.kt | 133 ------------------ .../internal/cordapp/CordappResolverTest.kt | 92 ------------ .../cordapp/JarScanningCordappLoader.kt | 31 +++- .../cordapp/CordappProviderImplTests.kt | 33 ++++- .../node/services/FinalityHandlerTest.kt | 10 +- .../services/vault/NodeVaultServiceTest.kt | 28 ++-- 9 files changed, 81 insertions(+), 263 deletions(-) delete mode 100644 core/src/main/kotlin/net/corda/core/internal/cordapp/CordappResolver.kt delete mode 100644 core/src/test/kotlin/net/corda/core/internal/cordapp/CordappResolverTest.kt diff --git a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt index 80bf85d6a3..78f1a6f4f2 100644 --- a/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt +++ b/confidential-identities/src/main/kotlin/net/corda/confidential/SwapIdentitiesFlow.kt @@ -13,7 +13,6 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.internal.VisibleForTesting -import net.corda.core.internal.cordapp.CordappResolver import net.corda.core.internal.warnOnce import net.corda.core.node.ServiceHub import net.corda.core.serialization.CordaSerializable @@ -95,7 +94,7 @@ private constructor(private val otherSideSession: FlowSession?, override fun call(): LinkedHashMap { val session = if (otherParty != null && otherParty != otherSideSession?.counterparty) { logger.warnOnce("The current usage of SwapIdentitiesFlow is unsafe. Please consider upgrading your CorDapp to use " + - "SwapIdentitiesFlow with FlowSessions. (${CordappResolver.currentCordapp?.info})") + "SwapIdentitiesFlow with FlowSessions. (${serviceHub.getAppContext().cordapp.info})") initiateFlow(otherParty) } else { otherSideSession!! diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt index ef37af8364..6ac39993ff 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt @@ -4,7 +4,6 @@ import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import net.corda.core.flows.FinalityFlow import net.corda.core.identity.Party -import net.corda.core.internal.cordapp.CordappResolver import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.getOrThrow @@ -25,7 +24,8 @@ class FinalityFlowTests : WithFinality { private val CHARLIE = TestIdentity(CHARLIE_NAME, 90).party } - override val mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, enclosedCordapp())) + override val mockNet = InternalMockNetwork(cordappsForAllNodes = listOf(FINANCE_CONTRACTS_CORDAPP, enclosedCordapp(), + CustomCordapp(targetPlatformVersion = 3, classes = setOf(FinalityFlow::class.java)))) private val aliceNode = makeNode(ALICE_NAME) @@ -60,11 +60,8 @@ class FinalityFlowTests : WithFinality { fun `allow use of the old API if the CorDapp target version is 3`() { val oldBob = createBob(cordapps = listOf(tokenOldCordapp())) val stx = aliceNode.issuesCashTo(oldBob) - val resultFuture = CordappResolver.withTestCordapp(targetPlatformVersion = 3) { - @Suppress("DEPRECATION") - aliceNode.startFlowAndRunNetwork(FinalityFlow(stx)).resultFuture - } - resultFuture.getOrThrow() + @Suppress("DEPRECATION") + aliceNode.startFlowAndRunNetwork(FinalityFlow(stx)).resultFuture.getOrThrow() assertThat(oldBob.services.validatedTransactions.getTransaction(stx.id)).isNotNull() } diff --git a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt index 33b3ebf038..80a663a123 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FinalityFlow.kt @@ -5,7 +5,6 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.isFulfilledBy import net.corda.core.identity.Party import net.corda.core.identity.groupAbstractPartyByWellKnownParty -import net.corda.core.internal.cordapp.CordappResolver import net.corda.core.internal.pushToLoggingContext import net.corda.core.internal.warnOnce import net.corda.core.node.StatesToRecord @@ -136,7 +135,7 @@ class FinalityFlow private constructor(val transaction: SignedTransaction, override fun call(): SignedTransaction { if (!newApi) { logger.warnOnce("The current usage of FinalityFlow is unsafe. Please consider upgrading your CorDapp to use " + - "FinalityFlow with FlowSessions. (${CordappResolver.currentCordapp?.info})") + "FinalityFlow with FlowSessions. (${serviceHub.getAppContext().cordapp.info})") } else { require(sessions.none { serviceHub.myInfo.isLegalIdentity(it.counterparty) }) { "Do not provide flow sessions for the local node. FinalityFlow will record the notarised transaction locally." 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 deleted file mode 100644 index 024f89d2d0..0000000000 --- a/core/src/main/kotlin/net/corda/core/internal/cordapp/CordappResolver.kt +++ /dev/null @@ -1,133 +0,0 @@ -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 - -/** - * Provides a way to acquire information about the calling CorDapp. - */ -object CordappResolver { - - private val logger = loggerFor() - private val cordappClasses: ConcurrentHashMap> = ConcurrentHashMap() - - private val insideInMemoryTest: Boolean by lazy { insideInMemoryTest() } - - // TODO Use the StackWalker API once we migrate to Java 9+ - private var cordappResolver: () -> Cordapp? = { - Exception().stackTrace - .mapNotNull { cordappClasses[it.className] } - // in case there are multiple classes matched, we select the first one having a single CorDapp registered against it. - .firstOrNull { it.size == 1 } - // otherwise we return null, signalling we cannot reliably determine the current CorDapp. - ?.single() - } - - /** - * Associates class names with CorDapps or logs a warning when a CorDapp is already registered for a given class. - * This could happen when trying to run different versions of the same CorDapp on the same node. - * - * @throws IllegalStateException when multiple CorDapps are registered for the same contract class, - * since this can lead to undefined behaviour. - */ - @Synchronized - fun register(cordapp: 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 ((registeredClassName, registeredCordapps) in alreadyRegistered) { - val duplicateCordapps = registeredCordapps.filter { it.jarHash == cordapp.jarHash }.toSet() - - if (duplicateCordapps.isNotEmpty()) { - logger.warnOnce("The CorDapp (name: ${cordapp.info.shortName}, file: ${cordapp.name}) " + - "is installed multiple times on the node. The following files correspond to the exact same content: " + - "${duplicateCordapps.map { it.name }}") - continue - } - // During in-memory tests, the spawned nodes share the same CordappResolver, so detected conflicts can be spurious. - if (registeredClassName in contractClasses && !insideInMemoryTest) { - throw IllegalStateException("More than one CorDapp installed on the node for contract $registeredClassName. " + - "Please remove the previous version when upgrading to a new version.") - } - - cordappClasses[registeredClassName] = registeredCordapps + cordapp - } - } - - private fun insideInMemoryTest(): Boolean { - return Exception().stackTrace.any { - it.className.startsWith("net.corda.testing.node.internal.InternalMockNetwork") || - it.className.startsWith("net.corda.testing.node.internal.InProcessNode") || - it.className.startsWith("net.corda.testing.node.MockServices") - } - } - - /* - * This should only be used when making a change that would break compatibility with existing CorDapps. The change - * can then be version-gated, meaning the old behaviour is used if the calling CorDapp's target version is lower - * than the platform version that introduces the new behaviour. - * In situations where a `[CordappProvider]` is available the CorDapp context should be obtained from there. - * - * @return Information about the CorDapp from which the invoker is called, null if called outside a CorDapp or the - * calling CorDapp cannot be reliably determined. - */ - val currentCordapp: Cordapp? get() = cordappResolver() - - /** - * Returns the target version of the current calling CorDapp. Defaults to platform version 1 if there isn't one, - * assuming only basic platform capabilities. - */ - val currentTargetVersion: Int get() = currentCordapp?.targetPlatformVersion ?: 1 - - // A list of extra CorDapps added to the current CorDapps list for testing purposes. - private var extraCordappsForTesting = listOf() - - /** - * Return all the CorDapps that were involved in the call stack at the point the provided exception was generated. - * - * This is provided to allow splitting the cost of generating the exception and retrieving the CorDapps involved. - */ - fun cordappsFromException(exception: Exception): List { - val apps = exception.stackTrace - .mapNotNull { cordappClasses[it.className] } - .flatten() - .distinct() - return (apps + extraCordappsForTesting) - } - - /** - * Temporarily apply a fake CorDapp with the given parameters. For use in testing. - */ - @Synchronized - @VisibleForTesting - fun withTestCordapp(minimumPlatformVersion: Int = 1, - targetPlatformVersion: Int = PLATFORM_VERSION, - extraApps: List = listOf(), - block: () -> T): T { - val currentResolver = cordappResolver - cordappResolver = { - CordappImpl.TEST_INSTANCE.copy(minimumPlatformVersion = minimumPlatformVersion, targetPlatformVersion = targetPlatformVersion) - } - extraCordappsForTesting = listOf(cordappResolver()!!) + extraApps - try { - return block() - } finally { - cordappResolver = currentResolver - extraCordappsForTesting = listOf() - } - } - - @VisibleForTesting - internal fun clear() { - cordappClasses.clear() - } -} \ No newline at end of file diff --git a/core/src/test/kotlin/net/corda/core/internal/cordapp/CordappResolverTest.kt b/core/src/test/kotlin/net/corda/core/internal/cordapp/CordappResolverTest.kt deleted file mode 100644 index be8e04c3d3..0000000000 --- a/core/src/test/kotlin/net/corda/core/internal/cordapp/CordappResolverTest.kt +++ /dev/null @@ -1,92 +0,0 @@ -package net.corda.core.internal.cordapp - -import net.corda.core.crypto.SecureHash -import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy -import org.junit.After -import org.junit.Before -import org.junit.Test -import java.lang.IllegalStateException -import kotlin.test.assertEquals - -class CordappResolverTest { - @Before - @After - fun clearCordappInfoResolver() { - CordappResolver.clear() - } - - @Test(timeout=300_000) - fun `the correct cordapp resolver is used after calling withCordappInfo`() { - val defaultTargetVersion = 222 - - CordappResolver.register(CordappImpl.TEST_INSTANCE.copy( - contractClassNames = listOf(javaClass.name), - minimumPlatformVersion = 3, - targetPlatformVersion = defaultTargetVersion - )) - assertEquals(defaultTargetVersion, CordappResolver.currentTargetVersion) - - val expectedTargetVersion = 555 - CordappResolver.withTestCordapp(targetPlatformVersion = expectedTargetVersion) { - val actualTargetVersion = CordappResolver.currentTargetVersion - assertEquals(expectedTargetVersion, actualTargetVersion) - } - assertEquals(defaultTargetVersion, CordappResolver.currentTargetVersion) - } - - @Test(timeout=300_000) - fun `when the same cordapp is registered for the same class multiple times, the resolver deduplicates and returns it as the current one`() { - CordappResolver.register(CordappImpl.TEST_INSTANCE.copy( - contractClassNames = listOf(javaClass.name), - minimumPlatformVersion = 3, - targetPlatformVersion = 222 - )) - CordappResolver.register(CordappImpl.TEST_INSTANCE.copy( - contractClassNames = listOf(javaClass.name), - minimumPlatformVersion = 2, - targetPlatformVersion = 456 - )) - assertThat(CordappResolver.currentCordapp).isNotNull() - } - - @Test(timeout=300_000) - fun `when different cordapps are registered for the same (non-contract) class, the resolver returns null`() { - CordappResolver.register(CordappImpl.TEST_INSTANCE.copy( - contractClassNames = listOf("ContractClass1"), - minimumPlatformVersion = 3, - targetPlatformVersion = 222, - jarHash = SecureHash.randomSHA256() - )) - CordappResolver.register(CordappImpl.TEST_INSTANCE.copy( - contractClassNames = listOf("ContractClass2"), - minimumPlatformVersion = 2, - targetPlatformVersion = 456, - jarHash = SecureHash.randomSHA256() - )) - assertThat(CordappResolver.currentCordapp).isNull() - } - - @Test(timeout=300_000) - fun `when different cordapps are registered for the same (contract) class, the resolver throws an exception`() { - val firstCordapp = CordappImpl.TEST_INSTANCE.copy( - contractClassNames = listOf(javaClass.name), - minimumPlatformVersion = 3, - targetPlatformVersion = 222, - jarHash = SecureHash.randomSHA256() - ) - val secondCordapp = CordappImpl.TEST_INSTANCE.copy( - contractClassNames = listOf(javaClass.name), - minimumPlatformVersion = 2, - targetPlatformVersion = 456, - jarHash = SecureHash.randomSHA256() - ) - - CordappResolver.register(firstCordapp) - assertThatThrownBy { CordappResolver.register(secondCordapp) } - .isInstanceOf(IllegalStateException::class.java) - .hasMessageContaining("More than one CorDapp installed on the node for contract ${javaClass.name}. " + - "Please remove the previous version when upgrading to a new version.") - } - -} diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt index 0eba7d18ad..b3a93e5655 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt @@ -9,7 +9,6 @@ import net.corda.core.flows.* import net.corda.core.internal.* import net.corda.core.internal.cordapp.CordappImpl import net.corda.core.internal.cordapp.CordappImpl.Companion.UNKNOWN_INFO -import net.corda.core.internal.cordapp.CordappResolver import net.corda.core.internal.cordapp.get import net.corda.core.internal.notary.NotaryService import net.corda.core.internal.notary.SinglePartyNotaryService @@ -30,6 +29,7 @@ import java.net.URL import java.net.URLClassLoader import java.nio.file.Path import java.util.* +import java.util.concurrent.ConcurrentHashMap import java.util.jar.JarInputStream import java.util.jar.Manifest import java.util.zip.ZipInputStream @@ -52,7 +52,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: logger.info("Loading CorDapps from ${cordappJarPaths.joinToString()}") } } - + private val cordappClasses: ConcurrentHashMap> = ConcurrentHashMap() override val cordapps: List by lazy { loadCordapps() + extraCordapps } override val appClassLoader: URLClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader) @@ -128,10 +128,35 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: } } } - cordapps.forEach(CordappResolver::register) + cordapps.forEach(::register) return cordapps } + private fun register(cordapp: 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 ((registeredClassName, registeredCordapps) in alreadyRegistered) { + val duplicateCordapps = registeredCordapps.filter { it.jarHash == cordapp.jarHash }.toSet() + + if (duplicateCordapps.isNotEmpty()) { + throw IllegalStateException("The CorDapp (name: ${cordapp.info.shortName}, file: ${cordapp.name}) " + + "is installed multiple times on the node. The following files correspond to the exact same content: " + + "${duplicateCordapps.map { it.name }}") + } + if (registeredClassName in contractClasses) { + throw IllegalStateException("More than one CorDapp installed on the node for contract $registeredClassName. " + + "Please remove the previous version when upgrading to a new version.") + } + cordappClasses[registeredClassName] = registeredCordapps + cordapp + } + } + private fun RestrictedScanResult.toCordapp(url: RestrictedURL): CordappImpl { val manifest: Manifest? = url.url.openStream().use { JarInputStream(it).manifest } val info = parseCordappInfo(manifest, CordappImpl.jarName(url.url)) diff --git a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt index ed38ebd7af..f981b11e24 100644 --- a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt +++ b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt @@ -5,7 +5,8 @@ import com.typesafe.config.ConfigFactory import net.corda.core.node.services.AttachmentId import net.corda.core.node.services.AttachmentStorage import net.corda.node.VersionInfo -import net.corda.testing.common.internal.testNetworkParameters +import net.corda.testing.core.internal.ContractJarTestUtils +import net.corda.testing.core.internal.SelfCleaningDir import net.corda.testing.internal.MockCordappConfigProvider import net.corda.testing.services.MockAttachmentStorage import org.assertj.core.api.Assertions.assertThat @@ -14,7 +15,9 @@ import org.junit.Before import org.junit.Test import java.io.File import java.io.FileOutputStream +import java.lang.IllegalStateException import java.net.URL +import java.nio.file.Files import java.util.jar.JarOutputStream import java.util.zip.Deflater.NO_COMPRESSION import java.util.zip.ZipEntry @@ -56,7 +59,6 @@ class CordappProviderImplTests { } private lateinit var attachmentStore: AttachmentStorage - private val whitelistedContractImplementations = testNetworkParameters().whitelistedContractImplementations @Before fun setup() { @@ -189,6 +191,33 @@ class CordappProviderImplTests { assertThat(fixedIDs).containsExactlyInAnyOrder(ID2, ID4) } + @Test(timeout=300_000) + fun `test an exception is raised when we have two jars with the same hash`() { + + SelfCleaningDir().use { file -> + val jarAndSigner = ContractJarTestUtils.makeTestSignedContractJar(file.path, "com.example.MyContract") + val signedJarPath = jarAndSigner.first + val duplicateJarPath = signedJarPath.parent.resolve("duplicate-" + signedJarPath.fileName) + + Files.copy(signedJarPath, duplicateJarPath) + assertFailsWith { + newCordappProvider(signedJarPath.toUri().toURL(), duplicateJarPath.toUri().toURL()) + } + } + } + + @Test(timeout=300_000) + fun `test an exception is raised when two jars share a contract`() { + + SelfCleaningDir().use { file -> + val jarA = ContractJarTestUtils.makeTestContractJar(file.path, listOf("com.example.MyContract", "com.example.AnotherContractForA"), generateManifest = false, jarFileName = "sampleA.jar") + val jarB = ContractJarTestUtils.makeTestContractJar(file.path, listOf("com.example.MyContract", "com.example.AnotherContractForB"), generateManifest = false, jarFileName = "sampleB.jar") + assertFailsWith { + newCordappProvider(jarA.toUri().toURL(), jarB.toUri().toURL()) + } + } + } + private fun File.writeFixupRules(vararg lines: String): File { JarOutputStream(FileOutputStream(this)).use { jar -> jar.setMethod(DEFLATED) diff --git a/node/src/test/kotlin/net/corda/node/services/FinalityHandlerTest.kt b/node/src/test/kotlin/net/corda/node/services/FinalityHandlerTest.kt index d67b9c588b..594932f5c0 100644 --- a/node/src/test/kotlin/net/corda/node/services/FinalityHandlerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/FinalityHandlerTest.kt @@ -5,7 +5,6 @@ import net.corda.core.contracts.TransactionVerificationException import net.corda.core.crypto.SecureHash import net.corda.core.flows.FinalityFlow import net.corda.core.flows.StateMachineRunId -import net.corda.core.internal.cordapp.CordappResolver import net.corda.core.toFuture import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder @@ -35,7 +34,8 @@ class FinalityHandlerTest { fun `sent to flow hospital on error and attempted retry on node restart`() { // Setup a network where only Alice has the finance CorDapp and it sends a cash tx to Bob who doesn't have the // CorDapp. Bob's FinalityHandler will error when validating the tx. - val alice = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, additionalCordapps = FINANCE_CORDAPPS)) + val alice = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME, + additionalCordapps = FINANCE_CORDAPPS + CustomCordapp(targetPlatformVersion = 3, classes = setOf(FinalityFlow::class.java)))) var bob = mockNet.createNode(InternalMockNodeParameters( legalName = BOB_NAME, @@ -82,11 +82,9 @@ class FinalityHandlerTest { } private fun TestStartedNode.finaliseWithOldApi(stx: SignedTransaction): CordaFuture { - return CordappResolver.withTestCordapp(targetPlatformVersion = 3) { - @Suppress("DEPRECATION") - services.startFlow(FinalityFlow(stx)).resultFuture.apply { + @Suppress("DEPRECATION") + return services.startFlow(FinalityFlow(stx)).resultFuture.apply { mockNet.runNetwork() - } } } diff --git a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt index ac468f5533..e1bb3d136e 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/NodeVaultServiceTest.kt @@ -2,7 +2,6 @@ package net.corda.node.services.vault import co.paralleluniverse.fibers.Suspendable import com.nhaarman.mockito_kotlin.argThat -import com.nhaarman.mockito_kotlin.doNothing import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.* @@ -11,7 +10,6 @@ import net.corda.core.crypto.SecureHash import net.corda.core.crypto.generateKeyPair import net.corda.core.identity.* import net.corda.core.internal.NotaryChangeTransactionBuilder -import net.corda.core.internal.cordapp.CordappResolver import net.corda.core.internal.packageName import net.corda.core.node.NotaryInfo import net.corda.core.node.StatesToRecord @@ -887,7 +885,7 @@ class NodeVaultServiceTest { } @Test(timeout=300_000) - fun `V3 vault queries return all states by default`() { + fun `Vault queries return all states by default`() { fun createTx(number: Int, vararg participants: Party): SignedTransaction { return services.signInitialTransaction(TransactionBuilder(DUMMY_NOTARY).apply { addOutputState(DummyState(number, participants.toList()), DummyContract.PROGRAM_ID) @@ -897,20 +895,18 @@ class NodeVaultServiceTest { fun List>.getNumbers() = map { it.state.data.magicNumber }.toSet() - CordappResolver.withTestCordapp(targetPlatformVersion = 3) { - services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(1, megaCorp.party))) - services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(2, miniCorp.party))) - services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(3, miniCorp.party, megaCorp.party))) - services.recordTransactions(StatesToRecord.ALL_VISIBLE, listOf(createTx(4, miniCorp.party))) - services.recordTransactions(StatesToRecord.ALL_VISIBLE, listOf(createTx(5, bankOfCorda.party))) - services.recordTransactions(StatesToRecord.ALL_VISIBLE, listOf(createTx(6, megaCorp.party, bankOfCorda.party))) - services.recordTransactions(StatesToRecord.NONE, listOf(createTx(7, bankOfCorda.party))) + services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(1, megaCorp.party))) + services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(2, miniCorp.party))) + services.recordTransactions(StatesToRecord.ONLY_RELEVANT, listOf(createTx(3, miniCorp.party, megaCorp.party))) + services.recordTransactions(StatesToRecord.ALL_VISIBLE, listOf(createTx(4, miniCorp.party))) + services.recordTransactions(StatesToRecord.ALL_VISIBLE, listOf(createTx(5, bankOfCorda.party))) + services.recordTransactions(StatesToRecord.ALL_VISIBLE, listOf(createTx(6, megaCorp.party, bankOfCorda.party))) + services.recordTransactions(StatesToRecord.NONE, listOf(createTx(7, bankOfCorda.party))) - // Test one. - // RelevancyStatus is ALL by default. This should return five states. - val resultOne = vaultService.queryBy().states.getNumbers() - assertEquals(setOf(1, 3, 4, 5, 6), resultOne) - } + // Test one. + // RelevancyStatus is ALL by default. This should return five states. + val resultOne = vaultService.queryBy().states.getNumbers() + assertEquals(setOf(1, 3, 4, 5, 6), resultOne) // We should never see 2 or 7. } From 275e80109437cc180cbdbcb5eb4205ec85343b57 Mon Sep 17 00:00:00 2001 From: Viktor Kolomeyko Date: Tue, 25 Feb 2020 14:10:59 +0000 Subject: [PATCH 49/83] ENT-4955: Hide usage of FlowHospital inside StateMachineManager (#5997) --- .../main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt | 2 +- .../statemachine/SingleThreadedStateMachineManager.kt | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) 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 402d6ee368..571c97b82c 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -167,7 +167,7 @@ internal class CordaRPCOpsImpl( return snapshot } - override fun killFlow(id: StateMachineRunId): Boolean = if (smm.killFlow(id)) true else smm.flowHospital.dropSessionInit(id) + override fun killFlow(id: StateMachineRunId): Boolean = smm.killFlow(id) override fun stateMachinesFeed(): DataFeed, StateMachineUpdate> { 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 34c9b77582..637400b128 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 @@ -229,7 +229,7 @@ class SingleThreadedStateMachineManager( } override fun killFlow(id: StateMachineRunId): Boolean { - return mutex.locked { + val killFlowResult = mutex.locked { cancelTimeoutIfScheduled(id) val flow = flows.remove(id) if (flow != null) { @@ -257,6 +257,11 @@ class SingleThreadedStateMachineManager( } } } + return if(killFlowResult) { + true + } else { + flowHospital.dropSessionInit(id) + } } override fun addSessionBinding(flowId: StateMachineRunId, sessionId: SessionId) { From 4ea74a45b5928d522b16b912b7533a2efff8f3f1 Mon Sep 17 00:00:00 2001 From: amosmwsmith <52164621+amosmwsmith@users.noreply.github.com> Date: Tue, 25 Feb 2020 17:28:04 +0000 Subject: [PATCH 50/83] =?UTF-8?q?ENT-5018:=20Updated=20documentation=20for?= =?UTF-8?q?=20RPC=20Settings=20address=20field=20based=20on=20client=20?= =?UTF-8?q?=E2=80=A6=20(#5991)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated documentation for RPC Settings address field based on client feedback explain implications of specifying 0.0.0.0 as host. * Further updates to p2pAddress and messagingServerAddress sections of node settings. * Further updates to additionalP2pAddresses and explaining localhost vs loopback address implications. * Correct messagingServerAddress and amend adminAddress section. --- docs/source/corda-configuration-file.rst | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index a8f80f2834..c3642f38b2 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -106,6 +106,9 @@ overridden via: Limitations ``````````` +* Please note that to limit external connections to your node please use loopback address 127.0.0.1 instead of + localhost for client settings such as p2pAddress; since localhost is translated internally to the physical hostname + and can be reached externally. * If the same key is overridden by both an environment variable and system property, the system property takes precedence. @@ -150,6 +153,7 @@ Configuration file fields additionalP2PAddresses An array of additional host:port values, which will be included in the advertised NodeInfo in the network map in addition to the :ref:`p2pAddress `. Nodes can use this configuration option to advertise HA endpoints and aliases to external parties. + 0.0.0.0 is not a valid host setting since each additionalP2PAddress must be an external client address. *Default:* empty list @@ -414,7 +418,8 @@ lazyBridgeStart messagingServerAddress The address of the ArtemisMQ broker instance. If not provided the node will run one locally. - + 0.0.0.0 should not be specified since this needs to be a valid client address. + *Default:* not defined messagingServerExternal @@ -549,9 +554,11 @@ p2pAddress However, note that the host is the included as the advertised entry in the network map. As a result the value listed here must be **externally accessible when running nodes across a cluster of machines.** If the provided host is unreachable, the node will try to auto-discover its public one. + 0.0.0.0 is not a valid host setting since p2pAddress must be an external client address. + *Default:* not defined - + rpcAddress (deprecated) The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC. @@ -568,12 +575,13 @@ rpcSettings **Important: The RPC SSL certificate is used by RPC clients to authenticate the connection. The Node operator must provide RPC clients with a truststore containing the certificate they can trust. We advise Node operators to not use the P2P keystore for RPC. The node can be run with the "generate-rpc-ssl-settings" command, which generates a secure keystore and truststore that can be used to secure the RPC connection. You can use this if you have no special requirements.** address - host and port for the RPC server binding. + host and port for the RPC server binding. Specifying 0.0.0.0 (as host) is a convention allowing the host to bind all of it's network interfaces when listening on a socket. By itself 0.0.0.0 is non-routeable. i.e. not a proper address. *Default:* not defined adminAddress host and port for the RPC admin binding (this is the endpoint that the node process will connect to). + this needs to follow the same host rules as address setting (see above) *Default:* not defined From 92a437f333d00655bce2a0e24bfd6c460a7937b8 Mon Sep 17 00:00:00 2001 From: Walter Oggioni <6357328+woggioni@users.noreply.github.com> Date: Wed, 26 Feb 2020 09:49:14 +0000 Subject: [PATCH 51/83] EG-69 - fixed bug with picocli that was causing uncaught exception's stacktrace to always be printed on stderr (#5985) calling `picocli.CommandLine.AbstractHandler#andExit` causes the exception handler to forcefully terminate the program execution (calling `java.lang.System#exit`) at all costs, even in case of `ExecutionException` being throw by `picocli.CommandLine#parseWithHandlers(picocli.CommandLine.IParseResultHandler2, picocli.CommandLine.IExceptionHandler2, java.lang.String...)`, this was causing the program to always print the exception stack trace on the `stderr` and terminating even before entering the `catch` block from lines 85 to 93 --- .../src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt index 0c12a2dcd5..8bb4130a64 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt +++ b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt @@ -70,9 +70,9 @@ fun CordaCliWrapper.start(args: Array) { } else { Help.Ansi.AUTO } + @Suppress("SpreadOperator") val results = cmd.parseWithHandlers(RunLast().useOut(System.out).useAnsi(defaultAnsiMode), - DefaultExceptionHandler>().useErr(System.err).useAnsi(defaultAnsiMode).andExit(ExitCodes.FAILURE), - *args) + DefaultExceptionHandler>().useErr(System.err).useAnsi(defaultAnsiMode), *args) // If an error code has been returned, use this and exit results?.firstOrNull()?.let { if (it is Int) { From 054563e40c06225c37f2259255aadde4b77e1fd5 Mon Sep 17 00:00:00 2001 From: Denis Rekalov Date: Wed, 26 Feb 2020 10:38:08 +0000 Subject: [PATCH 52/83] ENT-4990: Port AMQP state machine logging and stability fixes from Enterprise to OS --- .../nodeapi/internal/ArtemisConstants.kt | 5 ++ .../engine/ConnectionStateMachine.kt | 55 +++++++++++++----- .../protonwrapper/engine/EventProcessor.kt | 58 +++++++++++++------ 3 files changed, 85 insertions(+), 33 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisConstants.kt diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisConstants.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisConstants.kt new file mode 100644 index 0000000000..f2c887d464 --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/ArtemisConstants.kt @@ -0,0 +1,5 @@ +package net.corda.nodeapi.internal + +object ArtemisConstants { + const val MESSAGE_ID_KEY = "_AMQ_DUPL_ID" +} \ No newline at end of file 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 b79f86cbd1..ce6987e826 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 @@ -7,6 +7,8 @@ import io.netty.channel.Channel import io.netty.channel.ChannelHandlerContext import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.toHexString +import net.corda.nodeapi.internal.ArtemisConstants.MESSAGE_ID_KEY import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.messages.impl.ReceivedMessageImpl import net.corda.nodeapi.internal.protonwrapper.messages.impl.SendableMessageImpl @@ -26,6 +28,8 @@ import org.slf4j.MDC import java.net.InetSocketAddress import java.nio.ByteBuffer import java.util.* +import kotlin.math.max +import kotlin.math.min /** * This ConnectionStateMachine class handles the events generated by the proton-j library to track @@ -51,6 +55,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, MDC.put("serverMode", serverMode.toString()) MDC.put("localLegalName", localLegalName) MDC.put("remoteLegalName", remoteLegalName) + MDC.put("conn", connection.prettyPrint) block() } finally { MDC.setContextMap(oldMDC) @@ -73,12 +78,22 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, private val transport: Transport private val id = UUID.randomUUID().toString() private var session: Session? = null + /** + * Key is message topic and value is the list of messages + */ private val messageQueues = mutableMapOf>() private val unackedQueue = LinkedList() private val receivers = mutableMapOf() private val senders = mutableMapOf() private var tagId: Int = 0 + private val Connection?.prettyPrint: String + get() = this?.context?.toString() ?: "" + + private val Transport?.prettyPrint: String + // Inside Transport's context - there is Connection, inside Connection's context there is NIO channel that has useful information + get() = (this?.context as? Endpoint)?.context?.toString() ?: "" + init { connection = Engine.connection() connection.container = "CORDA:$id" @@ -116,12 +131,12 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, override fun onConnectionInit(event: Event) { val connection = event.connection - logDebugWithMDC { "Connection init $connection" } + logDebugWithMDC { "Connection init ${connection.prettyPrint}" } } override fun onConnectionLocalOpen(event: Event) { val connection = event.connection - logInfoWithMDC("Connection local open $connection") + logInfoWithMDC("Connection local open ${connection.prettyPrint}") val session = connection.session() session.open() this.session = session @@ -132,13 +147,15 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, override fun onConnectionLocalClose(event: Event) { val connection = event.connection - logInfoWithMDC("Connection local close $connection") + logInfoWithMDC("Connection local close ${connection.prettyPrint}") connection.close() connection.free() } override fun onConnectionUnbound(event: Event) { - if (event.connection == this.connection) { + val connection = event.connection + logInfoWithMDC("Connection unbound ${connection.prettyPrint}") + if (connection == this.connection) { val channel = connection.context as? Channel if (channel != null) { if (channel.isActive) { @@ -150,12 +167,13 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, override fun onConnectionFinal(event: Event) { val connection = event.connection - logDebugWithMDC { "Connection final $connection" } + logDebugWithMDC { "Connection final ${connection.prettyPrint}" } if (connection == this.connection) { this.connection.context = null for (queue in messageQueues.values) { // clear any dead messages while (true) { + logDebugWithMDC { "Queue size: ${queue.size}" } val msg = queue.poll() if (msg != null) { msg.doComplete(MessageStatus.Rejected) @@ -167,6 +185,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, } messageQueues.clear() while (true) { + logDebugWithMDC { "Unacked queue size: ${unackedQueue.size}" } val msg = unackedQueue.poll() if (msg != null) { msg.doComplete(MessageStatus.Rejected) @@ -185,26 +204,28 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, session = null receivers.clear() senders.clear() + } else { + logDebugWithMDC { "Connection from the event: ${connection.prettyPrint} is not the connection owned: ${this.connection.prettyPrint}" } } } override fun onTransportHeadClosed(event: Event) { val transport = event.transport - logDebugWithMDC { "Transport Head Closed $transport" } + logDebugWithMDC { "Transport Head Closed ${transport.prettyPrint}" } transport.close_tail() onTransportInternal(transport) } override fun onTransportTailClosed(event: Event) { val transport = event.transport - logDebugWithMDC { "Transport Tail Closed $transport" } + logDebugWithMDC { "Transport Tail Closed ${transport.prettyPrint}" } transport.close_head() onTransportInternal(transport) } override fun onTransportClosed(event: Event) { val transport = event.transport - logDebugWithMDC { "Transport Closed $transport" } + logDebugWithMDC { "Transport Closed ${transport.prettyPrint}" } if (transport == this.transport) { transport.unbind() transport.free() @@ -214,7 +235,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, override fun onTransportError(event: Event) { val transport = event.transport - logInfoWithMDC("Transport Error $transport") + logInfoWithMDC("Transport Error ${transport.prettyPrint}") val condition = event.transport.condition if (condition != null) { logInfoWithMDC("Error: ${condition.description}") @@ -226,7 +247,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, override fun onTransport(event: Event) { val transport = event.transport - logDebugWithMDC { "Transport $transport" } + logDebugWithMDC { "Transport ${transport.prettyPrint}" } onTransportInternal(transport) } @@ -361,7 +382,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, delivery.context = nextMessage sender.send(messageBuf.array(), messageBuf.arrayOffset() + messageBuf.readerIndex(), messageBuf.readableBytes()) nextMessage.status = MessageStatus.Sent - logDebugWithMDC { "Put tag ${javax.xml.bind.DatatypeConverter.printHexBinary(delivery.tag)} on wire uuid: ${nextMessage.applicationProperties["_AMQ_DUPL_ID"]}" } + logDebugWithMDC { "Put tag ${delivery.tag.toHexString()} on wire uuid: ${nextMessage.applicationProperties[MESSAGE_ID_KEY]}" } unackedQueue.offer(nextMessage) sender.advance() } finally { @@ -398,7 +419,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, appProperties, channel, delivery) - logDebugWithMDC { "Full message received uuid: ${appProperties["_AMQ_DUPL_ID"]}" } + logDebugWithMDC { "Full message received uuid: ${appProperties[MESSAGE_ID_KEY]}" } channel.writeAndFlush(receivedMessage) if (link.current() == delivery) { link.advance() @@ -409,7 +430,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, } } } else if (link is Sender) { - logDebugWithMDC { "Sender delivery confirmed tag ${javax.xml.bind.DatatypeConverter.printHexBinary(delivery.tag)}" } + logDebugWithMDC { "Sender delivery confirmed tag ${delivery.tag.toHexString()}" } val ok = delivery.remotelySettled() && delivery.remoteState == Accepted.getInstance() val sourceMessage = delivery.context as? SendableMessageImpl unackedQueue.remove(sourceMessage) @@ -462,6 +483,8 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, if (session != null) { val sender = getSender(msg.topic) transmitMessages(sender) + } else { + logInfoWithMDC("Session been closed already") } } @@ -470,7 +493,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, try { do { val buffer = transport.inputBuffer - val limit = Math.min(buffer.remaining(), source.remaining()) + val limit = min(buffer.remaining(), source.remaining()) val duplicate = source.duplicate() duplicate.limit(source.position() + limit) buffer.put(duplicate) @@ -483,7 +506,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, condition.description = ex.message transport.condition = condition transport.close_tail() - transport.pop(Math.max(0, transport.pending())) // Force generation of TRANSPORT_HEAD_CLOSE (not in C code) + transport.pop(max(0, transport.pending())) // Force generation of TRANSPORT_HEAD_CLOSE (not in C code) } } @@ -508,7 +531,7 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, condition.description = ex.message transport.condition = condition transport.close_head() - transport.pop(Math.max(0, transport.pending())) // Force generation of TRANSPORT_HEAD_CLOSE (not in C code) + transport.pop(max(0, transport.pending())) // Force generation of TRANSPORT_HEAD_CLOSE (not in C code) } } } \ No newline at end of file diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessor.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessor.kt index 8611373d2b..b91642a840 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessor.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessor.kt @@ -3,6 +3,8 @@ package net.corda.nodeapi.internal.protonwrapper.engine import io.netty.buffer.ByteBuf import io.netty.channel.Channel import io.netty.channel.ChannelHandlerContext +import net.corda.core.internal.declaredField +import net.corda.core.utilities.Try import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.messages.impl.ReceivedMessageImpl @@ -13,7 +15,6 @@ import org.apache.qpid.proton.amqp.messaging.Rejected import org.apache.qpid.proton.amqp.transport.DeliveryState import org.apache.qpid.proton.amqp.transport.ErrorCondition import org.apache.qpid.proton.engine.* -import org.apache.qpid.proton.engine.impl.CollectorImpl import org.apache.qpid.proton.reactor.FlowController import org.apache.qpid.proton.reactor.Handshaker import org.slf4j.MDC @@ -21,6 +22,7 @@ import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock +import kotlin.math.max /** * The EventProcessor class converts calls on the netty scheduler/pipeline @@ -29,12 +31,12 @@ import kotlin.concurrent.withLock * and simple sliding window flow control, so that these events don't have to live inside ConnectionStateMachine. * Everything here is single threaded, because the proton-j library has to be run that way. */ -internal class EventProcessor(channel: Channel, +internal class EventProcessor(private val channel: Channel, private val serverMode: Boolean, private val localLegalName: String, private val remoteLegalName: String, userName: String?, - password: String?) : BaseHandler() { + password: String?) { companion object { private const val FLOW_WINDOW_SIZE = 10 private val log = contextLogger() @@ -45,7 +47,9 @@ internal class EventProcessor(channel: Channel, try { MDC.put("serverMode", serverMode.toString()) MDC.put("localLegalName", localLegalName) + MDC.put("localAddress", channel.localAddress()?.toString()) MDC.put("remoteLegalName", remoteLegalName) + MDC.put("remoteAddress", channel.remoteAddress()?.toString()) block() } finally { MDC.setContextMap(oldMDC) @@ -59,10 +63,13 @@ internal class EventProcessor(channel: Channel, } private val lock = ReentrantLock() + @Volatile private var pendingExecute: Boolean = false + @Volatile + private var processorClosed: Boolean = false private val executor: ScheduledExecutorService = channel.eventLoop() - private val collector = Proton.collector() as CollectorImpl - private val handlers = mutableListOf() + private val collector = Proton.collector() + private val handlers: List private val stateMachine: ConnectionStateMachine = ConnectionStateMachine(serverMode, collector, localLegalName, @@ -73,15 +80,11 @@ internal class EventProcessor(channel: Channel, val connection: Connection = stateMachine.connection init { - addHandler(Handshaker()) - addHandler(FlowController(FLOW_WINDOW_SIZE)) - addHandler(stateMachine) + handlers = listOf(Handshaker(), FlowController(FLOW_WINDOW_SIZE), stateMachine) connection.context = channel tick(stateMachine.connection) } - fun addHandler(handler: Handler) = handlers.add(handler) - private fun popEvent(): Event? { var ev = collector.peek() if (ev != null) { @@ -93,23 +96,28 @@ internal class EventProcessor(channel: Channel, private fun tick(connection: Connection) { lock.withLock { + logDebugWithMDC { "Tick" } try { if ((connection.localState != EndpointState.CLOSED) && !connection.transport.isClosed) { val now = System.currentTimeMillis() - val tickDelay = Math.max(0L, connection.transport.tick(now) - now) + val tickDelay = max(0L, connection.transport.tick(now) - now) executor.schedule({ tick(connection) processEvents() }, tickDelay, TimeUnit.MILLISECONDS) + logDebugWithMDC {"Tick done. Next tick scheduled in $tickDelay ms"} + } else { + logDebugWithMDC { "Connection closed - no more ticking" } } } catch (ex: Exception) { + withMDC { log.info("Tick failed", ex) } connection.transport.close() connection.condition = ErrorCondition() } } } - fun processEvents() { + private fun processEvents() { lock.withLock { pendingExecute = false logDebugWithMDC { "Process Events" } @@ -135,11 +143,27 @@ internal class EventProcessor(channel: Channel, } fun close() { - if (connection.localState != EndpointState.CLOSED) { - connection.close() - processEvents() - connection.free() - processEvents() + lock.withLock { + if (!processorClosed) { + processorClosed = true + connection.logLocalState("Before close") + connection.close() + processEvents() + logDebugWithMDC { "Freeing-up connection" } + connection.free() + processEvents() + connection.logLocalState("After close") + } else { + logDebugWithMDC { "Processor is already closed" } + } + } + } + + private fun Connection.logLocalState(prefix: String) { + if (log.isDebugEnabled) { + val freedTry = Try.on { declaredField("freed").value } + val refcountTry = Try.on { declaredField("refcount").value } + logDebugWithMDC { "$prefix, local state: $localState, freed: $freedTry, refcount: $refcountTry" } } } From 0af88365a18e74db68b634e65a0aeccfc15668f2 Mon Sep 17 00:00:00 2001 From: Denis Rekalov Date: Wed, 26 Feb 2020 10:48:26 +0000 Subject: [PATCH 53/83] ENT-4990: Proper AMQP disconnect handling when remote session was not opened --- .../engine/ConnectionStateMachine.kt | 11 +++ .../engine/EventProcessorTest.kt | 71 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessorTest.kt 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 ce6987e826..1f7d9f398f 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 @@ -305,6 +305,17 @@ internal class ConnectionStateMachine(private val serverMode: Boolean, logDebugWithMDC { "Session final $session" } if (session == this.session) { this.session = null + + // If TRANSPORT_CLOSED event was already processed, the 'transport' in all subsequent events is set to null. + // There is, however, a chance of missing TRANSPORT_CLOSED event, e.g. when disconnect occurs before opening remote session. + // In such cases we must explicitly cleanup the 'transport' in order to guarantee the delivery of CONNECTION_FINAL event. + val transport = event.transport + if (transport == this.transport) { + logDebugWithMDC { "Missed TRANSPORT_CLOSED: force cleanup ${transport.prettyPrint}" } + transport.unbind() + transport.free() + transport.context = null + } } } diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessorTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessorTest.kt new file mode 100644 index 0000000000..6ad42f25c2 --- /dev/null +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessorTest.kt @@ -0,0 +1,71 @@ +package net.corda.nodeapi.internal.protonwrapper.engine + +import com.nhaarman.mockito_kotlin.any +import com.nhaarman.mockito_kotlin.doReturn +import com.nhaarman.mockito_kotlin.mock +import com.nhaarman.mockito_kotlin.whenever +import io.netty.channel.Channel +import io.netty.channel.ChannelFuture +import io.netty.channel.DefaultEventLoop +import io.netty.channel.EventLoop +import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus +import net.corda.nodeapi.internal.protonwrapper.messages.impl.SendableMessageImpl +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.internal.rigorousMock +import org.apache.qpid.proton.amqp.transport.Begin +import org.apache.qpid.proton.amqp.transport.Open +import org.apache.qpid.proton.engine.impl.TransportImpl +import org.junit.Test +import java.util.concurrent.TimeUnit +import kotlin.test.assertEquals + +class EventProcessorTest { + @Test(timeout=300_000) + fun `reject unacknowledged message on disconnect`() { + val executor = DefaultEventLoop() + val channel = channel(executor) + + val eventProcessor = EventProcessor(channel, false, ALICE_NAME.toString(), BOB_NAME.toString(), "username", "password") + eventProcessor.processEventsAsync() + + val msg = SendableMessageImpl("test".toByteArray(), "topic", BOB_NAME.toString(), mock(), mapOf()) + eventProcessor.transportWriteMessage(msg) + eventProcessor.processEventsAsync() + + // Open remote connection and session + (eventProcessor.connection.transport as TransportImpl).also { + Open().invoke(it, null, 0) + Begin().invoke(it, null, 0) + } + eventProcessor.processEventsAsync() + + executor.execute { eventProcessor.close() } + assertEquals(MessageStatus.Rejected, msg.onComplete.get(5, TimeUnit.SECONDS)) + } + + @Test(timeout=300_000) + fun `reject unacknowledged message on disconnect without remote session being open`() { + val executor = DefaultEventLoop() + val channel = channel(executor) + + val eventProcessor = EventProcessor(channel, false, ALICE_NAME.toString(), BOB_NAME.toString(), "username", "password") + eventProcessor.processEventsAsync() + + val msg = SendableMessageImpl("test".toByteArray(), "topic", BOB_NAME.toString(), mock(), mapOf()) + eventProcessor.transportWriteMessage(msg) + eventProcessor.processEventsAsync() + + executor.execute { eventProcessor.close() } + assertEquals(MessageStatus.Rejected, msg.onComplete.get(5, TimeUnit.SECONDS)) + } + + private fun channel(executor: EventLoop) = rigorousMock().also { + doReturn(executor).whenever(it).eventLoop() + doReturn(mock()).whenever(it).writeAndFlush(any()) + doReturn(true).whenever(it).isActive + doReturn(mock()).whenever(it).close() + doReturn(null).whenever(it).localAddress() + doReturn(null).whenever(it).remoteAddress() + } +} \ No newline at end of file From 1bf311ce4f94a745e9e68aa3582afc569bbd3cd9 Mon Sep 17 00:00:00 2001 From: Joel Dudley Date: Thu, 27 Feb 2020 13:27:38 +0000 Subject: [PATCH 54/83] Removes use of notaries.first. (#6008) --- docs/source/api-flows.rst | 2 +- .../net/corda/docs/java/FlowCookbook.java | 15 +++------ .../net/corda/docs/kotlin/FlowCookbook.kt | 15 +++------ .../docs/kotlin/FxTransactionBuildTutorial.kt | 33 +++++++++++++------ .../WorkflowTransactionBuildTutorial.kt | 24 +++++++++++--- .../kotlin/FxTransactionBuildTutorialTest.kt | 3 +- .../WorkflowTransactionBuildTutorialTest.kt | 2 +- 7 files changed, 56 insertions(+), 38 deletions(-) diff --git a/docs/source/api-flows.rst b/docs/source/api-flows.rst index 65c45d28a1..27de7b5c28 100644 --- a/docs/source/api-flows.rst +++ b/docs/source/api-flows.rst @@ -215,7 +215,7 @@ Remember that a transaction generally needs a notary to: * Prevent double-spends if the transaction has inputs * Serve as a timestamping authority if the transaction has a time-window -There are several ways to retrieve a notary from the network map: +A notary can be retrieved from the network map as follows: .. container:: codeset diff --git a/docs/source/example-code/src/main/java/net/corda/docs/java/FlowCookbook.java b/docs/source/example-code/src/main/java/net/corda/docs/java/FlowCookbook.java index 082ce9a264..cabd3d301f 100644 --- a/docs/source/example-code/src/main/java/net/corda/docs/java/FlowCookbook.java +++ b/docs/source/example-code/src/main/java/net/corda/docs/java/FlowCookbook.java @@ -117,19 +117,14 @@ public class FlowCookbook { progressTracker.setCurrentStep(ID_OTHER_NODES); // DOCEND 18 - // A transaction generally needs a notary: + // Every transaction needs a notary: // - To prevent double-spends if the transaction has inputs // - To serve as a timestamping authority if the transaction has a // time-window // We retrieve a notary from the network map. // DOCSTART 01 CordaX500Name notaryName = new CordaX500Name("Notary Service", "London", "GB"); - Party specificNotary = getServiceHub().getNetworkMapCache().getNotary(notaryName); - // Alternatively, we can pick an arbitrary notary from the notary - // list. However, it is always preferable to specify the notary - // explicitly, as the notary list might change when new notaries are - // introduced, or old ones decommissioned. - Party firstNotary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0); + Party notary = getServiceHub().getNetworkMapCache().getNotary(notaryName); // DOCEND 01 // We may also need to identify a specific counterparty. We do so @@ -328,7 +323,7 @@ public class FlowCookbook { // If our transaction has input states or a time-window, we must instantiate it with a // notary. // DOCSTART 19 - TransactionBuilder txBuilder = new TransactionBuilder(specificNotary); + TransactionBuilder txBuilder = new TransactionBuilder(notary); // DOCEND 19 // Otherwise, we can choose to instantiate it without one: @@ -362,7 +357,7 @@ public class FlowCookbook { // An output state can be added as a ``ContractState``, contract class name and notary. // DOCSTART 49 - txBuilder.addOutputState(ourOutputState, DummyContract.PROGRAM_ID, specificNotary); + txBuilder.addOutputState(ourOutputState, DummyContract.PROGRAM_ID, notary); // DOCEND 49 // We can also leave the notary field blank, in which case the transaction's default // notary is used. @@ -372,7 +367,7 @@ public class FlowCookbook { // Or we can add the output state as a ``TransactionState``, which already specifies // the output's contract and notary. // DOCSTART 51 - TransactionState txState = new TransactionState(ourOutputState, DummyContract.PROGRAM_ID, specificNotary); + TransactionState txState = new TransactionState(ourOutputState, DummyContract.PROGRAM_ID, notary); // DOCEND 51 // Commands can be added as ``Command``s. diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/FlowCookbook.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/FlowCookbook.kt index 1d821caf7c..e417f74300 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/FlowCookbook.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/FlowCookbook.kt @@ -97,7 +97,7 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty: progressTracker.currentStep = ID_OTHER_NODES // DOCEND 18 - // A transaction generally needs a notary: + // Every transaction needs a notary: // - To prevent double-spends if the transaction has inputs // - To serve as a timestamping authority if the transaction has a // time-window @@ -107,12 +107,7 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty: organisation = "Notary Service", locality = "London", country = "GB") - val specificNotary: Party = serviceHub.networkMapCache.getNotary(notaryName)!! - // Alternatively, we can pick an arbitrary notary from the notary - // list. However, it is always preferable to specify the notary - // explicitly, as the notary list might change when new notaries are - // introduced, or old ones decommissioned. - val firstNotary: Party = serviceHub.networkMapCache.notaryIdentities.first() + val notary: Party = serviceHub.networkMapCache.getNotary(notaryName)!! // DOCEND 01 // We may also need to identify a specific counterparty. We do so @@ -328,7 +323,7 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty: // If our transaction has input states or a time-window, we must instantiate it with a // notary. // DOCSTART 19 - val txBuilder: TransactionBuilder = TransactionBuilder(specificNotary) + val txBuilder: TransactionBuilder = TransactionBuilder(notary) // DOCEND 19 // Otherwise, we can choose to instantiate it without one: @@ -362,7 +357,7 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty: // An output state can be added as a ``ContractState``, contract class name and notary. // DOCSTART 49 - txBuilder.addOutputState(ourOutputState, DummyContract.PROGRAM_ID, specificNotary) + txBuilder.addOutputState(ourOutputState, DummyContract.PROGRAM_ID, notary) // DOCEND 49 // We can also leave the notary field blank, in which case the transaction's default // notary is used. @@ -372,7 +367,7 @@ class InitiatorFlow(val arg1: Boolean, val arg2: Int, private val counterparty: // Or we can add the output state as a ``TransactionState``, which already specifies // the output's contract and notary. // DOCSTART 51 - val txState: TransactionState = TransactionState(ourOutputState, DummyContract.PROGRAM_ID, specificNotary) + val txState: TransactionState = TransactionState(ourOutputState, DummyContract.PROGRAM_ID, notary) // DOCEND 51 // Commands can be added as ``Command``s. diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/FxTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/FxTransactionBuildTutorial.kt index 1e84ad0f1f..b3496c41b4 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/FxTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/FxTransactionBuildTutorial.kt @@ -1,10 +1,23 @@ package net.corda.docs.kotlin import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.* +import net.corda.core.contracts.Amount +import net.corda.core.contracts.Issued +import net.corda.core.contracts.StateAndContract +import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.withoutIssuer import net.corda.core.crypto.SecureHash import net.corda.core.crypto.TransactionSignature -import net.corda.core.flows.* +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.ReceiveFinalityFlow +import net.corda.core.flows.ReceiveStateAndRefFlow +import net.corda.core.flows.ReceiveTransactionFlow +import net.corda.core.flows.SendStateAndRefFlow +import net.corda.core.flows.SendTransactionFlow import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.node.ServiceHub @@ -24,7 +37,7 @@ private data class FxRequest(val tradeId: String, val amount: Amount>, val owner: Party, val counterparty: Party, - val notary: Party? = null) + val notary: Party) // DOCSTART 1 // This is equivalent to the Cash.generateSpend @@ -38,8 +51,7 @@ private fun gatherOurInputs(serviceHub: ServiceHub, val ourParties = ourKeys.map { serviceHub.identityService.partyFromKey(it) ?: throw IllegalStateException("Unable to resolve party from key") } val fungibleCriteria = QueryCriteria.FungibleAssetQueryCriteria(owner = ourParties) - val notaries = notary ?: serviceHub.networkMapCache.notaryIdentities.first() - val vaultCriteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(notary = listOf(notaries as AbstractParty)) + val vaultCriteria: QueryCriteria = QueryCriteria.VaultQueryCriteria(notary = listOf(notary as AbstractParty)) val logicalExpression = builder { CashSchemaV1.PersistentCashState::currency.equal(amountRequired.token.product.currencyCode) } val cashCriteria = QueryCriteria.VaultCustomQueryCriteria(logicalExpression) @@ -90,19 +102,20 @@ class ForeignExchangeFlow(private val tradeId: String, private val baseCurrencyAmount: Amount>, private val quoteCurrencyAmount: Amount>, private val counterparty: Party, - private val weAreBaseCurrencySeller: Boolean) : FlowLogic() { + private val weAreBaseCurrencySeller: Boolean, + private val notary: Party) : FlowLogic() { @Suspendable override fun call(): SecureHash { // Select correct sides of the Fx exchange to query for. // Specifically we own the assets we wish to sell. // Also prepare the other side query val (localRequest, remoteRequest) = if (weAreBaseCurrencySeller) { - val local = FxRequest(tradeId, baseCurrencyAmount, ourIdentity, counterparty) - val remote = FxRequest(tradeId, quoteCurrencyAmount, counterparty, ourIdentity) + val local = FxRequest(tradeId, baseCurrencyAmount, ourIdentity, counterparty, notary) + val remote = FxRequest(tradeId, quoteCurrencyAmount, counterparty, ourIdentity, notary) Pair(local, remote) } else { - val local = FxRequest(tradeId, quoteCurrencyAmount, ourIdentity, counterparty) - val remote = FxRequest(tradeId, baseCurrencyAmount, counterparty, ourIdentity) + val local = FxRequest(tradeId, quoteCurrencyAmount, ourIdentity, counterparty, notary) + val remote = FxRequest(tradeId, baseCurrencyAmount, counterparty, ourIdentity, notary) Pair(local, remote) } diff --git a/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/txbuild/WorkflowTransactionBuildTutorial.kt b/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/txbuild/WorkflowTransactionBuildTutorial.kt index 01829ddcc8..ba8b081158 100644 --- a/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/txbuild/WorkflowTransactionBuildTutorial.kt +++ b/docs/source/example-code/src/main/kotlin/net/corda/docs/kotlin/txbuild/WorkflowTransactionBuildTutorial.kt @@ -3,9 +3,24 @@ package net.corda.docs.kotlin.txbuild import co.paralleluniverse.fibers.Suspendable -import net.corda.core.contracts.* +import net.corda.core.contracts.Command +import net.corda.core.contracts.CommandData +import net.corda.core.contracts.Contract +import net.corda.core.contracts.LinearState +import net.corda.core.contracts.StateAndContract +import net.corda.core.contracts.StateAndRef +import net.corda.core.contracts.StateRef +import net.corda.core.contracts.TypeOnlyCommandData +import net.corda.core.contracts.UniqueIdentifier +import net.corda.core.contracts.requireSingleCommand +import net.corda.core.contracts.requireThat import net.corda.core.crypto.TransactionSignature -import net.corda.core.flows.* +import net.corda.core.flows.FinalityFlow +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.ReceiveFinalityFlow import net.corda.core.identity.AbstractParty import net.corda.core.identity.Party import net.corda.core.node.services.queryBy @@ -95,13 +110,12 @@ data class TradeApprovalContract(val blank: Unit? = null) : Contract { */ @InitiatingFlow class SubmitTradeApprovalFlow(private val tradeId: String, - private val counterparty: Party) : FlowLogic>() { + private val counterparty: Party, + private val notary: Party) : FlowLogic>() { @Suspendable override fun call(): StateAndRef { // Manufacture an initial state val tradeProposal = TradeApprovalContract.State(tradeId, ourIdentity, counterparty) - // identify a notary. This might also be done external to the flow - val notary = serviceHub.networkMapCache.notaryIdentities.first() // Create the TransactionBuilder and populate with the new state. val tx = TransactionBuilder(notary).withItems( StateAndContract(tradeProposal, TRADE_APPROVAL_PROGRAM_ID), diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/FxTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/FxTransactionBuildTutorialTest.kt index 45e97656d5..275c6e1304 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/FxTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/FxTransactionBuildTutorialTest.kt @@ -58,7 +58,8 @@ class FxTransactionBuildTutorialTest { POUNDS(100).issuedBy(nodeB.info.singleIdentity().ref(0x01)), DOLLARS(200).issuedBy(nodeA.info.singleIdentity().ref(0x01)), nodeB.info.singleIdentity(), - weAreBaseCurrencySeller = false)).getOrThrow() + weAreBaseCurrencySeller = false, + notary = mockNet.defaultNotaryIdentity)).getOrThrow() // wait for the flow to finish and the vault updates to be done // Get the balances when the vault updates nodeAVaultUpdate.get() diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/txbuild/WorkflowTransactionBuildTutorialTest.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/txbuild/WorkflowTransactionBuildTutorialTest.kt index 85fe0faf59..4a302257b4 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/txbuild/WorkflowTransactionBuildTutorialTest.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/txbuild/WorkflowTransactionBuildTutorialTest.kt @@ -51,7 +51,7 @@ class WorkflowTransactionBuildTutorialTest { // Setup a vault subscriber to wait for successful upload of the proposal to NodeB val nodeBVaultUpdate = bobNode.services.vaultService.updates.toFuture() // Kick of the proposal flow - val flow1 = aliceNode.startFlow(SubmitTradeApprovalFlow("1234", bob)) + val flow1 = aliceNode.startFlow(SubmitTradeApprovalFlow("1234", bob, mockNet.defaultNotaryIdentity)) // Wait for the flow to finish val proposalRef = flow1.getOrThrow() val proposalLinearId = proposalRef.state.data.linearId From d037dc0d06c2b33d3be760e20aaae1be45042e1a Mon Sep 17 00:00:00 2001 From: pnemeth Date: Fri, 28 Feb 2020 12:16:53 +0000 Subject: [PATCH 55/83] EG-466 - Fix bug: (#5999) 'shutdown' command is repeated twice in the list of available shell commands. --- .../net/corda/tools/shell/RunShellCommand.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java index df75fba4fd..d3c3f9f286 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/RunShellCommand.java @@ -1,6 +1,5 @@ package net.corda.tools.shell; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import net.corda.client.jackson.StringToMethodCallParser; import net.corda.core.messaging.CordaRPCOps; @@ -13,6 +12,7 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -56,6 +56,9 @@ public class RunShellCommand extends InteractiveShellCommand { // Each element we emit is a map of column -> content. Set> entries = cordaRpcOpsParser.getAvailableCommands().entrySet(); List> entryList = new ArrayList<>(entries); + + entryList.add(new AbstractMap.SimpleEntry<>("gracefulShutdown", ""));//Shell only command + entryList.sort(comparing(Map.Entry::getKey)); for (Map.Entry entry : entryList) { // Skip these entries as they aren't really interesting for the user. @@ -68,17 +71,6 @@ public class RunShellCommand extends InteractiveShellCommand { throw new RuntimeException(e); } } - - Lists.newArrayList( - commandAndDesc("shutdown", "Shuts node down (immediately)"), - commandAndDesc("gracefulShutdown", "Shuts node down gracefully, waiting for all flows to complete first.") - ).forEach(stringStringMap -> { - try { - context.provide(stringStringMap); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); } @NotNull From f6c30453a273c4a0234718ff410a207544d1c9b0 Mon Sep 17 00:00:00 2001 From: Andrei Palade <2119610+palade@users.noreply.github.com> Date: Mon, 2 Mar 2020 09:26:46 +0000 Subject: [PATCH 56/83] EG-71 - Changed the default file limit in the log4j2 configuration (#5996) * Changed the default file limit in the log4j2 configuration to address roll over issue described in EG-71 * Updated the default file limit in the comment --- config/dev/log4j2.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml index fe67301f84..051518c4a6 100644 --- a/config/dev/log4j2.xml +++ b/config/dev/log4j2.xml @@ -66,8 +66,8 @@ - + @@ -93,7 +93,7 @@ - + From da3adb40b4e9d3d6cb5cf91ef2eec6b4c9a9585a Mon Sep 17 00:00:00 2001 From: jakubbielawa Date: Mon, 2 Mar 2020 11:14:55 +0000 Subject: [PATCH 57/83] ENT-5039 Improved help text for commands (#6006) * Improved help text for commands * Address feedback --- build.gradle | 2 +- .../net/corda/tools/shell/AttachmentShellCommand.java | 2 ++ .../net/corda/tools/shell/CheckpointShellCommand.java | 2 ++ .../main/java/net/corda/tools/shell/FlowShellCommand.java | 8 ++++++-- .../net/corda/tools/shell/HashLookupShellCommand.java | 6 +++++- .../java/net/corda/tools/shell/OutputFormatCommand.java | 1 + 6 files changed, 17 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 3db5d81af9..b43b778053 100644 --- a/build.gradle +++ b/build.gradle @@ -104,7 +104,7 @@ buildscript { ext.dependency_checker_version = '5.2.0' ext.commons_collections_version = '4.3' ext.beanutils_version = '1.9.3' - ext.crash_version = '1.7.2' + ext.crash_version = '1.7.4' ext.jsr305_version = constants.getProperty("jsr305Version") ext.shiro_version = '1.4.1' ext.artifactory_plugin_version = constants.getProperty('artifactoryPluginVersion') diff --git a/tools/shell/src/main/java/net/corda/tools/shell/AttachmentShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/AttachmentShellCommand.java index 1e58eb1c9c..ec075e38fb 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/AttachmentShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/AttachmentShellCommand.java @@ -2,6 +2,7 @@ package net.corda.tools.shell; import org.crsh.cli.Command; import org.crsh.cli.Man; +import org.crsh.cli.Usage; import static net.corda.tools.shell.InteractiveShell.runAttachmentTrustInfoView; @@ -9,6 +10,7 @@ public class AttachmentShellCommand extends InteractiveShellCommand { @Command @Man("Displays the trusted CorDapp attachments that have been manually installed or received over the network") + @Usage("Displays the trusted CorDapp attachments that have been manually installed or received over the network") public void trustInfo() { runAttachmentTrustInfoView(out, ops()); } diff --git a/tools/shell/src/main/java/net/corda/tools/shell/CheckpointShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/CheckpointShellCommand.java index 8c0f50666c..19b5e99a53 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/CheckpointShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/CheckpointShellCommand.java @@ -2,6 +2,7 @@ package net.corda.tools.shell; import org.crsh.cli.Command; import org.crsh.cli.Man; +import org.crsh.cli.Usage; import static net.corda.tools.shell.InteractiveShell.*; @@ -9,6 +10,7 @@ public class CheckpointShellCommand extends InteractiveShellCommand { @Command @Man("Outputs the contents of all checkpoints as json to be manually reviewed") + @Usage("Outputs the contents of all checkpoints as json to be manually reviewed") public void dump() { runDumpCheckpoints(ops()); } diff --git a/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java index a99c767e54..d0120ed36f 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/FlowShellCommand.java @@ -31,7 +31,11 @@ public class FlowShellCommand extends InteractiveShellCommand { private static final Logger logger = LoggerFactory.getLogger(FlowShellCommand.class); @Command - @Usage("Start a (work)flow on the node. This is how you can change the ledger.") + @Usage("Start a (work)flow on the node. This is how you can change the ledger.\n\n" + + "\t\t Starting flow is the primary way in which you command the node to change the ledger.\n" + + "\t\t This command is generic, so the right way to use it depends on the flow you wish to start. You can use the 'flow start'\n" + + "\t\t command with either a full class name, or a substring of the class name that's unambiguous. The parameters to the\n" + + "\t\t flow constructors (the right one is picked automatically) are then specified using the same syntax as for the run command.\n") public void start( @Usage("The class name of the flow to run, or an unambiguous substring") @Argument String name, @Usage("The data to pass as input") @Argument(unquote = false) List input @@ -55,7 +59,7 @@ public class FlowShellCommand extends InteractiveShellCommand { ANSIProgressRenderer ansiProgressRenderer, ObjectMapper om) { if (name == null) { - out.println("You must pass a name for the flow, see 'man flow'", Decoration.bold, Color.red); + out.println("You must pass a name for the flow. Example: \"start Yo target: Some other company\"", Decoration.bold, Color.red); return; } String inp = input == null ? "" : String.join(" ", input).trim(); diff --git a/tools/shell/src/main/java/net/corda/tools/shell/HashLookupShellCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/HashLookupShellCommand.java index 53b7c147d4..79cf1ee273 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/HashLookupShellCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/HashLookupShellCommand.java @@ -28,7 +28,11 @@ public class HashLookupShellCommand extends InteractiveShellCommand { logger.info("Executing command \"hashLookup\"."); if (txIdHash == null) { - out.println("Please provide a hexadecimal transaction Id hash value, see 'man hashLookup'", Decoration.bold, Color.red); + out.println("Checks if a transaction matching a specified Id hash value is recorded on this node.\n\n" + + "This is mainly intended to be used for troubleshooting notarisation issues when a\n" + + "state is claimed to be already consumed by another transaction.\n\n" + + "Example usage: hashLookup E470FD8A6350A74217B0A99EA5FB71F091C84C64AD0DE0E72ECC10421D03AAC9"); + out.println("Please provide a hexadecimal transaction Id hash value", Decoration.bold, Color.red); return; } diff --git a/tools/shell/src/main/java/net/corda/tools/shell/OutputFormatCommand.java b/tools/shell/src/main/java/net/corda/tools/shell/OutputFormatCommand.java index 3b20da82f0..1a3fffb79e 100644 --- a/tools/shell/src/main/java/net/corda/tools/shell/OutputFormatCommand.java +++ b/tools/shell/src/main/java/net/corda/tools/shell/OutputFormatCommand.java @@ -15,6 +15,7 @@ import org.crsh.text.RenderPrintWriter; import java.util.Map; @Man("Allows you to see and update the format that's currently used for the commands' output.") +@Usage("Allows you to see and update the format that's currently used for the commands' output.") public class OutputFormatCommand extends InteractiveShellCommand { public OutputFormatCommand() {} From 30167fd2e8f6840920b6fd6f33229a5da5f11cc2 Mon Sep 17 00:00:00 2001 From: Razvan Codreanu <52859362+Schife@users.noreply.github.com> Date: Mon, 2 Mar 2020 14:50:01 +0000 Subject: [PATCH 58/83] TM-204 Fixing regression builds (#6009) * TM-204 attempting to fix regression builds * TM-204 attempting to fix regression builds * TM-204 reverting spring boot version and only removing missing dependency * TM-204 reverting to original build.gradle * TM-204 re applying dependency * TM-204 consolidating dependencies * TM-204 setting spring boot dependency * TM-204 reverting and upgrading dependency management plugin version in parent gradle file * TM-204 apply dependency plugin differently * TM-204 new gradle files * TM-204 try and fix corda rpc evaluation * TM-204 try and fix corda rpc evaluation * TM-204 separate slow integration and smoke test --- .ci/dev/on-demand-tests/commentMappings.yml | 1 + build.gradle | 11 ++++++++++- samples/irs-demo/build.gradle | 15 +++------------ samples/irs-demo/web/build.gradle | 4 ++-- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.ci/dev/on-demand-tests/commentMappings.yml b/.ci/dev/on-demand-tests/commentMappings.yml index 3b68c8366c..8f141bea9a 100644 --- a/.ci/dev/on-demand-tests/commentMappings.yml +++ b/.ci/dev/on-demand-tests/commentMappings.yml @@ -1,4 +1,5 @@ integration: { allParallelIntegrationTest } pr-merge: { parallelRegressionTest } smoke: { allParallelSmokeTest } +slow: { allParallelSlowIntegrationTest } unit: { allParallelUnitTest } diff --git a/build.gradle b/build.gradle index 23b522603a..a8a7496ab0 100644 --- a/build.gradle +++ b/build.gradle @@ -672,7 +672,16 @@ task parallelRegressionTest(type: ParallelTestGroup) { nodeTaints "big" } task allParallelSmokeTest(type: ParallelTestGroup) { - testGroups "slowIntegrationTest", "smokeTest" + testGroups "smokeTest" + numberOfShards 4 + streamOutput false + coresPerFork 6 + memoryInGbPerFork 10 + distribute DistributeTestsBy.CLASS + nodeTaints "big" +} +task allParallelSlowIntegrationTest(type: ParallelTestGroup) { + testGroups "slowIntegrationTest" numberOfShards 4 streamOutput false coresPerFork 6 diff --git a/samples/irs-demo/build.gradle b/samples/irs-demo/build.gradle index c5157cfd00..7628bf32bc 100644 --- a/samples/irs-demo/build.gradle +++ b/samples/irs-demo/build.gradle @@ -1,14 +1,6 @@ -buildscript { - ext { - springBootVersion = '1.5.21.RELEASE' - } - repositories { - mavenCentral() - } - dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion" - classpath "io.spring.gradle:dependency-management-plugin:1.0.8.RELEASE" - } +plugins { + id "org.springframework.boot" version "1.5.21.RELEASE" + id 'io.spring.dependency-management' version '1.0.9.RELEASE' apply false } // Spring Boot plugin adds a numerous hardcoded dependencies in the version much lower then Corda expects @@ -23,7 +15,6 @@ ext['mockito.version'] = "$mockito_version" apply plugin: 'kotlin' apply plugin: 'idea' -apply plugin: 'org.springframework.boot' apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'application' diff --git a/samples/irs-demo/web/build.gradle b/samples/irs-demo/web/build.gradle index 13b5428a84..c7f130691c 100644 --- a/samples/irs-demo/web/build.gradle +++ b/samples/irs-demo/web/build.gradle @@ -12,8 +12,9 @@ buildscript { } plugins { - id 'io.spring.dependency-management' id 'com.craigburke.client-dependencies' version '1.4.0' + id 'io.spring.dependency-management' + id 'org.springframework.boot' } group = "${parent.group}.irs-demo" @@ -55,7 +56,6 @@ ext['jackson.version'] = jackson_version apply plugin: 'kotlin' apply plugin: 'kotlin-spring' apply plugin: 'eclipse' -apply plugin: 'org.springframework.boot' apply plugin: 'project-report' apply plugin: 'application' From cd6098fd4ff9ae6862584a01440521aa348cab5b Mon Sep 17 00:00:00 2001 From: Ramzi El-Yafi Date: Tue, 3 Mar 2020 08:42:10 +0000 Subject: [PATCH 59/83] Flag finance library as not for production use in documentation (#6011) --- docs/packages.md | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/docs/packages.md b/docs/packages.md index a1c257ac91..ddcfe77327 100644 --- a/docs/packages.md +++ b/docs/packages.md @@ -92,55 +92,73 @@ Corda utility classes, providing a broad range of functionality to help implemen Some simple testing utilities like pre-defined top-level values for common currencies. Mostly useful for writing unit tests in Kotlin. -WARNING: NOT API STABLE. +__WARNING:__ This library is not suitable for production use and should not be used in real CorDapps. +Instead, use the [Token SDK](https://github.com/corda/token-sdk), or implement your own library. This +library may be removed in a future release without warning. # Package net.corda.finance.utils A collection of utilities for summing financial states, for example, summing obligations to get total debts. -WARNING: NOT API STABLE. +__WARNING:__ This library is not suitable for production use and should not be used in real CorDapps. +Instead, use the [Token SDK](https://github.com/corda/token-sdk), or implement your own library. This +library may be removed in a future release without warning. # Package net.corda.finance.contracts Various types for common financial concepts like day roll conventions, fixes, etc. -WARNING: NOT API STABLE. +__WARNING:__ This library is not suitable for production use and should not be used in real CorDapps. +Instead, use the [Token SDK](https://github.com/corda/token-sdk), or implement your own library. This +library may be removed in a future release without warning. # Package net.corda.finance.contracts.asset Cash states, obligations and commodities. -WARNING: NOT API STABLE. +__WARNING:__ This library is not suitable for production use and should not be used in real CorDapps. +Instead, use the [Token SDK](https://github.com/corda/token-sdk), or implement your own library. This +library may be removed in a future release without warning. # Package net.corda.finance.contracts.asset.cash.selection Provisional support for pluggable cash selectors, needed for different database backends. -WARNING: NOT API STABLE. +__WARNING:__ This library is not suitable for production use and should not be used in real CorDapps. +Instead, use the [Token SDK](https://github.com/corda/token-sdk), or implement your own library. This +library may be removed in a future release without warning. # Package net.corda.finance.contracts.math Splines and interpolation. -WARNING: NOT API STABLE. +__WARNING:__ This library is not suitable for production use and should not be used in real CorDapps. +Instead, use the [Token SDK](https://github.com/corda/token-sdk), or implement your own library. This +library may be removed in a future release without warning. # Package net.corda.finance.flows Cash payments and issuances. Two party "delivery vs payment" atomic asset swaps. -WARNING: NOT API STABLE. +__WARNING:__ This library is not suitable for production use and should not be used in real CorDapps. +Instead, use the [Token SDK](https://github.com/corda/token-sdk), or implement your own library. This +library may be removed in a future release without warning. # Package net.corda.finance.plugin JSON/Jackson plugin for business calendars. -WARNING: NOT API STABLE. +__WARNING:__ This library is not suitable for production use and should not be used in real CorDapps. +Instead, use the [Token SDK](https://github.com/corda/token-sdk), or implement your own library. This +library may be removed in a future release without warning. # Package net.corda.finance.schemas JPA (Java Persistence Architecture) schemas for the financial state types. -WARNING: NOT API STABLE. +__WARNING:__ This library is not suitable for production use and should not be used in real CorDapps. +Instead, use the [Token SDK](https://github.com/corda/token-sdk), or implement your own library. This +library may be removed in a future release without warning. # Package net.corda.testing.core From bd197b5229f63e250211eb11a026b031e4f654c5 Mon Sep 17 00:00:00 2001 From: Razvan Codreanu <52859362+Schife@users.noreply.github.com> Date: Tue, 3 Mar 2020 11:16:38 +0000 Subject: [PATCH 60/83] TM-204 Modifying regression builds to prevent bad dependency graphs (#6013) * TM-204 attempting to fix regression builds * TM-204 attempting to fix regression builds * TM-204 reverting spring boot version and only removing missing dependency * TM-204 reverting to original build.gradle * TM-204 re applying dependency * TM-204 consolidating dependencies * TM-204 setting spring boot dependency * TM-204 reverting and upgrading dependency management plugin version in parent gradle file * TM-204 apply dependency plugin differently * TM-204 new gradle files * TM-204 try and fix corda rpc evaluation * TM-204 try and fix corda rpc evaluation * TM-204 separate slow integration and smoke test * TM-204 modifying regression builds to separate slow integration tests and smoke tests as they sometimes result in a bad gradle dependency graph * TM-204 separating slow integration tests from the rest * TM-204 change to allow ci03 to run this jenkinsfile * TM-204 switching to aks label to allow testing on ci03 * TM-204 now that test was successful, switching back to correct prod label --- .ci/dev/nightly-regression/Jenkinsfile | 63 ++++++++++++++++---------- .ci/dev/regression/Jenkinsfile | 39 +++++++++++----- build.gradle | 2 +- 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/.ci/dev/nightly-regression/Jenkinsfile b/.ci/dev/nightly-regression/Jenkinsfile index de26a41c90..1b8739fe7b 100644 --- a/.ci/dev/nightly-regression/Jenkinsfile +++ b/.ci/dev/nightly-regression/Jenkinsfile @@ -23,34 +23,51 @@ pipeline { } stages { - stage('Generate Build Image') { - steps { - withCredentials([string(credentialsId: 'container_reg_passwd', variable: 'DOCKER_PUSH_PWD')]) { - sh "./gradlew " + - "-Dkubenetize=true " + - "-Ddocker.push.password=\"\${DOCKER_PUSH_PWD}\" " + - "-Ddocker.work.dir=\"/tmp/\${EXECUTOR_NUMBER}\" " + - "-Ddocker.build.tag=\"\${DOCKER_TAG_TO_USE}\"" + - " clean pushBuildImage --stacktrace" + stage('Corda Pull Request - Generate Build Image') { + steps { + withCredentials([string(credentialsId: 'container_reg_passwd', variable: 'DOCKER_PUSH_PWD')]) { + sh "./gradlew " + + "-Dkubenetize=true " + + "-Ddocker.push.password=\"\${DOCKER_PUSH_PWD}\" " + + "-Ddocker.work.dir=\"/tmp/\${EXECUTOR_NUMBER}\" " + + "-Ddocker.build.tag=\"\${DOCKER_TAG_TO_USE}\"" + + " clean pushBuildImage --stacktrace" + } + sh "kubectl auth can-i get pods" } - sh "kubectl auth can-i get pods" } - } - stage('Regression Test') { - steps { - sh "./gradlew " + - "-DbuildId=\"\${BUILD_ID}\" " + - "-Dkubenetize=true " + - "-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " + - "-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " + - "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + - "-Dgit.branch=\"\${GIT_BRANCH}\" " + - "-Dgit.target.branch=\"\${GIT_BRANCH}\" " + - " parallelRegressionTest --stacktrace" + stage('Testing phase') { + parallel { + stage('Regression Test') { + steps { + sh "./gradlew " + + "-DbuildId=\"\${BUILD_ID}\" " + + "-Dkubenetize=true " + + "-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " + + "-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " + + "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + + "-Dgit.branch=\"\${GIT_BRANCH}\" " + + "-Dgit.target.branch=\"\${GIT_BRANCH}\" " + + " parallelRegressionTest --stacktrace" + } + } + stage('Slow Integration Test') { + steps { + sh "./gradlew " + + "-DbuildId=\"\${BUILD_ID}\" " + + "-Dkubenetize=true " + + "-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " + + "-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " + + "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + + "-Dgit.branch=\"\${GIT_BRANCH}\" " + + "-Dgit.target.branch=\"\${GIT_BRANCH}\" " + + " allParallelSlowIntegrationTest --stacktrace" + } + } + } } } - } post { diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index ed550bd401..0f785396bb 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -33,17 +33,34 @@ pipeline { } } - stage('Regression Test') { - steps { - sh "./gradlew " + - "-DbuildId=\"\${BUILD_ID}\" " + - "-Dkubenetize=true " + - "-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " + - "-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " + - "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + - "-Dgit.branch=\"\${GIT_BRANCH}\" " + - "-Dgit.target.branch=\"\${GIT_BRANCH}\" " + - " parallelRegressionTest --stacktrace" + stage('Testing phase') { + parallel { + stage('Regression Test') { + steps { + sh "./gradlew " + + "-DbuildId=\"\${BUILD_ID}\" " + + "-Dkubenetize=true " + + "-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " + + "-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " + + "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + + "-Dgit.branch=\"\${GIT_BRANCH}\" " + + "-Dgit.target.branch=\"\${GIT_BRANCH}\" " + + " parallelRegressionTest --stacktrace" + } + } + stage('Slow Integration Test') { + steps { + sh "./gradlew " + + "-DbuildId=\"\${BUILD_ID}\" " + + "-Dkubenetize=true " + + "-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " + + "-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " + + "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + + "-Dgit.branch=\"\${GIT_BRANCH}\" " + + "-Dgit.target.branch=\"\${GIT_BRANCH}\" " + + " allParallelSlowIntegrationTest --stacktrace" + } + } } } } diff --git a/build.gradle b/build.gradle index a8a7496ab0..d4e1cdee29 100644 --- a/build.gradle +++ b/build.gradle @@ -663,7 +663,7 @@ task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) { nodeTaints "big" } task parallelRegressionTest(type: ParallelTestGroup) { - testGroups "test", "integrationTest", "slowIntegrationTest", "smokeTest" + testGroups "test", "integrationTest", "smokeTest" numberOfShards 15 streamOutput false coresPerFork 2 From b86ae0d6a64c909cfc19239dc1b9fb5eb9185d83 Mon Sep 17 00:00:00 2001 From: Adel El-Beik <48713346+adelel1@users.noreply.github.com> Date: Tue, 3 Mar 2020 12:10:04 +0000 Subject: [PATCH 61/83] Web server fails to connect to node if node takes too long to startup (#6012) * CORDA-3557: The web server attempts to reconnect to the client for a minute if initial attempt fails. * CORDA-3557: Fixing detekt issues --- .../corda/webserver/internal/NodeWebServer.kt | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/testing/testserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt b/testing/testserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt index 477c03e4b2..52dce3a5b7 100644 --- a/testing/testserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt +++ b/testing/testserver/src/main/kotlin/net/corda/webserver/internal/NodeWebServer.kt @@ -6,6 +6,7 @@ import net.corda.client.jackson.JacksonSupport import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCConnection import net.corda.client.rpc.GracefulReconnect +import net.corda.client.rpc.RPCException import net.corda.core.internal.errors.AddressBindingException import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.contextLogger @@ -34,7 +35,8 @@ import javax.servlet.http.HttpServletRequest class NodeWebServer(val config: WebServerConfig) { private companion object { private val log = contextLogger() - const val retryDelay = 1000L // Milliseconds + private const val NODE_CONNECT_RETRY_COUNT = 30 + private const val NODE_CONNECT_WAIT_BETWEEN_RETRYS = 2000L } val address = config.webAddress @@ -186,13 +188,26 @@ class NodeWebServer(val config: WebServerConfig) { private lateinit var rpc: CordaRPCConnection private fun reconnectingCordaRPCOps(): CordaRPCOps { - rpc = CordaRPCClient(config.rpcAddress, null, javaClass.classLoader) - .start( - config.runAs.username, - config.runAs.password, - GracefulReconnect() - ) - return rpc.proxy + var retryCount = NODE_CONNECT_RETRY_COUNT + while (true) { + try { + rpc = CordaRPCClient(config.rpcAddress, null, javaClass.classLoader) + .start( + config.runAs.username, + config.runAs.password, + GracefulReconnect() + ) + return rpc.proxy + } + catch (ex: RPCException) { + if (retryCount-- == 0) { + throw ex + } + else { + Thread.sleep(NODE_CONNECT_WAIT_BETWEEN_RETRYS) + } + } + } } /** From fec2ef2fd38d04ac8b9c599422e3e937d8f64f20 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 3 Mar 2020 15:01:12 +0000 Subject: [PATCH 62/83] CORDA-3638: Allow CordaFuture to complete even if an Error is thrown. (#5994) * Allow CordaFuture to complete even if an Error is thrown. * Allow CordaFuture.flatMap() to notify for failures due to Error. --- .../core/internal/concurrent/CordaFutureImpl.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/main/kotlin/net/corda/core/internal/concurrent/CordaFutureImpl.kt b/core/src/main/kotlin/net/corda/core/internal/concurrent/CordaFutureImpl.kt index 02cf0706c8..dcaceb2295 100644 --- a/core/src/main/kotlin/net/corda/core/internal/concurrent/CordaFutureImpl.kt +++ b/core/src/main/kotlin/net/corda/core/internal/concurrent/CordaFutureImpl.kt @@ -78,6 +78,7 @@ fun CordaFuture.mapError(transform: (Throwable) -> Throwa * But if this future or the transform fails, the returned future's outcome is the same throwable. * In the case where this future fails, the transform is not invoked. */ +@Suppress("TooGenericExceptionCaught") fun CordaFuture.flatMap(transform: (V) -> CordaFuture): CordaFuture = CordaFutureImpl().also { result -> thenMatch(success@ { result.captureLater(try { @@ -85,6 +86,9 @@ fun CordaFuture.flatMap(transform: (V) -> CordaFuture): Cor } catch (e: Exception) { result.setException(e) return@success + } catch (t: Throwable) { + result.setException(t) + throw t }) }, { result.setException(it) @@ -136,11 +140,15 @@ interface ValueOrException { fun captureLater(f: CordaFuture) = f.then { capture { f.getOrThrow() } } /** Run the given block (in the foreground) and set this future to its outcome. */ + @Suppress("TooGenericExceptionCaught") fun capture(block: () -> V): Boolean { return set(try { block() } catch (e: Exception) { return setException(e) + } catch (t: Throwable) { + setException(t) + throw t }) } } @@ -160,12 +168,16 @@ internal class CordaFutureImpl(private val impl: CompletableFuture = Compl override fun setException(t: Throwable) = impl.completeExceptionally(t) override fun then(callback: (CordaFuture) -> W) = thenImpl(defaultLog, callback) /** For testing only. */ + @Suppress("TooGenericExceptionCaught") internal fun thenImpl(log: Logger, callback: (CordaFuture) -> W) { impl.whenComplete { _, _ -> try { callback(this) } catch (e: Exception) { log.error(listenerFailedMessage, e) + } catch (t: Throwable) { + log.error(listenerFailedMessage, t) + throw t } } } From e38cd9ec631263a9f9a7ce946383b514933a5875 Mon Sep 17 00:00:00 2001 From: Christian Sailer Date: Tue, 3 Mar 2020 15:55:17 +0000 Subject: [PATCH 63/83] ENT-5043 separate out test utils (#5998) * Remove unused dependencies from test-common * Explicit imports and formatting * Add core-test-utils project * Add dependency * Move Kryo serialization context to node-api (not serialization as we do not want to pull kryo into the serialization lib) * Move AMQP server serialization scheme to node api * Move serialization tests to node-api * Move internal test helpers without further dependencies. * Move out some types from RPCClientProxyHandler to node-api in preparation for moving the AMQP scheme * Move client AMQP context to node-api so we can move the test serialization rule out. * Move InternalSerializationTestHelpers to core-test-utils * Moved testing.core to core-test-utils * Make detekt happy * Add api-scanner to core-test-utils * Remove inlined package names introduced by IntelliJ refactoring * Update api-current.txt to account for reordering. * Add core-test-utils to list of published artifacts. * Add missing import * Location of things in api text has moved again (publish name of artefact?) * Revert all additions to the API, leaving just the reordering * Code review: fix up core-test-utils build.gradle and introduce kryo version constant. * Remove OpenSsl flag from ssl config stub (can't be used from node-api) * Suppress detekt warning * Move core test util tests to the right module * Expose kotlin test as a transient dependency - projects have come to rely on that. * Fix typo in package name --- .ci/api-current.txt | 772 +++++++++--------- build.gradle | 3 + .../client/jackson/JacksonSupportTest.kt | 4 +- client/rpc/build.gradle | 2 +- .../net/corda/client/rpc/RPCStabilityTests.kt | 2 +- .../net/corda/client/rpc/CordaRPCClient.kt | 2 +- .../rpc/internal/RPCClientProxyHandler.kt | 27 +- .../corda/client/rpc/RPCConcurrencyTests.kt | 2 +- .../corda/client/rpc/RPCPerformanceTests.kt | 2 +- .../net/corda/common/logging/Constants.kt | 2 +- .../confidential/SwapIdentitiesFlowTests.kt | 6 +- .../coretests/crypto/PartialMerkleTreeTest.kt | 2 +- .../corda/coretests/flows/AttachmentTests.kt | 4 +- .../flows/CollectSignaturesFlowTests.kt | 6 +- .../flows/ContractUpgradeFlowRPCTest.kt | 4 +- .../flows/ContractUpgradeFlowTest.kt | 4 +- .../coretests/flows/FinalityFlowTests.kt | 4 +- .../coretests/flows/ReceiveAllFlowTests.kt | 2 +- .../indentity/PartyAndCertificateTest.kt | 2 +- .../TransactionSerializationTests.kt | 4 +- .../transactions/TransactionBuilderTest.kt | 2 +- .../transactions/TransactionTests.kt | 2 +- .../coretests/utilities/KotlinUtilsTest.kt | 2 +- core/build.gradle | 2 +- detekt-baseline.xml | 4 +- .../tutorial/testdsl/TutorialTestDSL.kt | 1 - .../finance/contracts/CommercialPaperTests.kt | 2 +- .../contracts/asset/ObligationTests.kt | 2 +- node-api/build.gradle | 6 +- .../rpc/ObservableContextInterface.kt | 2 +- .../internal/rpc/ObservableSubscription.kt | 7 + .../client}/AMQPClientSerializationScheme.kt | 17 +- .../internal/rpc/client/ObservableContext.kt | 27 + .../client}/RpcClientCordaFutureSerializer.kt | 4 +- .../RpcClientObservableDeSerializer.kt | 11 +- .../amqp/AMQPServerSerializationScheme.kt | 2 +- .../amqp/RpcServerCordaFutureSerializer.kt | 8 +- .../amqp/RpcServerObservableSerializer.kt | 12 +- .../serialization/kryo/CordaClassResolver.kt | 2 +- .../kryo/CordaClosureSerializer.kt | 2 +- .../kryo/DefaultKryoCustomizer.kt | 2 +- .../internal}/serialization/kryo/Kryo.kt | 2 +- .../kryo/KryoCheckpointSerializer.kt | 2 +- .../serialization/kryo/KryoStreams.kt | 2 +- .../kryo/SerializeAsTokenSerializer.kt | 2 +- ...tachmentsClassLoaderStaticContractTests.kt | 2 +- .../nodeapi/internal/SignedNodeInfoTest.kt | 4 +- .../internal/crypto/AliasPrivateKeyTest.kt | 2 +- .../internal/crypto/X509UtilitiesTest.kt | 10 +- .../bouncycastle/BCCryptoServiceTests.kt | 2 +- .../network/NetworkBootstrapperTest.kt | 8 +- .../RoundTripObservableSerializerTests.kt | 16 +- .../RpcServerObservableSerializerTests.kt | 10 +- .../serialization/kryo/KryoStreamsTest.kt | 2 +- .../internal}/serialization/kryo/KryoTests.kt | 10 +- .../testutils/AMQPTestSerialiationScheme.kt | 8 +- .../testutils/TestObservableContext.kt | 6 +- .../testutils/TestSerializationContext.kt | 2 +- node/build.gradle | 4 - .../registration/NodeRegistrationTest.kt | 2 +- .../kotlin/net/corda/node/BootTests.kt | 2 +- .../net/corda/node/CordappConstraintsTests.kt | 2 +- .../net/corda/node/NodeKeystoreCheckTest.kt | 2 +- .../net/corda/node/NodePerformanceTests.kt | 2 +- .../net/corda/node/amqp/AMQPBridgeTest.kt | 4 +- .../CertificateRevocationListNodeTests.kt | 8 +- .../net/corda/node/amqp/ProtonWrapperTests.kt | 4 +- .../messaging/ArtemisMessagingTest.kt | 4 +- .../messaging/MQSecurityAsNodeTest.kt | 2 +- .../kotlin/net/corda/node/internal/Node.kt | 8 +- .../net/corda/node/services/rpc/RPCServer.kt | 32 +- .../net/corda/node/internal/NodeTest.kt | 4 +- .../node/messaging/TwoPartyTradeFlowTests.kt | 6 +- .../AttachmentTrustCalculatorTest.kt | 2 +- .../events/NodeSchedulerServiceTest.kt | 6 +- .../identity/InMemoryIdentityServiceTests.kt | 4 +- .../PersistentIdentityServiceTests.kt | 13 +- .../network/DBNetworkParametersStorageTest.kt | 4 +- .../services/network/NetworkMapClientTest.kt | 8 +- .../services/network/NetworkMapUpdaterTest.kt | 7 +- .../network/NetworkParametersReaderTest.kt | 2 +- .../services/network/NodeInfoWatcherTest.kt | 3 +- .../persistence/NodeAttachmentServiceTest.kt | 3 +- .../schema/PersistentStateServiceTests.kt | 2 +- .../node/services/vault/VaultQueryTests.kt | 2 +- .../vault/VaultSoftLockManagerTest.kt | 2 +- .../HTTPNetworkRegistrationServiceTest.kt | 2 +- .../NetworkRegistrationHelperTest.kt | 4 +- .../corda/irs/api/NodeInterestRatesTest.kt | 2 +- .../flow/TransactionGraphSearchTests.kt | 2 +- .../LambdaCheckpointSerializationTest.java | 2 +- .../ContractAttachmentSerializerTest.kt | 2 +- .../internal/CordaClassResolverTests.kt | 6 +- .../internal/ListsSerializationTest.kt | 2 +- .../internal/MapsSerializationTest.kt | 2 +- .../internal/SerializationTokenTest.kt | 10 +- .../internal/SetsSerializationTest.kt | 2 +- .../AbstractAMQPSerializationSchemeTest.kt | 2 +- .../amqp/DeserializeQueryableStateTest.kt | 4 +- .../internal/amqp/SerializationOutputTests.kt | 4 +- settings.gradle | 3 +- testing/core-test-utils/build.gradle | 22 + .../InternalSerializationTestHelpers.kt | 16 +- .../internal/InternalTestConstants.kt | 2 +- .../coretesting}/internal/NettyTestClient.kt | 2 +- .../coretesting}/internal/NettyTestHandler.kt | 2 +- .../coretesting}/internal/NettyTestServer.kt | 2 +- .../coretesting}/internal/RigorousMock.kt | 3 +- .../internal/TestNodeInfoBuilder.kt | 4 +- .../internal/TestThreadFactory.kt | 2 +- .../internal/matchers/Matchers.kt | 2 +- .../internal/matchers/flow/FlowMatchers.kt | 12 +- .../matchers/future/FutureMatchers.kt | 4 +- .../internal/matchers/rpc/RpcMatchers.kt | 14 +- .../coretesting}/internal/performance/Rate.kt | 2 +- .../internal/stubs/CertificateStoreStubs.kt | 8 +- .../corda/testing/contracts/DummyContract.kt | 0 .../testing/contracts/DummyContractV2.kt | 3 +- .../testing/contracts/DummyContractV3.kt | 0 .../net/corda/testing/contracts/DummyState.kt | 0 .../kotlin/net/corda/testing/core/Expect.kt | 0 .../core/SerializationEnvironmentRule.kt | 9 +- .../net/corda/testing/core/TestConstants.kt | 0 .../net/corda/testing/core/TestUtils.kt | 4 +- .../CheckpointSerializationTestHelpers.kt | 11 +- .../core/internal/ContractJarTestUtils.kt | 0 .../core/internal/JarSignatureTestUtils.kt | 0 .../org.mockito.plugins.MockMaker | 0 .../corda/coretesting}/TestIdentityTests.kt | 2 +- .../coretesting}/internal/MatcherTests.kt | 4 +- .../coretesting}/internal/RigorousMockTest.kt | 2 +- .../net/corda/testing/node/MockServices.kt | 2 +- .../testing/node/internal/DriverDSLImpl.kt | 2 +- .../node/internal/InternalMockNetwork.kt | 6 +- .../node/internal/InternalTestUtils.kt | 4 +- .../testing/node/internal/NodeBasedTest.kt | 2 +- .../corda/testing/node/internal/RPCDriver.kt | 2 +- .../node/internal/performance/Injectors.kt | 2 +- .../node/internal/InternalMockNetworkTests.kt | 2 +- .../net/corda/smoketesting/NodeProcess.kt | 2 +- testing/test-common/build.gradle | 2 - testing/test-utils/build.gradle | 5 +- .../testing/internal/InternalTestUtils.kt | 4 +- tools/checkpoint-agent/build.gradle | 2 +- .../kotlin/net/corda/demobench/DemoBench.kt | 2 +- .../net/corda/demobench/pty/ZeroFilterTest.kt | 2 +- .../serialization/SerializationHelper.kt | 6 +- .../tools/shell/InteractiveShellJavaTest.java | 2 +- .../corda/tools/shell/InteractiveShellTest.kt | 2 +- 149 files changed, 760 insertions(+), 681 deletions(-) rename {node/src/main/kotlin/net/corda/node/services => node-api/src/main/kotlin/net/corda/nodeapi/internal}/rpc/ObservableContextInterface.kt (95%) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/ObservableSubscription.kt rename {client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp => node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client}/AMQPClientSerializationScheme.kt (84%) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/ObservableContext.kt rename {client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp => node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client}/RpcClientCordaFutureSerializer.kt (90%) rename {client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp => node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client}/RpcClientObservableDeSerializer.kt (93%) rename {node/src/main/kotlin/net/corda/node => node-api/src/main/kotlin/net/corda/nodeapi/internal}/serialization/amqp/AMQPServerSerializationScheme.kt (97%) rename node/src/main/kotlin/net/corda/node/serialization/amqp/RpcServerCordaFutureSerialiser.kt => node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/RpcServerCordaFutureSerializer.kt (74%) rename {node/src/main/kotlin/net/corda/node => node-api/src/main/kotlin/net/corda/nodeapi/internal}/serialization/amqp/RpcServerObservableSerializer.kt (92%) rename {node/src/main/kotlin/net/corda/node => node-api/src/main/kotlin/net/corda/nodeapi/internal}/serialization/kryo/CordaClassResolver.kt (99%) rename {node/src/main/kotlin/net/corda/node => node-api/src/main/kotlin/net/corda/nodeapi/internal}/serialization/kryo/CordaClosureSerializer.kt (94%) rename {node/src/main/kotlin/net/corda/node => node-api/src/main/kotlin/net/corda/nodeapi/internal}/serialization/kryo/DefaultKryoCustomizer.kt (99%) rename {node/src/main/kotlin/net/corda/node => node-api/src/main/kotlin/net/corda/nodeapi/internal}/serialization/kryo/Kryo.kt (99%) rename {node/src/main/kotlin/net/corda/node => node-api/src/main/kotlin/net/corda/nodeapi/internal}/serialization/kryo/KryoCheckpointSerializer.kt (99%) rename {node/src/main/kotlin/net/corda/node => node-api/src/main/kotlin/net/corda/nodeapi/internal}/serialization/kryo/KryoStreams.kt (95%) rename {node/src/main/kotlin/net/corda/node => node-api/src/main/kotlin/net/corda/nodeapi/internal}/serialization/kryo/SerializeAsTokenSerializer.kt (96%) rename {node/src/test/kotlin/net/corda/node => node-api/src/test/kotlin/net/corda/nodeapi}/internal/serialization/RoundTripObservableSerializerTests.kt (85%) rename {node/src/test/kotlin/net/corda/node => node-api/src/test/kotlin/net/corda/nodeapi}/internal/serialization/RpcServerObservableSerializerTests.kt (89%) rename {node/src/test/kotlin/net/corda/node => node-api/src/test/kotlin/net/corda/nodeapi/internal}/serialization/kryo/KryoStreamsTest.kt (98%) rename {node/src/test/kotlin/net/corda/node => node-api/src/test/kotlin/net/corda/nodeapi/internal}/serialization/kryo/KryoTests.kt (98%) rename {node/src/test/kotlin/net/corda/node => node-api/src/test/kotlin/net/corda/nodeapi}/internal/serialization/testutils/AMQPTestSerialiationScheme.kt (87%) rename {node/src/test/kotlin/net/corda/node => node-api/src/test/kotlin/net/corda/nodeapi}/internal/serialization/testutils/TestObservableContext.kt (77%) rename {node/src/test/kotlin/net/corda/node => node-api/src/test/kotlin/net/corda/nodeapi}/internal/serialization/testutils/TestSerializationContext.kt (92%) create mode 100644 testing/core-test-utils/build.gradle rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/InternalSerializationTestHelpers.kt (73%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/InternalTestConstants.kt (91%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/NettyTestClient.kt (98%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/NettyTestHandler.kt (98%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/NettyTestServer.kt (98%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/RigorousMock.kt (98%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/TestNodeInfoBuilder.kt (96%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/TestThreadFactory.kt (94%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/matchers/Matchers.kt (98%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/matchers/flow/FlowMatchers.kt (69%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/matchers/future/FutureMatchers.kt (95%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/matchers/rpc/RpcMatchers.kt (65%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/performance/Rate.kt (94%) rename testing/{test-utils/src/main/kotlin/net/corda/testing => core-test-utils/src/main/kotlin/net/corda/coretesting}/internal/stubs/CertificateStoreStubs.kt (94%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt (100%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt (95%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/contracts/DummyContractV3.kt (100%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/contracts/DummyState.kt (100%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/core/Expect.kt (100%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/core/SerializationEnvironmentRule.kt (83%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/core/TestConstants.kt (100%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/core/TestUtils.kt (98%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/core/internal/CheckpointSerializationTestHelpers.kt (89%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/core/internal/ContractJarTestUtils.kt (100%) rename testing/{test-utils => core-test-utils}/src/main/kotlin/net/corda/testing/core/internal/JarSignatureTestUtils.kt (100%) rename testing/{test-utils => core-test-utils}/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker (100%) rename testing/{test-utils/src/test/kotlin/net/corda/testing => core-test-utils/src/test/kotlin/net/corda/coretesting}/TestIdentityTests.kt (96%) rename testing/{test-utils/src/test/kotlin/net/corda/testing => core-test-utils/src/test/kotlin/net/corda/coretesting}/internal/MatcherTests.kt (95%) rename testing/{test-utils/src/test/kotlin/net/corda/testing => core-test-utils/src/test/kotlin/net/corda/coretesting}/internal/RigorousMockTest.kt (99%) diff --git a/.ci/api-current.txt b/.ci/api-current.txt index 03aed42800..64e351610e 100644 --- a/.ci/api-current.txt +++ b/.ci/api-current.txt @@ -6818,6 +6818,392 @@ public static final class net.corda.core.utilities.UuidGenerator$Companion exten public interface net.corda.core.utilities.VariablePropertyDelegate extends net.corda.core.utilities.PropertyDelegate public abstract void setValue(Object, kotlin.reflect.KProperty, T) ## +public final class net.corda.testing.contracts.DummyContract extends java.lang.Object implements net.corda.core.contracts.Contract + public () + public (Object) + public (Object, int, kotlin.jvm.internal.DefaultConstructorMarker) + @Nullable + public final Object component1() + @NotNull + public final net.corda.testing.contracts.DummyContract copy(Object) + public boolean equals(Object) + @NotNull + public static final net.corda.core.transactions.TransactionBuilder generateInitial(int, net.corda.core.identity.Party, net.corda.core.contracts.PartyAndReference, net.corda.core.contracts.PartyAndReference...) + @Nullable + public final Object getBlank() + @NotNull + public final String getPROGRAM_ID() + public int hashCode() + @NotNull + public static final net.corda.core.transactions.TransactionBuilder move(java.util.List>, net.corda.core.identity.AbstractParty) + @NotNull + public static final net.corda.core.transactions.TransactionBuilder move(net.corda.core.contracts.StateAndRef, net.corda.core.identity.AbstractParty) + @NotNull + public String toString() + public void verify(net.corda.core.transactions.LedgerTransaction) + public static final net.corda.testing.contracts.DummyContract$Companion Companion + @NotNull + public static final String PROGRAM_ID = "net.corda.testing.contracts.DummyContract" +## +public static interface net.corda.testing.contracts.DummyContract$Commands extends net.corda.core.contracts.CommandData +## +public static final class net.corda.testing.contracts.DummyContract$Commands$Create extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContract$Commands + public () +## +public static final class net.corda.testing.contracts.DummyContract$Commands$Move extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContract$Commands + public () +## +public static final class net.corda.testing.contracts.DummyContract$Companion extends java.lang.Object + public (kotlin.jvm.internal.DefaultConstructorMarker) + @NotNull + public final net.corda.core.transactions.TransactionBuilder generateInitial(int, net.corda.core.identity.Party, net.corda.core.contracts.PartyAndReference, net.corda.core.contracts.PartyAndReference...) + @NotNull + public final net.corda.core.transactions.TransactionBuilder move(java.util.List>, net.corda.core.identity.AbstractParty) + @NotNull + public final net.corda.core.transactions.TransactionBuilder move(net.corda.core.contracts.StateAndRef, net.corda.core.identity.AbstractParty) +## +@DoNotImplement +public static final class net.corda.testing.contracts.DummyContract$MultiOwnerState extends java.lang.Object implements net.corda.testing.contracts.DummyContract$State + public (int, java.util.List) + public (int, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker) + public final int component1() + @NotNull + public final java.util.List component2() + @NotNull + public final net.corda.testing.contracts.DummyContract$MultiOwnerState copy(int, java.util.List) + public boolean equals(Object) + public int getMagicNumber() + @NotNull + public final java.util.List getOwners() + @NotNull + public java.util.List getParticipants() + public int hashCode() + @NotNull + public String toString() +## +@DoNotImplement +public static final class net.corda.testing.contracts.DummyContract$SingleOwnerState extends java.lang.Object implements net.corda.core.contracts.OwnableState, net.corda.testing.contracts.DummyContract$State + public (int, net.corda.core.identity.AbstractParty) + public (int, net.corda.core.identity.AbstractParty, int, kotlin.jvm.internal.DefaultConstructorMarker) + public final int component1() + @NotNull + public final net.corda.core.identity.AbstractParty component2() + @NotNull + public final net.corda.testing.contracts.DummyContract$SingleOwnerState copy(int, net.corda.core.identity.AbstractParty) + public boolean equals(Object) + public int getMagicNumber() + @NotNull + public net.corda.core.identity.AbstractParty getOwner() + @NotNull + public java.util.List getParticipants() + public int hashCode() + @NotNull + public String toString() + @NotNull + public net.corda.core.contracts.CommandAndState withNewOwner(net.corda.core.identity.AbstractParty) +## +@DoNotImplement +public static interface net.corda.testing.contracts.DummyContract$State extends net.corda.core.contracts.ContractState + public abstract int getMagicNumber() +## +public final class net.corda.testing.contracts.DummyContractV2 extends java.lang.Object implements net.corda.core.contracts.UpgradedContractWithLegacyConstraint + public () + @NotNull + public String getLegacyContract() + @NotNull + public net.corda.core.contracts.AttachmentConstraint getLegacyContractConstraint() + @NotNull + public net.corda.testing.contracts.DummyContractV2$State upgrade(net.corda.testing.contracts.DummyContract$State) + public void verify(net.corda.core.transactions.LedgerTransaction) + public static final net.corda.testing.contracts.DummyContractV2$Companion Companion + @NotNull + public static final String PROGRAM_ID = "net.corda.testing.contracts.DummyContractV2" +## +public static interface net.corda.testing.contracts.DummyContractV2$Commands extends net.corda.core.contracts.CommandData +## +public static final class net.corda.testing.contracts.DummyContractV2$Commands$Create extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV2$Commands + public () +## +public static final class net.corda.testing.contracts.DummyContractV2$Commands$Move extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV2$Commands + public () +## +public static final class net.corda.testing.contracts.DummyContractV2$Companion extends java.lang.Object + public (kotlin.jvm.internal.DefaultConstructorMarker) +## +public static final class net.corda.testing.contracts.DummyContractV2$State extends java.lang.Object implements net.corda.core.contracts.ContractState + public (int, java.util.List) + public (int, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker) + public final int component1() + @NotNull + public final java.util.List component2() + @NotNull + public final net.corda.testing.contracts.DummyContractV2$State copy(int, java.util.List) + public boolean equals(Object) + public final int getMagicNumber() + @NotNull + public final java.util.List getOwners() + @NotNull + public java.util.List getParticipants() + public int hashCode() + @NotNull + public String toString() +## +public final class net.corda.testing.contracts.DummyContractV3 extends java.lang.Object implements net.corda.core.contracts.UpgradedContractWithLegacyConstraint + public () + @NotNull + public String getLegacyContract() + @NotNull + public net.corda.core.contracts.AttachmentConstraint getLegacyContractConstraint() + @NotNull + public net.corda.testing.contracts.DummyContractV3$State upgrade(net.corda.testing.contracts.DummyContractV2$State) + public void verify(net.corda.core.transactions.LedgerTransaction) + public static final net.corda.testing.contracts.DummyContractV3$Companion Companion + @NotNull + public static final String PROGRAM_ID = "net.corda.testing.contracts.DummyContractV3" +## +public static interface net.corda.testing.contracts.DummyContractV3$Commands extends net.corda.core.contracts.CommandData +## +public static final class net.corda.testing.contracts.DummyContractV3$Commands$Create extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV3$Commands + public () +## +public static final class net.corda.testing.contracts.DummyContractV3$Commands$Move extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV3$Commands + public () +## +public static final class net.corda.testing.contracts.DummyContractV3$Companion extends java.lang.Object + public (kotlin.jvm.internal.DefaultConstructorMarker) +## +public static final class net.corda.testing.contracts.DummyContractV3$State extends java.lang.Object implements net.corda.core.contracts.ContractState + public (int, java.util.List) + public (int, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker) + public final int component1() + @NotNull + public final java.util.List component2() + @NotNull + public final net.corda.testing.contracts.DummyContractV3$State copy(int, java.util.List) + public boolean equals(Object) + public final int getMagicNumber() + @NotNull + public final java.util.List getOwners() + @NotNull + public java.util.List getParticipants() + public int hashCode() + @NotNull + public String toString() +## +@BelongsToContract +public final class net.corda.testing.contracts.DummyState extends java.lang.Object implements net.corda.core.contracts.ContractState + public () + public (int) + public (int, java.util.List) + public (int, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker) + public final int component1() + @NotNull + public final java.util.List component2() + @NotNull + public final net.corda.testing.contracts.DummyState copy(int) + @NotNull + public final net.corda.testing.contracts.DummyState copy(int, java.util.List) + public boolean equals(Object) + public final int getMagicNumber() + @NotNull + public java.util.List getParticipants() + public int hashCode() + @NotNull + public String toString() +## +public final class net.corda.testing.core.DummyCommandData extends net.corda.core.contracts.TypeOnlyCommandData + public static final net.corda.testing.core.DummyCommandData INSTANCE +## +public final class net.corda.testing.core.Expect extends java.lang.Object + public (Class, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1) + @NotNull + public final Class component1() + @NotNull + public final kotlin.jvm.functions.Function1 component2() + @NotNull + public final kotlin.jvm.functions.Function1 component3() + @NotNull + public final net.corda.testing.core.Expect copy(Class, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1) + public boolean equals(Object) + @NotNull + public final Class getClazz() + @NotNull + public final kotlin.jvm.functions.Function1 getExpectClosure() + @NotNull + public final kotlin.jvm.functions.Function1 getMatch() + public int hashCode() + @NotNull + public String toString() +## +@DoNotImplement +public abstract class net.corda.testing.core.ExpectCompose extends java.lang.Object + public (kotlin.jvm.internal.DefaultConstructorMarker) +## +@DoNotImplement +public static final class net.corda.testing.core.ExpectCompose$Parallel extends net.corda.testing.core.ExpectCompose + public (java.util.List>) + @NotNull + public final java.util.List> getParallel() +## +@DoNotImplement +public static final class net.corda.testing.core.ExpectCompose$Sequential extends net.corda.testing.core.ExpectCompose + public (java.util.List>) + @NotNull + public final java.util.List> getSequence() +## +@DoNotImplement +public static final class net.corda.testing.core.ExpectCompose$Single extends net.corda.testing.core.ExpectCompose + public (net.corda.testing.core.Expect) + @NotNull + public final net.corda.testing.core.Expect getExpect() +## +public static final class net.corda.testing.core.ExpectComposeState$Companion extends java.lang.Object + public (kotlin.jvm.internal.DefaultConstructorMarker) + @NotNull + public final net.corda.testing.core.ExpectComposeState fromExpectCompose(net.corda.testing.core.ExpectCompose) +## +public static final class net.corda.testing.core.ExpectComposeState$Finished extends net.corda.testing.core.ExpectComposeState + public () + @NotNull + public java.util.List> getExpectedEvents() + @Nullable + public Void nextState(E) +## +public static final class net.corda.testing.core.ExpectComposeState$Parallel extends net.corda.testing.core.ExpectComposeState + public (net.corda.testing.core.ExpectCompose$Parallel, java.util.List>) + @NotNull + public java.util.List> getExpectedEvents() + @NotNull + public final net.corda.testing.core.ExpectCompose$Parallel getParallel() + @NotNull + public final java.util.List> getStates() + @Nullable + public kotlin.Pair, net.corda.testing.core.ExpectComposeState> nextState(E) +## +public static final class net.corda.testing.core.ExpectComposeState$Sequential extends net.corda.testing.core.ExpectComposeState + public (net.corda.testing.core.ExpectCompose$Sequential, int, net.corda.testing.core.ExpectComposeState) + @NotNull + public java.util.List> getExpectedEvents() + public final int getIndex() + @NotNull + public final net.corda.testing.core.ExpectCompose$Sequential getSequential() + @NotNull + public final net.corda.testing.core.ExpectComposeState getState() + @Nullable + public kotlin.Pair, net.corda.testing.core.ExpectComposeState> nextState(E) +## +public static final class net.corda.testing.core.ExpectComposeState$Single extends net.corda.testing.core.ExpectComposeState + public (net.corda.testing.core.ExpectCompose$Single) + @NotNull + public java.util.List> getExpectedEvents() + @NotNull + public final net.corda.testing.core.ExpectCompose$Single getSingle() + @Nullable + public kotlin.Pair, net.corda.testing.core.ExpectComposeState> nextState(E) +## +public final class net.corda.testing.core.ExpectKt extends java.lang.Object + @NotNull + public static final net.corda.testing.core.ExpectCompose expect(Class, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1) + public static final void expectEvents(Iterable, boolean, kotlin.jvm.functions.Function0>) + public static final void expectEvents(rx.Observable, boolean, kotlin.jvm.functions.Function0>) + public static final void genericExpectEvents(S, boolean, kotlin.jvm.functions.Function2, kotlin.Unit>, kotlin.jvm.functions.Function0>) + @NotNull + public static final net.corda.testing.core.ExpectCompose parallel(java.util.List>) + @NotNull + public static final net.corda.testing.core.ExpectCompose parallel(net.corda.testing.core.ExpectCompose...) + @NotNull + public static final net.corda.testing.core.ExpectCompose replicate(int, kotlin.jvm.functions.Function1>) + @NotNull + public static final net.corda.testing.core.ExpectCompose sequence(java.util.List>) + @NotNull + public static final net.corda.testing.core.ExpectCompose sequence(net.corda.testing.core.ExpectCompose...) +## +public final class net.corda.testing.core.SerializationEnvironmentRule extends java.lang.Object implements org.junit.rules.TestRule + public () + public (boolean) + public (boolean, int, kotlin.jvm.internal.DefaultConstructorMarker) + @NotNull + public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement, org.junit.runner.Description) + @NotNull + public final net.corda.core.serialization.SerializationFactory getSerializationFactory() + public static final net.corda.testing.core.SerializationEnvironmentRule$Companion Companion +## +public static final class net.corda.testing.core.SerializationEnvironmentRule$Companion extends java.lang.Object + public (kotlin.jvm.internal.DefaultConstructorMarker) +## +public final class net.corda.testing.core.TestConstants extends java.lang.Object + @NotNull + public static final net.corda.core.contracts.Command dummyCommand(java.security.PublicKey...) + @NotNull + public static final net.corda.core.identity.CordaX500Name ALICE_NAME + @NotNull + public static final net.corda.core.identity.CordaX500Name BOB_NAME + @NotNull + public static final net.corda.core.identity.CordaX500Name BOC_NAME + @NotNull + public static final net.corda.core.identity.CordaX500Name CHARLIE_NAME + @NotNull + public static final net.corda.core.identity.CordaX500Name DUMMY_BANK_A_NAME + @NotNull + public static final net.corda.core.identity.CordaX500Name DUMMY_BANK_B_NAME + @NotNull + public static final net.corda.core.identity.CordaX500Name DUMMY_BANK_C_NAME + @NotNull + public static final net.corda.core.identity.CordaX500Name DUMMY_NOTARY_NAME + public static final int MAX_MESSAGE_SIZE = 10485760 +## +public final class net.corda.testing.core.TestIdentity extends java.lang.Object + public (net.corda.core.identity.CordaX500Name) + public (net.corda.core.identity.CordaX500Name, long) + public (net.corda.core.identity.CordaX500Name, long, net.corda.core.crypto.SignatureScheme) + public (net.corda.core.identity.CordaX500Name, long, net.corda.core.crypto.SignatureScheme, int, kotlin.jvm.internal.DefaultConstructorMarker) + public (net.corda.core.identity.CordaX500Name, java.security.KeyPair) + public (net.corda.core.identity.CordaX500Name, net.corda.core.crypto.SignatureScheme) + public (net.corda.core.identity.CordaX500Name, net.corda.core.crypto.SignatureScheme, int, kotlin.jvm.internal.DefaultConstructorMarker) + @NotNull + public static final net.corda.testing.core.TestIdentity fresh(String) + @NotNull + public static final net.corda.testing.core.TestIdentity fresh(String, net.corda.core.crypto.SignatureScheme) + @NotNull + public final net.corda.core.identity.PartyAndCertificate getIdentity() + @NotNull + public final java.security.KeyPair getKeyPair() + @NotNull + public final net.corda.core.identity.CordaX500Name getName() + @NotNull + public final net.corda.core.identity.Party getParty() + @NotNull + public final java.security.PublicKey getPublicKey() + @NotNull + public final net.corda.core.contracts.PartyAndReference ref(byte...) + public static final net.corda.testing.core.TestIdentity$Companion Companion +## +public static final class net.corda.testing.core.TestIdentity$Companion extends java.lang.Object + public (kotlin.jvm.internal.DefaultConstructorMarker) + @NotNull + public final net.corda.testing.core.TestIdentity fresh(String) + @NotNull + public final net.corda.testing.core.TestIdentity fresh(String, net.corda.core.crypto.SignatureScheme) +## +public final class net.corda.testing.core.TestUtils extends java.lang.Object + @NotNull + public static final net.corda.core.utilities.NetworkHostAndPort freeLocalHostAndPort() + public static final int freePort() + @NotNull + public static final net.corda.core.contracts.StateRef generateStateRef() + @NotNull + public static final java.util.List getFreeLocalPorts(String, int) + @NotNull + public static final net.corda.core.identity.PartyAndCertificate getTestPartyAndCertificate(net.corda.core.identity.CordaX500Name, java.security.PublicKey) + @NotNull + public static final net.corda.core.identity.PartyAndCertificate getTestPartyAndCertificate(net.corda.core.identity.Party) + @NotNull + public static final net.corda.core.identity.CordaX500Name makeUnique(net.corda.core.identity.CordaX500Name) + @NotNull + public static final net.corda.core.identity.Party singleIdentity(net.corda.core.node.NodeInfo) + @NotNull + public static final net.corda.core.identity.PartyAndCertificate singleIdentityAndCert(net.corda.core.node.NodeInfo) +## public final class net.corda.client.jackson.JacksonSupport extends java.lang.Object @NotNull public static final com.fasterxml.jackson.databind.ObjectMapper createDefaultMapper(net.corda.core.messaging.CordaRPCOps) @@ -8148,392 +8534,6 @@ public static class net.corda.finance.test.SampleCashSchemaV3$PersistentCashStat public void setParticipants(java.util.Set) public void setPennies(long) ## -public final class net.corda.testing.contracts.DummyContract extends java.lang.Object implements net.corda.core.contracts.Contract - public () - public (Object) - public (Object, int, kotlin.jvm.internal.DefaultConstructorMarker) - @Nullable - public final Object component1() - @NotNull - public final net.corda.testing.contracts.DummyContract copy(Object) - public boolean equals(Object) - @NotNull - public static final net.corda.core.transactions.TransactionBuilder generateInitial(int, net.corda.core.identity.Party, net.corda.core.contracts.PartyAndReference, net.corda.core.contracts.PartyAndReference...) - @Nullable - public final Object getBlank() - @NotNull - public final String getPROGRAM_ID() - public int hashCode() - @NotNull - public static final net.corda.core.transactions.TransactionBuilder move(java.util.List>, net.corda.core.identity.AbstractParty) - @NotNull - public static final net.corda.core.transactions.TransactionBuilder move(net.corda.core.contracts.StateAndRef, net.corda.core.identity.AbstractParty) - @NotNull - public String toString() - public void verify(net.corda.core.transactions.LedgerTransaction) - public static final net.corda.testing.contracts.DummyContract$Companion Companion - @NotNull - public static final String PROGRAM_ID = "net.corda.testing.contracts.DummyContract" -## -public static interface net.corda.testing.contracts.DummyContract$Commands extends net.corda.core.contracts.CommandData -## -public static final class net.corda.testing.contracts.DummyContract$Commands$Create extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContract$Commands - public () -## -public static final class net.corda.testing.contracts.DummyContract$Commands$Move extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContract$Commands - public () -## -public static final class net.corda.testing.contracts.DummyContract$Companion extends java.lang.Object - public (kotlin.jvm.internal.DefaultConstructorMarker) - @NotNull - public final net.corda.core.transactions.TransactionBuilder generateInitial(int, net.corda.core.identity.Party, net.corda.core.contracts.PartyAndReference, net.corda.core.contracts.PartyAndReference...) - @NotNull - public final net.corda.core.transactions.TransactionBuilder move(java.util.List>, net.corda.core.identity.AbstractParty) - @NotNull - public final net.corda.core.transactions.TransactionBuilder move(net.corda.core.contracts.StateAndRef, net.corda.core.identity.AbstractParty) -## -@DoNotImplement -public static final class net.corda.testing.contracts.DummyContract$MultiOwnerState extends java.lang.Object implements net.corda.testing.contracts.DummyContract$State - public (int, java.util.List) - public (int, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker) - public final int component1() - @NotNull - public final java.util.List component2() - @NotNull - public final net.corda.testing.contracts.DummyContract$MultiOwnerState copy(int, java.util.List) - public boolean equals(Object) - public int getMagicNumber() - @NotNull - public final java.util.List getOwners() - @NotNull - public java.util.List getParticipants() - public int hashCode() - @NotNull - public String toString() -## -@DoNotImplement -public static final class net.corda.testing.contracts.DummyContract$SingleOwnerState extends java.lang.Object implements net.corda.core.contracts.OwnableState, net.corda.testing.contracts.DummyContract$State - public (int, net.corda.core.identity.AbstractParty) - public (int, net.corda.core.identity.AbstractParty, int, kotlin.jvm.internal.DefaultConstructorMarker) - public final int component1() - @NotNull - public final net.corda.core.identity.AbstractParty component2() - @NotNull - public final net.corda.testing.contracts.DummyContract$SingleOwnerState copy(int, net.corda.core.identity.AbstractParty) - public boolean equals(Object) - public int getMagicNumber() - @NotNull - public net.corda.core.identity.AbstractParty getOwner() - @NotNull - public java.util.List getParticipants() - public int hashCode() - @NotNull - public String toString() - @NotNull - public net.corda.core.contracts.CommandAndState withNewOwner(net.corda.core.identity.AbstractParty) -## -@DoNotImplement -public static interface net.corda.testing.contracts.DummyContract$State extends net.corda.core.contracts.ContractState - public abstract int getMagicNumber() -## -public final class net.corda.testing.contracts.DummyContractV2 extends java.lang.Object implements net.corda.core.contracts.UpgradedContractWithLegacyConstraint - public () - @NotNull - public String getLegacyContract() - @NotNull - public net.corda.core.contracts.AttachmentConstraint getLegacyContractConstraint() - @NotNull - public net.corda.testing.contracts.DummyContractV2$State upgrade(net.corda.testing.contracts.DummyContract$State) - public void verify(net.corda.core.transactions.LedgerTransaction) - public static final net.corda.testing.contracts.DummyContractV2$Companion Companion - @NotNull - public static final String PROGRAM_ID = "net.corda.testing.contracts.DummyContractV2" -## -public static interface net.corda.testing.contracts.DummyContractV2$Commands extends net.corda.core.contracts.CommandData -## -public static final class net.corda.testing.contracts.DummyContractV2$Commands$Create extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV2$Commands - public () -## -public static final class net.corda.testing.contracts.DummyContractV2$Commands$Move extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV2$Commands - public () -## -public static final class net.corda.testing.contracts.DummyContractV2$Companion extends java.lang.Object - public (kotlin.jvm.internal.DefaultConstructorMarker) -## -public static final class net.corda.testing.contracts.DummyContractV2$State extends java.lang.Object implements net.corda.core.contracts.ContractState - public (int, java.util.List) - public (int, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker) - public final int component1() - @NotNull - public final java.util.List component2() - @NotNull - public final net.corda.testing.contracts.DummyContractV2$State copy(int, java.util.List) - public boolean equals(Object) - public final int getMagicNumber() - @NotNull - public final java.util.List getOwners() - @NotNull - public java.util.List getParticipants() - public int hashCode() - @NotNull - public String toString() -## -public final class net.corda.testing.contracts.DummyContractV3 extends java.lang.Object implements net.corda.core.contracts.UpgradedContractWithLegacyConstraint - public () - @NotNull - public String getLegacyContract() - @NotNull - public net.corda.core.contracts.AttachmentConstraint getLegacyContractConstraint() - @NotNull - public net.corda.testing.contracts.DummyContractV3$State upgrade(net.corda.testing.contracts.DummyContractV2$State) - public void verify(net.corda.core.transactions.LedgerTransaction) - public static final net.corda.testing.contracts.DummyContractV3$Companion Companion - @NotNull - public static final String PROGRAM_ID = "net.corda.testing.contracts.DummyContractV3" -## -public static interface net.corda.testing.contracts.DummyContractV3$Commands extends net.corda.core.contracts.CommandData -## -public static final class net.corda.testing.contracts.DummyContractV3$Commands$Create extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV3$Commands - public () -## -public static final class net.corda.testing.contracts.DummyContractV3$Commands$Move extends net.corda.core.contracts.TypeOnlyCommandData implements net.corda.testing.contracts.DummyContractV3$Commands - public () -## -public static final class net.corda.testing.contracts.DummyContractV3$Companion extends java.lang.Object - public (kotlin.jvm.internal.DefaultConstructorMarker) -## -public static final class net.corda.testing.contracts.DummyContractV3$State extends java.lang.Object implements net.corda.core.contracts.ContractState - public (int, java.util.List) - public (int, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker) - public final int component1() - @NotNull - public final java.util.List component2() - @NotNull - public final net.corda.testing.contracts.DummyContractV3$State copy(int, java.util.List) - public boolean equals(Object) - public final int getMagicNumber() - @NotNull - public final java.util.List getOwners() - @NotNull - public java.util.List getParticipants() - public int hashCode() - @NotNull - public String toString() -## -@BelongsToContract -public final class net.corda.testing.contracts.DummyState extends java.lang.Object implements net.corda.core.contracts.ContractState - public () - public (int) - public (int, java.util.List) - public (int, java.util.List, int, kotlin.jvm.internal.DefaultConstructorMarker) - public final int component1() - @NotNull - public final java.util.List component2() - @NotNull - public final net.corda.testing.contracts.DummyState copy(int) - @NotNull - public final net.corda.testing.contracts.DummyState copy(int, java.util.List) - public boolean equals(Object) - public final int getMagicNumber() - @NotNull - public java.util.List getParticipants() - public int hashCode() - @NotNull - public String toString() -## -public final class net.corda.testing.core.DummyCommandData extends net.corda.core.contracts.TypeOnlyCommandData - public static final net.corda.testing.core.DummyCommandData INSTANCE -## -public final class net.corda.testing.core.Expect extends java.lang.Object - public (Class, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1) - @NotNull - public final Class component1() - @NotNull - public final kotlin.jvm.functions.Function1 component2() - @NotNull - public final kotlin.jvm.functions.Function1 component3() - @NotNull - public final net.corda.testing.core.Expect copy(Class, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1) - public boolean equals(Object) - @NotNull - public final Class getClazz() - @NotNull - public final kotlin.jvm.functions.Function1 getExpectClosure() - @NotNull - public final kotlin.jvm.functions.Function1 getMatch() - public int hashCode() - @NotNull - public String toString() -## -@DoNotImplement -public abstract class net.corda.testing.core.ExpectCompose extends java.lang.Object - public (kotlin.jvm.internal.DefaultConstructorMarker) -## -@DoNotImplement -public static final class net.corda.testing.core.ExpectCompose$Parallel extends net.corda.testing.core.ExpectCompose - public (java.util.List>) - @NotNull - public final java.util.List> getParallel() -## -@DoNotImplement -public static final class net.corda.testing.core.ExpectCompose$Sequential extends net.corda.testing.core.ExpectCompose - public (java.util.List>) - @NotNull - public final java.util.List> getSequence() -## -@DoNotImplement -public static final class net.corda.testing.core.ExpectCompose$Single extends net.corda.testing.core.ExpectCompose - public (net.corda.testing.core.Expect) - @NotNull - public final net.corda.testing.core.Expect getExpect() -## -public static final class net.corda.testing.core.ExpectComposeState$Companion extends java.lang.Object - public (kotlin.jvm.internal.DefaultConstructorMarker) - @NotNull - public final net.corda.testing.core.ExpectComposeState fromExpectCompose(net.corda.testing.core.ExpectCompose) -## -public static final class net.corda.testing.core.ExpectComposeState$Finished extends net.corda.testing.core.ExpectComposeState - public () - @NotNull - public java.util.List> getExpectedEvents() - @Nullable - public Void nextState(E) -## -public static final class net.corda.testing.core.ExpectComposeState$Parallel extends net.corda.testing.core.ExpectComposeState - public (net.corda.testing.core.ExpectCompose$Parallel, java.util.List>) - @NotNull - public java.util.List> getExpectedEvents() - @NotNull - public final net.corda.testing.core.ExpectCompose$Parallel getParallel() - @NotNull - public final java.util.List> getStates() - @Nullable - public kotlin.Pair, net.corda.testing.core.ExpectComposeState> nextState(E) -## -public static final class net.corda.testing.core.ExpectComposeState$Sequential extends net.corda.testing.core.ExpectComposeState - public (net.corda.testing.core.ExpectCompose$Sequential, int, net.corda.testing.core.ExpectComposeState) - @NotNull - public java.util.List> getExpectedEvents() - public final int getIndex() - @NotNull - public final net.corda.testing.core.ExpectCompose$Sequential getSequential() - @NotNull - public final net.corda.testing.core.ExpectComposeState getState() - @Nullable - public kotlin.Pair, net.corda.testing.core.ExpectComposeState> nextState(E) -## -public static final class net.corda.testing.core.ExpectComposeState$Single extends net.corda.testing.core.ExpectComposeState - public (net.corda.testing.core.ExpectCompose$Single) - @NotNull - public java.util.List> getExpectedEvents() - @NotNull - public final net.corda.testing.core.ExpectCompose$Single getSingle() - @Nullable - public kotlin.Pair, net.corda.testing.core.ExpectComposeState> nextState(E) -## -public final class net.corda.testing.core.ExpectKt extends java.lang.Object - @NotNull - public static final net.corda.testing.core.ExpectCompose expect(Class, kotlin.jvm.functions.Function1, kotlin.jvm.functions.Function1) - public static final void expectEvents(Iterable, boolean, kotlin.jvm.functions.Function0>) - public static final void expectEvents(rx.Observable, boolean, kotlin.jvm.functions.Function0>) - public static final void genericExpectEvents(S, boolean, kotlin.jvm.functions.Function2, kotlin.Unit>, kotlin.jvm.functions.Function0>) - @NotNull - public static final net.corda.testing.core.ExpectCompose parallel(java.util.List>) - @NotNull - public static final net.corda.testing.core.ExpectCompose parallel(net.corda.testing.core.ExpectCompose...) - @NotNull - public static final net.corda.testing.core.ExpectCompose replicate(int, kotlin.jvm.functions.Function1>) - @NotNull - public static final net.corda.testing.core.ExpectCompose sequence(java.util.List>) - @NotNull - public static final net.corda.testing.core.ExpectCompose sequence(net.corda.testing.core.ExpectCompose...) -## -public final class net.corda.testing.core.SerializationEnvironmentRule extends java.lang.Object implements org.junit.rules.TestRule - public () - public (boolean) - public (boolean, int, kotlin.jvm.internal.DefaultConstructorMarker) - @NotNull - public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement, org.junit.runner.Description) - @NotNull - public final net.corda.core.serialization.SerializationFactory getSerializationFactory() - public static final net.corda.testing.core.SerializationEnvironmentRule$Companion Companion -## -public static final class net.corda.testing.core.SerializationEnvironmentRule$Companion extends java.lang.Object - public (kotlin.jvm.internal.DefaultConstructorMarker) -## -public final class net.corda.testing.core.TestConstants extends java.lang.Object - @NotNull - public static final net.corda.core.contracts.Command dummyCommand(java.security.PublicKey...) - @NotNull - public static final net.corda.core.identity.CordaX500Name ALICE_NAME - @NotNull - public static final net.corda.core.identity.CordaX500Name BOB_NAME - @NotNull - public static final net.corda.core.identity.CordaX500Name BOC_NAME - @NotNull - public static final net.corda.core.identity.CordaX500Name CHARLIE_NAME - @NotNull - public static final net.corda.core.identity.CordaX500Name DUMMY_BANK_A_NAME - @NotNull - public static final net.corda.core.identity.CordaX500Name DUMMY_BANK_B_NAME - @NotNull - public static final net.corda.core.identity.CordaX500Name DUMMY_BANK_C_NAME - @NotNull - public static final net.corda.core.identity.CordaX500Name DUMMY_NOTARY_NAME - public static final int MAX_MESSAGE_SIZE = 10485760 -## -public final class net.corda.testing.core.TestIdentity extends java.lang.Object - public (net.corda.core.identity.CordaX500Name) - public (net.corda.core.identity.CordaX500Name, long) - public (net.corda.core.identity.CordaX500Name, long, net.corda.core.crypto.SignatureScheme) - public (net.corda.core.identity.CordaX500Name, long, net.corda.core.crypto.SignatureScheme, int, kotlin.jvm.internal.DefaultConstructorMarker) - public (net.corda.core.identity.CordaX500Name, java.security.KeyPair) - public (net.corda.core.identity.CordaX500Name, net.corda.core.crypto.SignatureScheme) - public (net.corda.core.identity.CordaX500Name, net.corda.core.crypto.SignatureScheme, int, kotlin.jvm.internal.DefaultConstructorMarker) - @NotNull - public static final net.corda.testing.core.TestIdentity fresh(String) - @NotNull - public static final net.corda.testing.core.TestIdentity fresh(String, net.corda.core.crypto.SignatureScheme) - @NotNull - public final net.corda.core.identity.PartyAndCertificate getIdentity() - @NotNull - public final java.security.KeyPair getKeyPair() - @NotNull - public final net.corda.core.identity.CordaX500Name getName() - @NotNull - public final net.corda.core.identity.Party getParty() - @NotNull - public final java.security.PublicKey getPublicKey() - @NotNull - public final net.corda.core.contracts.PartyAndReference ref(byte...) - public static final net.corda.testing.core.TestIdentity$Companion Companion -## -public static final class net.corda.testing.core.TestIdentity$Companion extends java.lang.Object - public (kotlin.jvm.internal.DefaultConstructorMarker) - @NotNull - public final net.corda.testing.core.TestIdentity fresh(String) - @NotNull - public final net.corda.testing.core.TestIdentity fresh(String, net.corda.core.crypto.SignatureScheme) -## -public final class net.corda.testing.core.TestUtils extends java.lang.Object - @NotNull - public static final net.corda.core.utilities.NetworkHostAndPort freeLocalHostAndPort() - public static final int freePort() - @NotNull - public static final net.corda.core.contracts.StateRef generateStateRef() - @NotNull - public static final java.util.List getFreeLocalPorts(String, int) - @NotNull - public static final net.corda.core.identity.PartyAndCertificate getTestPartyAndCertificate(net.corda.core.identity.CordaX500Name, java.security.PublicKey) - @NotNull - public static final net.corda.core.identity.PartyAndCertificate getTestPartyAndCertificate(net.corda.core.identity.Party) - @NotNull - public static final net.corda.core.identity.CordaX500Name makeUnique(net.corda.core.identity.CordaX500Name) - @NotNull - public static final net.corda.core.identity.Party singleIdentity(net.corda.core.node.NodeInfo) - @NotNull - public static final net.corda.core.identity.PartyAndCertificate singleIdentityAndCert(net.corda.core.node.NodeInfo) -## public final class net.corda.testing.dsl.AttachmentResolutionException extends net.corda.core.flows.FlowException public (net.corda.core.crypto.SecureHash) ## diff --git a/build.gradle b/build.gradle index b43b778053..c045fcf197 100644 --- a/build.gradle +++ b/build.gradle @@ -83,6 +83,8 @@ buildscript { ext.tcnative_version = '2.0.14.Final' ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion") ext.fileupload_version = '1.4' + ext.kryo_version = '4.0.2' + ext.kryo_serializer_version = '0.43' // Legacy JUnit 4 version ext.junit_version = '4.12' // Need this version to access classpath scanning error handling fix - @@ -515,6 +517,7 @@ bintrayConfig { 'corda-node-api', 'corda-node-djvm', 'corda-test-common', + 'corda-core-test-utils', 'corda-test-utils', 'corda-test-db', 'corda-jackson', diff --git a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt index bd554c73e9..b6f8d20f23 100644 --- a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt +++ b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt @@ -42,8 +42,8 @@ import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract import net.corda.testing.core.* -import net.corda.testing.internal.createNodeInfoAndSigned -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.createNodeInfoAndSigned +import net.corda.coretesting.internal.rigorousMock import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Before diff --git a/client/rpc/build.gradle b/client/rpc/build.gradle index ee557d933b..d27d64c7d0 100644 --- a/client/rpc/build.gradle +++ b/client/rpc/build.gradle @@ -103,7 +103,7 @@ dependencies { smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" // JDK11: required by Quasar at run-time - smokeTestRuntimeOnly "com.esotericsoftware:kryo:4.0.2" + smokeTestRuntimeOnly "com.esotericsoftware:kryo:$kryo_version" } task integrationTest(type: Test) { diff --git a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt index b0c80244fa..96d83d1d81 100644 --- a/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt +++ b/client/rpc/src/integration-test/kotlin/net/corda/client/rpc/RPCStabilityTests.kt @@ -20,7 +20,7 @@ import net.corda.testing.common.internal.eventually import net.corda.testing.common.internal.succeeds import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.internal.testThreadFactory +import net.corda.coretesting.internal.testThreadFactory import net.corda.testing.node.internal.RPCDriverDSL import net.corda.testing.node.internal.RpcBrokerHandle import net.corda.testing.node.internal.RpcServerHandle diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt index 8b43e35a26..6308ae1ebf 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/CordaRPCClient.kt @@ -3,7 +3,7 @@ package net.corda.client.rpc import com.github.benmanes.caffeine.cache.Caffeine import net.corda.client.rpc.internal.RPCClient import net.corda.client.rpc.internal.ReconnectingCordaRPCOps -import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme +import net.corda.nodeapi.internal.rpc.client.AMQPClientSerializationScheme import net.corda.client.rpc.reconnect.CouldNotStartFlowException import net.corda.core.CordaInternal import net.corda.core.context.Actor diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt index aabea195fc..9dc7d5cc70 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt @@ -1,6 +1,5 @@ package net.corda.client.rpc.internal -import com.github.benmanes.caffeine.cache.Cache import com.github.benmanes.caffeine.cache.Caffeine import com.github.benmanes.caffeine.cache.RemovalCause import com.github.benmanes.caffeine.cache.RemovalListener @@ -11,7 +10,7 @@ import net.corda.client.rpc.ConnectionFailureException import net.corda.client.rpc.CordaRPCClientConfiguration import net.corda.client.rpc.RPCException import net.corda.client.rpc.RPCSinceVersion -import net.corda.client.rpc.internal.serialization.amqp.RpcClientObservableDeSerializer +import net.corda.nodeapi.internal.rpc.client.RpcClientObservableDeSerializer import net.corda.core.context.Actor import net.corda.core.context.Trace import net.corda.core.context.Trace.InvocationId @@ -33,6 +32,10 @@ import net.corda.core.utilities.getOrThrow import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi.CLASS_METHOD_DIVIDER import net.corda.nodeapi.internal.DeduplicationChecker +import net.corda.nodeapi.internal.rpc.client.CallSite +import net.corda.nodeapi.internal.rpc.client.CallSiteMap +import net.corda.nodeapi.internal.rpc.client.ObservableContext +import net.corda.nodeapi.internal.rpc.client.RpcObservableMap import org.apache.activemq.artemis.api.core.ActiveMQException import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException import org.apache.activemq.artemis.api.core.RoutingType @@ -257,9 +260,6 @@ class RPCClientProxyHandler( startSessions() } - /** A throwable that doesn't represent a real error - it's just here to wrap a stack trace. */ - class CallSite(val rpcName: String) : Throwable("") - // This is the general function that transforms a client side RPC to internal Artemis messages. override fun invoke(proxy: Any, method: Method, arguments: Array?): Any? { lifeCycle.requireState { it == State.STARTED || it == State.SERVER_VERSION_NOT_SET } @@ -666,20 +666,5 @@ class RPCClientProxyHandler( } } -private typealias RpcObservableMap = Cache>> -private typealias RpcReplyMap = ConcurrentHashMap> -private typealias CallSiteMap = ConcurrentHashMap - -/** - * Holds a context available during de-serialisation of messages that are expected to contain Observables. - * - * @property observableMap holds the Observables that are ultimately exposed to the user. - * @property hardReferenceStore holds references to Observables we want to keep alive while they are subscribed to. - * @property callSiteMap keeps stack traces captured when an RPC was invoked, useful for debugging when an observable leaks. - */ -data class ObservableContext( - val callSiteMap: CallSiteMap?, - val observableMap: RpcObservableMap, - val hardReferenceStore: MutableSet> -) +private typealias RpcReplyMap = ConcurrentHashMap> diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt index 01514e12d3..f5aa1357e8 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt @@ -8,7 +8,7 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.millis import net.corda.node.services.rpc.RPCServerConfiguration -import net.corda.testing.internal.testThreadFactory +import net.corda.coretesting.internal.testThreadFactory import net.corda.testing.node.internal.RPCDriverDSL import net.corda.testing.node.internal.rpcDriver import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt index e1a0b3e853..ce423a748b 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt @@ -5,7 +5,7 @@ import net.corda.core.messaging.RPCOps import net.corda.core.utilities.minutes import net.corda.core.utilities.seconds import net.corda.node.services.rpc.RPCServerConfiguration -import net.corda.testing.internal.performance.div +import net.corda.coretesting.internal.performance.div import net.corda.testing.node.internal.RPCDriverDSL import net.corda.testing.node.internal.performance.startPublishingFixedRateInjector import net.corda.testing.node.internal.performance.startReporter diff --git a/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt b/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt index 6e878a6616..8d2d10d04f 100644 --- a/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt +++ b/common/logging/src/main/kotlin/net/corda/common/logging/Constants.kt @@ -9,4 +9,4 @@ package net.corda.common.logging * (originally added to source control for ease of use) */ -internal const val CURRENT_MAJOR_RELEASE = "4.5-SNAPSHOT" +internal const val CURRENT_MAJOR_RELEASE = "4.5-SNAPSHOT" \ No newline at end of file diff --git a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt index 71d4ff23c6..fe99ca34ca 100644 --- a/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt +++ b/confidential-identities/src/test/kotlin/net/corda/confidential/SwapIdentitiesFlowTests.kt @@ -15,9 +15,9 @@ import net.corda.core.identity.AnonymousParty import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.testing.core.* -import net.corda.testing.internal.matchers.allOf -import net.corda.testing.internal.matchers.flow.willReturn -import net.corda.testing.internal.matchers.hasOnlyEntries +import net.corda.coretesting.internal.matchers.allOf +import net.corda.coretesting.internal.matchers.flow.willReturn +import net.corda.coretesting.internal.matchers.hasOnlyEntries import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.TestStartedNode import net.corda.testing.node.internal.enclosedCordapp diff --git a/core-tests/src/test/kotlin/net/corda/coretests/crypto/PartialMerkleTreeTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/crypto/PartialMerkleTreeTest.kt index 4a50b3ffdc..edcca8e214 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/crypto/PartialMerkleTreeTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/crypto/PartialMerkleTreeTest.kt @@ -24,7 +24,7 @@ import net.corda.testing.core.TestIdentity import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter -import net.corda.testing.internal.TEST_TX_TIME +import net.corda.coretesting.internal.TEST_TX_TIME import net.corda.testing.internal.createWireTransaction import net.corda.testing.node.MockServices import net.corda.testing.node.ledger diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/AttachmentTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/AttachmentTests.kt index 445cfc4eb0..50fed81556 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/AttachmentTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/AttachmentTests.kt @@ -21,8 +21,8 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.makeUnique import net.corda.testing.core.singleIdentity import net.corda.testing.internal.fakeAttachment -import net.corda.testing.internal.matchers.flow.willReturn -import net.corda.testing.internal.matchers.flow.willThrow +import net.corda.coretesting.internal.matchers.flow.willReturn +import net.corda.coretesting.internal.matchers.flow.willThrow import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNodeParameters import net.corda.testing.node.internal.TestStartedNode diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/CollectSignaturesFlowTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/CollectSignaturesFlowTests.kt index e047358d1e..f0d13e2123 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/CollectSignaturesFlowTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/CollectSignaturesFlowTests.kt @@ -30,9 +30,9 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.CHARLIE_NAME import net.corda.testing.core.TestIdentity import net.corda.testing.core.singleIdentity -import net.corda.testing.internal.matchers.flow.willReturn -import net.corda.testing.internal.matchers.flow.willThrow -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.matchers.flow.willReturn +import net.corda.coretesting.internal.matchers.flow.willThrow +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.MockServices import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP import net.corda.testing.node.internal.InternalMockNetwork diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowRPCTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowRPCTest.kt index 1c00ee69de..fa7fa39c6f 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowRPCTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowRPCTest.kt @@ -18,8 +18,8 @@ import net.corda.testing.contracts.DummyContractV2 import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.singleIdentity -import net.corda.testing.internal.matchers.rpc.willReturn -import net.corda.testing.internal.matchers.rpc.willThrow +import net.corda.coretesting.internal.matchers.rpc.willReturn +import net.corda.coretesting.internal.matchers.rpc.willThrow import net.corda.testing.node.User import net.corda.testing.node.internal.* import org.junit.AfterClass diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowTest.kt index 7121630864..54678ff3c6 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/ContractUpgradeFlowTest.kt @@ -20,8 +20,8 @@ import net.corda.testing.contracts.DummyContractV3 import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.singleIdentity -import net.corda.testing.internal.matchers.flow.willReturn -import net.corda.testing.internal.matchers.flow.willThrow +import net.corda.coretesting.internal.matchers.flow.willReturn +import net.corda.coretesting.internal.matchers.flow.willThrow import net.corda.testing.node.internal.* import org.junit.AfterClass import org.junit.Test diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt index ef37af8364..175fcc0cfd 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/FinalityFlowTests.kt @@ -13,8 +13,8 @@ import net.corda.finance.POUNDS import net.corda.finance.contracts.asset.Cash import net.corda.finance.issuedBy import net.corda.testing.core.* -import net.corda.testing.internal.matchers.flow.willReturn -import net.corda.testing.internal.matchers.flow.willThrow +import net.corda.coretesting.internal.matchers.flow.willReturn +import net.corda.coretesting.internal.matchers.flow.willThrow import net.corda.testing.node.internal.* import org.assertj.core.api.Assertions.assertThat import org.junit.After diff --git a/core-tests/src/test/kotlin/net/corda/coretests/flows/ReceiveAllFlowTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/flows/ReceiveAllFlowTests.kt index 92441fe7c6..8dff9e634b 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/flows/ReceiveAllFlowTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/flows/ReceiveAllFlowTests.kt @@ -11,7 +11,7 @@ import net.corda.testing.core.singleIdentity import net.corda.testing.flows.from import net.corda.testing.flows.receiveAll import net.corda.testing.flows.registerCordappFlowFactory -import net.corda.testing.internal.matchers.flow.willReturn +import net.corda.coretesting.internal.matchers.flow.willReturn import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.TestStartedNode import org.junit.AfterClass diff --git a/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt index 436e222b86..54898eeaeb 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt @@ -12,7 +12,7 @@ import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.getTestPartyAndCertificate -import net.corda.testing.internal.DEV_ROOT_CA +import net.corda.coretesting.internal.DEV_ROOT_CA import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.junit.Test diff --git a/core-tests/src/test/kotlin/net/corda/coretests/serialization/TransactionSerializationTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/serialization/TransactionSerializationTests.kt index b305a49610..bce4e67224 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/serialization/TransactionSerializationTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/serialization/TransactionSerializationTests.kt @@ -19,8 +19,8 @@ import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.TestIdentity -import net.corda.testing.internal.TEST_TX_TIME -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.TEST_TX_TIME +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.MockServices import org.junit.Before import org.junit.Rule diff --git a/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionBuilderTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionBuilderTest.kt index 500bc101ab..55fb43f003 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionBuilderTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionBuilderTest.kt @@ -20,7 +20,7 @@ import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyState import net.corda.testing.core.* -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Assert.assertFalse diff --git a/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionTests.kt b/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionTests.kt index cec21d0f08..ec14b84029 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionTests.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/transactions/TransactionTests.kt @@ -17,7 +17,7 @@ import net.corda.testing.contracts.DummyContract import net.corda.testing.core.* import net.corda.testing.internal.createWireTransaction import net.corda.testing.internal.fakeAttachment -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import org.junit.Rule import org.junit.Test import java.math.BigInteger diff --git a/core-tests/src/test/kotlin/net/corda/coretests/utilities/KotlinUtilsTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/utilities/KotlinUtilsTest.kt index 15dd6aef8d..e989b3a107 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/utilities/KotlinUtilsTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/utilities/KotlinUtilsTest.kt @@ -7,7 +7,7 @@ import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.internal.checkpointDeserialize import net.corda.core.serialization.internal.checkpointSerialize import net.corda.core.utilities.transient -import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT +import net.corda.nodeapi.internal.serialization.kryo.KRYO_CHECKPOINT_CONTEXT import net.corda.serialization.internal.CheckpointSerializationContextImpl import net.corda.testing.core.SerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat diff --git a/core/build.gradle b/core/build.gradle index cbe24830ac..37b542c0e9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -82,7 +82,7 @@ dependencies { testCompile "org.ow2.asm:asm:$asm_version" // JDK11: required by Quasar at run-time - testRuntimeOnly "com.esotericsoftware:kryo:4.0.2" + testRuntimeOnly "com.esotericsoftware:kryo:$kryo_version" testCompile "com.nhaarman:mockito-kotlin:$mockito_kotlin_version" testCompile "org.mockito:mockito-core:$mockito_version" diff --git a/detekt-baseline.xml b/detekt-baseline.xml index 39017a0c2b..3e147108a6 100644 --- a/detekt-baseline.xml +++ b/detekt-baseline.xml @@ -1198,7 +1198,7 @@ MagicNumber:X509Utilities.kt$X509Utilities$3650 MagicNumber:errorAndTerminate.kt$10 MatchingDeclarationName:AMQPSerializerFactories.kt$net.corda.serialization.internal.amqp.AMQPSerializerFactories.kt - MatchingDeclarationName:AMQPTestSerialiationScheme.kt$net.corda.node.internal.serialization.testutils.AMQPTestSerialiationScheme.kt + MatchingDeclarationName:AMQPTestSerialiationScheme.kt$net.corda.nodeapi.internal.serialization.testutils.AMQPTestSerialiationScheme.kt MatchingDeclarationName:AttachmentDemo.kt$net.corda.attachmentdemo.AttachmentDemo.kt MatchingDeclarationName:AzureRegistryLocator.kt$net.corda.networkbuilder.containers.push.azure.AzureRegistryLocator.kt MatchingDeclarationName:CheckpointSerializationScheme.kt$net.corda.serialization.internal.CheckpointSerializationScheme.kt @@ -2113,7 +2113,7 @@ WildcardImport:FlowFrameworkTests.kt$import net.corda.testing.node.internal.* WildcardImport:FlowFrameworkTripartyTests.kt$import net.corda.testing.node.internal.* WildcardImport:FlowLogicRefFactoryImpl.kt$import net.corda.core.flows.* - WildcardImport:FlowMatchers.kt$import net.corda.testing.internal.matchers.* + WildcardImport:FlowMatchers.kt$import net.corda.coretesting.internal.matchers.* WildcardImport:FlowOverrideTests.kt$import net.corda.core.flows.* WildcardImport:FlowRetryTest.kt$import net.corda.core.flows.* WildcardImport:FlowStackSnapshotTest.kt$import net.corda.core.flows.* diff --git a/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt b/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt index b5670ef17c..dc7e657ad5 100644 --- a/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt +++ b/docs/source/example-code/src/test/kotlin/net/corda/docs/kotlin/tutorial/testdsl/TutorialTestDSL.kt @@ -17,7 +17,6 @@ import net.corda.finance.contracts.ICommercialPaperState import net.corda.finance.contracts.asset.CASH import net.corda.finance.contracts.asset.Cash import net.corda.testing.core.* -import net.corda.testing.internal.rigorousMock import net.corda.testing.node.MockServices import net.corda.testing.node.ledger import net.corda.testing.node.transaction diff --git a/finance/contracts/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt b/finance/contracts/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt index bdebb8f1cc..67b075dee0 100644 --- a/finance/contracts/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt +++ b/finance/contracts/src/test/kotlin/net/corda/finance/contracts/CommercialPaperTests.kt @@ -22,7 +22,7 @@ import net.corda.testing.core.* import net.corda.testing.dsl.EnforceVerifyOrFail import net.corda.testing.dsl.TransactionDSL import net.corda.testing.dsl.TransactionDSLInterpreter -import net.corda.testing.internal.TEST_TX_TIME +import net.corda.coretesting.internal.TEST_TX_TIME import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices diff --git a/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt b/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt index 9f417b9a35..e2be35de43 100644 --- a/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt +++ b/finance/contracts/src/test/kotlin/net/corda/finance/contracts/asset/ObligationTests.kt @@ -24,7 +24,7 @@ import net.corda.finance.workflows.asset.ObligationUtils import net.corda.testing.contracts.DummyContract import net.corda.testing.core.* import net.corda.testing.dsl.* -import net.corda.testing.internal.TEST_TX_TIME +import net.corda.coretesting.internal.TEST_TX_TIME import net.corda.testing.internal.fakeAttachment import net.corda.testing.internal.vault.CommodityState import net.corda.testing.node.MockServices diff --git a/node-api/build.gradle b/node-api/build.gradle index e2ac2b7f66..3f1797aa94 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -30,6 +30,10 @@ dependencies { // ClassGraph: classpath scanning compile "io.github.classgraph:classgraph:$class_graph_version" + // Kryo: object graph serialization. + compile "com.esotericsoftware:kryo:$kryo_version" + compile "de.javakaffee:kryo-serializers:$kryo_serializer_version" + // For caches rather than guava compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version" @@ -39,7 +43,7 @@ dependencies { runtime 'com.mattbertolini:liquibase-slf4j:2.0.0' // JDK11: required by Quasar at run-time - runtime "com.esotericsoftware:kryo:4.0.2" + runtime "com.esotericsoftware:kryo:$kryo_version" testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "junit:junit:$junit_version" diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/ObservableContextInterface.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/ObservableContextInterface.kt similarity index 95% rename from node/src/main/kotlin/net/corda/node/services/rpc/ObservableContextInterface.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/ObservableContextInterface.kt index 030fd5721a..cd14529152 100644 --- a/node/src/main/kotlin/net/corda/node/services/rpc/ObservableContextInterface.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/ObservableContextInterface.kt @@ -1,4 +1,4 @@ -package net.corda.node.services.rpc +package net.corda.nodeapi.internal.rpc import com.github.benmanes.caffeine.cache.Cache import net.corda.core.context.Trace diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/ObservableSubscription.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/ObservableSubscription.kt new file mode 100644 index 0000000000..1b3dabe04c --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/ObservableSubscription.kt @@ -0,0 +1,7 @@ +package net.corda.nodeapi.internal.rpc + +import rx.Subscription + +class ObservableSubscription( + val subscription: Subscription +) diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/AMQPClientSerializationScheme.kt similarity index 84% rename from client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/AMQPClientSerializationScheme.kt index fc36e59ffd..e86796bf8b 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/AMQPClientSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/AMQPClientSerializationScheme.kt @@ -1,4 +1,4 @@ -package net.corda.client.rpc.internal.serialization.amqp +package net.corda.nodeapi.internal.rpc.client import net.corda.core.cordapp.Cordapp import net.corda.core.internal.toSynchronised @@ -8,9 +8,18 @@ import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.internal.SerializationEnvironment import net.corda.core.serialization.internal._rpcClientSerializationEnv -import net.corda.core.serialization.internal.nodeSerializationEnv -import net.corda.serialization.internal.* -import net.corda.serialization.internal.amqp.* +import net.corda.serialization.internal.AMQP_P2P_CONTEXT +import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT +import net.corda.serialization.internal.AMQP_RPC_SERVER_CONTEXT +import net.corda.serialization.internal.AMQP_STORAGE_CONTEXT +import net.corda.serialization.internal.CordaSerializationMagic +import net.corda.serialization.internal.SerializationFactoryImpl +import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme +import net.corda.serialization.internal.amqp.AccessOrderLinkedHashMap +import net.corda.serialization.internal.amqp.SerializationFactoryCacheKey +import net.corda.serialization.internal.amqp.SerializerFactory +import net.corda.serialization.internal.amqp.SerializerFactoryBuilder +import net.corda.serialization.internal.amqp.amqpMagic import net.corda.serialization.internal.amqp.custom.RxNotificationSerializer /** diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/ObservableContext.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/ObservableContext.kt new file mode 100644 index 0000000000..8bd4d08bfa --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/ObservableContext.kt @@ -0,0 +1,27 @@ +package net.corda.nodeapi.internal.rpc.client + +import com.github.benmanes.caffeine.cache.Cache +import net.corda.core.context.Trace +import rx.Notification +import rx.Observable +import rx.subjects.UnicastSubject +import java.util.concurrent.ConcurrentHashMap + +/** A throwable that doesn't represent a real error - it's just here to wrap a stack trace. */ +class CallSite(val rpcName: String) : Throwable("") + +typealias RpcObservableMap = Cache>> +typealias CallSiteMap = ConcurrentHashMap + +/** + * Holds a context available during de-serialisation of messages that are expected to contain Observables. + * + * @property observableMap holds the Observables that are ultimately exposed to the user. + * @property hardReferenceStore holds references to Observables we want to keep alive while they are subscribed to. + * @property callSiteMap keeps stack traces captured when an RPC was invoked, useful for debugging when an observable leaks. + */ +data class ObservableContext( + val callSiteMap: CallSiteMap?, + val observableMap: RpcObservableMap, + val hardReferenceStore: MutableSet> +) diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/RpcClientCordaFutureSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/RpcClientCordaFutureSerializer.kt similarity index 90% rename from client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/RpcClientCordaFutureSerializer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/RpcClientCordaFutureSerializer.kt index 44e2a414a0..f7c23472f4 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/RpcClientCordaFutureSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/RpcClientCordaFutureSerializer.kt @@ -1,4 +1,4 @@ -package net.corda.client.rpc.internal.serialization.amqp +package net.corda.nodeapi.internal.rpc.client import net.corda.core.concurrent.CordaFuture import net.corda.core.toFuture @@ -14,7 +14,7 @@ import java.io.NotSerializableException class RpcClientCordaFutureSerializer (factory: SerializerFactory) : CustomSerializer.Proxy, RpcClientCordaFutureSerializer.FutureProxy>( CordaFuture::class.java, - RpcClientCordaFutureSerializer.FutureProxy::class.java, factory + FutureProxy::class.java, factory ) { override fun fromProxy(proxy: FutureProxy): CordaFuture<*> { try { diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/RpcClientObservableDeSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/RpcClientObservableDeSerializer.kt similarity index 93% rename from client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/RpcClientObservableDeSerializer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/RpcClientObservableDeSerializer.kt index 17ba71e200..765a70dd94 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/serialization/amqp/RpcClientObservableDeSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/rpc/client/RpcClientObservableDeSerializer.kt @@ -1,8 +1,6 @@ -package net.corda.client.rpc.internal.serialization.amqp +package net.corda.nodeapi.internal.rpc.client -import net.corda.client.rpc.internal.ObservableContext -import net.corda.client.rpc.internal.RPCClientProxyHandler import net.corda.core.context.Trace import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.loggerFor @@ -86,7 +84,7 @@ object RpcClientObservableDeSerializer : CustomSerializer.Implements) throw NotSerializableException("Input must be a serialised list") if (obj.size != 2) throw NotSerializableException("Expecting two elements, have ${obj.size}") @@ -105,7 +103,8 @@ object RpcClientObservableDeSerializer : CustomSerializer.Implements() } - private fun getRpcCallSite(context: SerializationContext, observableContext: ObservableContext): RPCClientProxyHandler.CallSite? { + private fun getRpcCallSite(context: SerializationContext, observableContext: ObservableContext): CallSite? { val rpcRequestOrObservableId = context.properties[RPCApi.RpcRequestOrObservableIdKey] as Trace.InvocationId // Will only return non-null if the trackRpcCallSites option in the RPC configuration has been specified. return observableContext.callSiteMap?.get(rpcRequestOrObservableId) diff --git a/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPServerSerializationScheme.kt similarity index 97% rename from node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPServerSerializationScheme.kt index ab801c9cde..d16f9d6466 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/amqp/AMQPServerSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/AMQPServerSerializationScheme.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.amqp +package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.cordapp.Cordapp import net.corda.core.internal.toSynchronised diff --git a/node/src/main/kotlin/net/corda/node/serialization/amqp/RpcServerCordaFutureSerialiser.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/RpcServerCordaFutureSerializer.kt similarity index 74% rename from node/src/main/kotlin/net/corda/node/serialization/amqp/RpcServerCordaFutureSerialiser.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/RpcServerCordaFutureSerializer.kt index 63430637d3..a604091f2e 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/amqp/RpcServerCordaFutureSerialiser.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/RpcServerCordaFutureSerializer.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.amqp +package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.concurrent.CordaFuture import net.corda.core.toObservable @@ -14,13 +14,13 @@ import java.io.NotSerializableException class RpcServerCordaFutureSerializer(factory: SerializerFactory) : CustomSerializer.Proxy, RpcServerCordaFutureSerializer.FutureProxy>( - CordaFuture::class.java, RpcServerCordaFutureSerializer.FutureProxy::class.java, factory + CordaFuture::class.java, FutureProxy::class.java, factory ) { - override fun fromProxy(proxy: RpcServerCordaFutureSerializer.FutureProxy): CordaFuture<*> { + override fun fromProxy(proxy: FutureProxy): CordaFuture<*> { throw UnsupportedOperationException() } - override fun toProxy(obj: CordaFuture<*>): RpcServerCordaFutureSerializer.FutureProxy { + override fun toProxy(obj: CordaFuture<*>): FutureProxy { try { return FutureProxy(obj.toObservable()) } catch (e: NotSerializableException) { diff --git a/node/src/main/kotlin/net/corda/node/serialization/amqp/RpcServerObservableSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/RpcServerObservableSerializer.kt similarity index 92% rename from node/src/main/kotlin/net/corda/node/serialization/amqp/RpcServerObservableSerializer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/RpcServerObservableSerializer.kt index 441ac77da4..6bb822f558 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/amqp/RpcServerObservableSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/RpcServerObservableSerializer.kt @@ -1,12 +1,12 @@ -package net.corda.node.serialization.amqp +package net.corda.nodeapi.internal.serialization.amqp import net.corda.core.context.Trace import net.corda.core.serialization.SerializationContext import net.corda.core.utilities.contextLogger import net.corda.core.utilities.loggerFor -import net.corda.node.services.rpc.ObservableContextInterface -import net.corda.node.services.rpc.ObservableSubscription import net.corda.nodeapi.RPCApi +import net.corda.nodeapi.internal.rpc.ObservableContextInterface +import net.corda.nodeapi.internal.rpc.ObservableSubscription import net.corda.serialization.internal.amqp.* import org.apache.qpid.proton.codec.Data import rx.Notification @@ -31,7 +31,7 @@ class RpcServerObservableSerializer : CustomSerializer.Implements> fun createContext( serializationContext: SerializationContext, observableContext: ObservableContextInterface - ) = serializationContext.withProperty(RpcServerObservableSerializer.RpcObservableContextKey, observableContext) + ) = serializationContext.withProperty(RpcObservableContextKey, observableContext) val log = contextLogger() } @@ -80,11 +80,11 @@ class RpcServerObservableSerializer : CustomSerializer.Implements> context: SerializationContext ) { val observableId = Trace.InvocationId.newInstance() - if (RpcServerObservableSerializer.RpcObservableContextKey !in context.properties) { + if (RpcObservableContextKey !in context.properties) { throw NotSerializableException("Missing Observable Key on serialization context - $type") } - val observableContext = context.properties[RpcServerObservableSerializer.RpcObservableContextKey] + val observableContext = context.properties[RpcObservableContextKey] as ObservableContextInterface data.withList { diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/CordaClassResolver.kt similarity index 99% rename from node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/CordaClassResolver.kt index 87183504e8..66715858a4 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClassResolver.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/CordaClassResolver.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.kryo +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.* import com.esotericsoftware.kryo.io.Input diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClosureSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/CordaClosureSerializer.kt similarity index 94% rename from node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClosureSerializer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/CordaClosureSerializer.kt index 2f005b7372..13ecd2682c 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/CordaClosureSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/CordaClosureSerializer.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.kryo +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.io.Output diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/DefaultKryoCustomizer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt similarity index 99% rename from node/src/main/kotlin/net/corda/node/serialization/kryo/DefaultKryoCustomizer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt index b8130dce5f..615f9a74f5 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/DefaultKryoCustomizer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.kryo +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Serializer diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt similarity index 99% rename from node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt index 6d2b2e4988..e5de8a1341 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/Kryo.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.kryo +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.* import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoCheckpointSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoCheckpointSerializer.kt similarity index 99% rename from node/src/main/kotlin/net/corda/node/serialization/kryo/KryoCheckpointSerializer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoCheckpointSerializer.kt index cd74dafb4c..7dddc2a65f 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoCheckpointSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoCheckpointSerializer.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.kryo +package net.corda.nodeapi.internal.serialization.kryo import co.paralleluniverse.fibers.Fiber import co.paralleluniverse.io.serialization.kryo.KryoSerializer diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoStreams.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreams.kt similarity index 95% rename from node/src/main/kotlin/net/corda/node/serialization/kryo/KryoStreams.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreams.kt index 2a6bd1c0c4..80591ebc07 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/KryoStreams.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreams.kt @@ -1,6 +1,6 @@ @file:JvmName("KryoStreams") -package net.corda.node.serialization.kryo +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output diff --git a/node/src/main/kotlin/net/corda/node/serialization/kryo/SerializeAsTokenSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt similarity index 96% rename from node/src/main/kotlin/net/corda/node/serialization/kryo/SerializeAsTokenSerializer.kt rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt index 3fa6d0b034..142e9fe35e 100644 --- a/node/src/main/kotlin/net/corda/node/serialization/kryo/SerializeAsTokenSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/SerializeAsTokenSerializer.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.kryo +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.KryoException diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt index a63ca7dcdb..27276cb615 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt @@ -25,7 +25,7 @@ import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.TestIdentity import net.corda.testing.internal.MockCordappConfigProvider -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.internal.cordappWithPackages import net.corda.testing.services.MockAttachmentStorage import org.assertj.core.api.Assertions.assertThat diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt index 6b82e1b9d2..eb7f2c20b6 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt @@ -11,8 +11,8 @@ import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.internal.TestNodeInfoBuilder -import net.corda.testing.internal.signWith +import net.corda.coretesting.internal.TestNodeInfoBuilder +import net.corda.coretesting.internal.signWith import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Rule diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/AliasPrivateKeyTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/AliasPrivateKeyTest.kt index 983f59c185..e68871bbcb 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/AliasPrivateKeyTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/AliasPrivateKeyTest.kt @@ -1,7 +1,7 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.crypto.internal.AliasPrivateKey -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt index a086e91306..f548fb0487 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt @@ -21,7 +21,7 @@ import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize import net.corda.core.utilities.days import net.corda.core.utilities.hours -import net.corda.node.serialization.amqp.AMQPServerSerializationScheme +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME @@ -37,11 +37,11 @@ import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.TestIdentity import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.internal.NettyTestClient -import net.corda.testing.internal.NettyTestHandler -import net.corda.testing.internal.NettyTestServer +import net.corda.coretesting.internal.NettyTestClient +import net.corda.coretesting.internal.NettyTestHandler +import net.corda.coretesting.internal.NettyTestServer import net.corda.testing.internal.createDevIntermediateCaCertPath -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.i2p.crypto.eddsa.EdDSAPrivateKey import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x509.* diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt index 0f441b0ae6..458cabdcba 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt @@ -12,7 +12,7 @@ import net.corda.nodeapi.internal.cryptoservice.CryptoServiceException import net.corda.nodeapi.internal.cryptoservice.WrappedPrivateKey import net.corda.nodeapi.internal.cryptoservice.WrappingMode import net.corda.testing.core.ALICE_NAME -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.bouncycastle.jce.provider.BouncyCastleProvider diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt index ff2cdb00eb..4d94be99a6 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt @@ -18,8 +18,12 @@ import net.corda.nodeapi.internal.config.toConfig import net.corda.nodeapi.internal.network.NetworkBootstrapper.Companion.DEFAULT_MAX_MESSAGE_SIZE import net.corda.nodeapi.internal.network.NetworkBootstrapper.Companion.DEFAULT_MAX_TRANSACTION_SIZE import net.corda.nodeapi.internal.network.NodeInfoFilesCopier.Companion.NODE_INFO_FILE_NAME_PREFIX -import net.corda.testing.core.* -import net.corda.testing.internal.createNodeInfoAndSigned +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.DUMMY_NOTARY_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity +import net.corda.coretesting.internal.createNodeInfoAndSigned import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After diff --git a/node/src/test/kotlin/net/corda/node/internal/serialization/RoundTripObservableSerializerTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/RoundTripObservableSerializerTests.kt similarity index 85% rename from node/src/test/kotlin/net/corda/node/internal/serialization/RoundTripObservableSerializerTests.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/RoundTripObservableSerializerTests.kt index af1739b3c1..f60f5488f5 100644 --- a/node/src/test/kotlin/net/corda/node/internal/serialization/RoundTripObservableSerializerTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/RoundTripObservableSerializerTests.kt @@ -1,19 +1,19 @@ -package net.corda.node.internal.serialization +package net.corda.nodeapi.internal.serialization import co.paralleluniverse.common.util.SameThreadExecutor import com.github.benmanes.caffeine.cache.Cache import com.github.benmanes.caffeine.cache.Caffeine import com.github.benmanes.caffeine.cache.RemovalListener import com.nhaarman.mockito_kotlin.mock -import net.corda.client.rpc.internal.serialization.amqp.RpcClientObservableDeSerializer +import net.corda.nodeapi.internal.rpc.client.RpcClientObservableDeSerializer import net.corda.core.context.Trace import net.corda.core.internal.ThreadBox import net.corda.core.internal.toSynchronised -import net.corda.node.internal.serialization.testutils.AMQPRoundTripRPCSerializationScheme -import net.corda.node.internal.serialization.testutils.serializationContext -import net.corda.node.serialization.amqp.RpcServerObservableSerializer -import net.corda.node.services.rpc.ObservableSubscription +import net.corda.nodeapi.internal.serialization.testutils.AMQPRoundTripRPCSerializationScheme +import net.corda.nodeapi.internal.serialization.testutils.serializationContext +import net.corda.nodeapi.internal.serialization.amqp.RpcServerObservableSerializer import net.corda.nodeapi.RPCApi +import net.corda.nodeapi.internal.rpc.ObservableSubscription import net.corda.serialization.internal.amqp.* import org.apache.activemq.artemis.api.core.SimpleString import org.junit.Test @@ -24,8 +24,8 @@ import java.time.Instant import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.TimeUnit -import net.corda.client.rpc.internal.ObservableContext as ClientObservableContext -import net.corda.node.internal.serialization.testutils.TestObservableContext as ServerObservableContext +import net.corda.nodeapi.internal.rpc.client.ObservableContext as ClientObservableContext +import net.corda.nodeapi.internal.serialization.testutils.TestObservableContext as ServerObservableContext class RoundTripObservableSerializerTests { private fun getID() = Trace.InvocationId("test1", Instant.now()) diff --git a/node/src/test/kotlin/net/corda/node/internal/serialization/RpcServerObservableSerializerTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/RpcServerObservableSerializerTests.kt similarity index 89% rename from node/src/test/kotlin/net/corda/node/internal/serialization/RpcServerObservableSerializerTests.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/RpcServerObservableSerializerTests.kt index dad905cae4..d0f8f6b3f6 100644 --- a/node/src/test/kotlin/net/corda/node/internal/serialization/RpcServerObservableSerializerTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/RpcServerObservableSerializerTests.kt @@ -1,13 +1,13 @@ -package net.corda.node.internal.serialization +package net.corda.nodeapi.internal.serialization import com.github.benmanes.caffeine.cache.Cache import com.github.benmanes.caffeine.cache.Caffeine import com.nhaarman.mockito_kotlin.mock import net.corda.core.context.Trace -import net.corda.node.internal.serialization.testutils.TestObservableContext -import net.corda.node.internal.serialization.testutils.serializationContext -import net.corda.node.serialization.amqp.RpcServerObservableSerializer -import net.corda.node.services.rpc.ObservableSubscription +import net.corda.nodeapi.internal.serialization.testutils.TestObservableContext +import net.corda.nodeapi.internal.serialization.testutils.serializationContext +import net.corda.nodeapi.internal.rpc.ObservableSubscription +import net.corda.nodeapi.internal.serialization.amqp.RpcServerObservableSerializer import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.amqp.SerializationOutput import net.corda.serialization.internal.amqp.SerializerFactoryBuilder diff --git a/node/src/test/kotlin/net/corda/node/serialization/kryo/KryoStreamsTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreamsTest.kt similarity index 98% rename from node/src/test/kotlin/net/corda/node/serialization/kryo/KryoStreamsTest.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreamsTest.kt index 67f7762129..b54bd8c458 100644 --- a/node/src/test/kotlin/net/corda/node/serialization/kryo/KryoStreamsTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoStreamsTest.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.kryo +package net.corda.nodeapi.internal.serialization.kryo import net.corda.core.internal.declaredField import net.corda.serialization.internal.ByteBufferOutputStream diff --git a/node/src/test/kotlin/net/corda/node/serialization/kryo/KryoTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt similarity index 98% rename from node/src/test/kotlin/net/corda/node/serialization/kryo/KryoTests.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt index 02688df506..1fae69d049 100644 --- a/node/src/test/kotlin/net/corda/node/serialization/kryo/KryoTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt @@ -1,4 +1,4 @@ -package net.corda.node.serialization.kryo +package net.corda.nodeapi.internal.serialization.kryo import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.KryoException @@ -23,7 +23,7 @@ import net.corda.serialization.internal.* import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.TestIdentity import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import org.apache.commons.lang3.SystemUtils import org.assertj.core.api.Assertions.* import org.junit.Assert.assertArrayEquals @@ -356,9 +356,9 @@ class KryoTests(private val compression: CordaSerializationEncoding?) { val compressedSize = obj.checkpointSerialize(context.withEncoding(CordaSerializationEncoding.SNAPPY)).size // If these need fixing, sounds like Kryo wire format changed and checkpoints might not survive an upgrade. if (SystemUtils.IS_JAVA_11) - assertEquals(20172, uncompressedSize) + assertEquals(20184, uncompressedSize) else - assertEquals(20222, uncompressedSize) - assertEquals(1111, compressedSize) + assertEquals(20234, uncompressedSize) + assertEquals(1123, compressedSize) } } diff --git a/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/AMQPTestSerialiationScheme.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/testutils/AMQPTestSerialiationScheme.kt similarity index 87% rename from node/src/test/kotlin/net/corda/node/internal/serialization/testutils/AMQPTestSerialiationScheme.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/testutils/AMQPTestSerialiationScheme.kt index 7234380773..5656ffff44 100644 --- a/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/AMQPTestSerialiationScheme.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/testutils/AMQPTestSerialiationScheme.kt @@ -1,16 +1,16 @@ -package net.corda.node.internal.serialization.testutils +package net.corda.nodeapi.internal.serialization.testutils -import net.corda.client.rpc.internal.serialization.amqp.RpcClientObservableDeSerializer +import net.corda.nodeapi.internal.rpc.client.RpcClientObservableDeSerializer import net.corda.core.context.Trace import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationWhitelist -import net.corda.node.serialization.amqp.RpcServerObservableSerializer +import net.corda.nodeapi.internal.serialization.amqp.RpcServerObservableSerializer import net.corda.nodeapi.RPCApi import net.corda.serialization.internal.CordaSerializationMagic import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.amqp.* -import net.corda.client.rpc.internal.ObservableContext as ClientObservableContext +import net.corda.nodeapi.internal.rpc.client.ObservableContext as ClientObservableContext /** * Special serialization context for the round trip tests that allows for both server and client RPC diff --git a/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestObservableContext.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/testutils/TestObservableContext.kt similarity index 77% rename from node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestObservableContext.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/testutils/TestObservableContext.kt index d8282863ea..e44e15cdd0 100644 --- a/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestObservableContext.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/testutils/TestObservableContext.kt @@ -1,10 +1,10 @@ -package net.corda.node.internal.serialization.testutils +package net.corda.nodeapi.internal.serialization.testutils import com.github.benmanes.caffeine.cache.Cache import net.corda.core.context.Trace -import net.corda.node.services.rpc.ObservableContextInterface -import net.corda.node.services.rpc.ObservableSubscription +import net.corda.nodeapi.internal.rpc.ObservableContextInterface import net.corda.nodeapi.RPCApi +import net.corda.nodeapi.internal.rpc.ObservableSubscription import org.apache.activemq.artemis.api.core.SimpleString import java.util.concurrent.ConcurrentHashMap diff --git a/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestSerializationContext.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/testutils/TestSerializationContext.kt similarity index 92% rename from node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestSerializationContext.kt rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/testutils/TestSerializationContext.kt index c80e176e56..27690cf76e 100644 --- a/node/src/test/kotlin/net/corda/node/internal/serialization/testutils/TestSerializationContext.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/testutils/TestSerializationContext.kt @@ -1,4 +1,4 @@ -package net.corda.node.internal.serialization.testutils +package net.corda.nodeapi.internal.serialization.testutils import net.corda.core.serialization.SerializationContext import net.corda.serialization.internal.AllWhitelist diff --git a/node/build.gradle b/node/build.gradle index 542fd349e4..cfc7e38706 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -109,10 +109,6 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - // Kryo: object graph serialization. - compile "com.esotericsoftware:kryo:4.0.2" - compile "de.javakaffee:kryo-serializers:0.43" - compile "com.google.guava:guava:$guava_version" // For caches rather than guava diff --git a/node/src/integration-test-slow/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt b/node/src/integration-test-slow/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt index b2e24591d7..889a4e880e 100644 --- a/node/src/integration-test-slow/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt +++ b/node/src/integration-test-slow/kotlin/net/corda/node/utilities/registration/NodeRegistrationTest.kt @@ -16,7 +16,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.internal.DEV_ROOT_CA +import net.corda.coretesting.internal.DEV_ROOT_CA import net.corda.testing.node.NotarySpec import net.corda.testing.node.internal.SharedCompatibilityZoneParams import net.corda.testing.node.internal.internalDriver diff --git a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt index 711d34a7f9..62aae2e54a 100644 --- a/node/src/integration-test/kotlin/net/corda/node/BootTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/BootTests.kt @@ -18,7 +18,7 @@ import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeParameters import net.corda.testing.driver.driver -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.testing.node.User import net.corda.testing.node.internal.enclosedCordapp import net.corda.testing.node.internal.startNode diff --git a/node/src/integration-test/kotlin/net/corda/node/CordappConstraintsTests.kt b/node/src/integration-test/kotlin/net/corda/node/CordappConstraintsTests.kt index 44205c956b..da3b020143 100644 --- a/node/src/integration-test/kotlin/net/corda/node/CordappConstraintsTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/CordappConstraintsTests.kt @@ -13,6 +13,7 @@ import net.corda.core.node.services.Vault import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.getOrThrow +import net.corda.coretesting.internal.DEV_ROOT_CA import net.corda.finance.DOLLARS import net.corda.finance.contracts.asset.Cash import net.corda.finance.flows.CashIssueFlow @@ -26,7 +27,6 @@ import net.corda.testing.core.* import net.corda.testing.core.internal.JarSignatureTestUtils.generateKey import net.corda.testing.core.internal.SelfCleaningDir import net.corda.testing.driver.* -import net.corda.testing.internal.DEV_ROOT_CA import net.corda.testing.node.NotarySpec import net.corda.testing.node.User import net.corda.testing.node.internal.cordappWithPackages diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt index 43640b5860..dad3fdc467 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeKeystoreCheckTest.kt @@ -9,7 +9,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.testing.core.ALICE_NAME import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import javax.security.auth.x500.X500Principal diff --git a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt index 0dd0ebc872..45e20f37ab 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodePerformanceTests.kt @@ -19,7 +19,7 @@ import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.InProcess import net.corda.testing.driver.driver import net.corda.testing.driver.internal.internalServices -import net.corda.testing.internal.performance.div +import net.corda.coretesting.internal.performance.div import net.corda.testing.node.NotarySpec import net.corda.testing.node.User import net.corda.testing.node.internal.FINANCE_CORDAPPS diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt index 0bcabb6ad4..221d11cd9a 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/AMQPBridgeTest.kt @@ -20,8 +20,8 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.core.TestIdentity import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.rigorousMock +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import org.apache.activemq.artemis.api.core.Message.HDR_DUPLICATE_DETECTION_ID import org.apache.activemq.artemis.api.core.RoutingType import org.apache.activemq.artemis.api.core.SimpleString 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 9512674d01..1ab13fc6d7 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 @@ -25,10 +25,10 @@ import net.corda.testing.core.BOB_NAME import net.corda.testing.core.CHARLIE_NAME import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.internal.DEV_INTERMEDIATE_CA -import net.corda.testing.internal.DEV_ROOT_CA -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.DEV_INTERMEDIATE_CA +import net.corda.coretesting.internal.DEV_ROOT_CA +import net.corda.coretesting.internal.rigorousMock +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import org.assertj.core.api.Assertions.assertThatIllegalArgumentException import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x509.* diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt index 0f08714025..d3515e56bd 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/ProtonWrapperTests.kt @@ -30,8 +30,8 @@ import net.corda.testing.core.CHARLIE_NAME import net.corda.testing.core.MAX_MESSAGE_SIZE import net.corda.testing.driver.internal.incrementalPortAllocation import net.corda.testing.internal.createDevIntermediateCaCertPath -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.rigorousMock +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import org.apache.activemq.artemis.api.core.RoutingType import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Assert.assertArrayEquals diff --git a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt index 5990fbecd6..ff71513398 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTest.kt @@ -22,8 +22,8 @@ import net.corda.testing.driver.internal.incrementalPortAllocation import net.corda.testing.internal.LogHelper import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.configureDatabase -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.rigorousMock +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.internal.MOCK_VERSION_INFO import org.apache.activemq.artemis.api.core.ActiveMQConnectionTimedOutException diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt index 6f95a7964c..261532c1e3 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityAsNodeTest.kt @@ -13,7 +13,7 @@ import net.corda.nodeapi.internal.DEV_ROOT_CA import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.loadDevCaTrustStore -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 4914df4833..eee9e063f0 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -8,7 +8,7 @@ import com.jcabi.manifests.Manifests import com.palominolabs.metrics.newrelic.AllEnabledMetricAttributeFilter import com.palominolabs.metrics.newrelic.NewRelicReporter import io.netty.util.NettyRuntime -import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme +import net.corda.nodeapi.internal.rpc.client.AMQPClientSerializationScheme import net.corda.cliutils.ShellConstants import net.corda.core.concurrent.CordaFuture import net.corda.core.flows.FlowLogic @@ -44,9 +44,9 @@ import net.corda.node.internal.artemis.BrokerAddresses import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.security.RPCSecurityManagerWithAdditionalUser -import net.corda.node.serialization.amqp.AMQPServerSerializationScheme -import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT -import net.corda.node.serialization.kryo.KryoCheckpointSerializer +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.KRYO_CHECKPOINT_CONTEXT +import net.corda.nodeapi.internal.serialization.kryo.KryoCheckpointSerializer import net.corda.node.services.Permissions import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.ServiceHubInternal diff --git a/node/src/main/kotlin/net/corda/node/services/rpc/RPCServer.kt b/node/src/main/kotlin/net/corda/node/services/rpc/RPCServer.kt index 35e093098e..9a1e1474d0 100644 --- a/node/src/main/kotlin/net/corda/node/services/rpc/RPCServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/rpc/RPCServer.kt @@ -21,10 +21,14 @@ import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.SerializationDefaults.RPC_SERVER_CONTEXT import net.corda.core.serialization.deserialize -import net.corda.core.utilities.* +import net.corda.core.utilities.Try +import net.corda.core.utilities.contextLogger +import net.corda.core.utilities.days +import net.corda.core.utilities.debug +import net.corda.core.utilities.seconds +import net.corda.core.utilities.trace import net.corda.node.internal.security.AuthorizingSubject import net.corda.node.internal.security.RPCSecurityManager -import net.corda.node.serialization.amqp.RpcServerObservableSerializer import net.corda.node.services.logging.pushToLoggingContext import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi.CLASS_METHOD_DIVIDER @@ -34,20 +38,32 @@ import net.corda.nodeapi.internal.DeduplicationChecker import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.contextDatabase import net.corda.nodeapi.internal.persistence.contextDatabaseOrNull +import net.corda.nodeapi.internal.rpc.ObservableContextInterface +import net.corda.nodeapi.internal.rpc.ObservableSubscription +import net.corda.nodeapi.internal.serialization.amqp.RpcServerObservableSerializer import org.apache.activemq.artemis.api.core.Message import org.apache.activemq.artemis.api.core.SimpleString -import org.apache.activemq.artemis.api.core.client.* import org.apache.activemq.artemis.api.core.client.ActiveMQClient.DEFAULT_ACK_BATCH_SIZE +import org.apache.activemq.artemis.api.core.client.ClientConsumer +import org.apache.activemq.artemis.api.core.client.ClientMessage +import org.apache.activemq.artemis.api.core.client.ClientProducer +import org.apache.activemq.artemis.api.core.client.ClientSession +import org.apache.activemq.artemis.api.core.client.ClientSessionFactory +import org.apache.activemq.artemis.api.core.client.ServerLocator import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl import org.apache.activemq.artemis.api.core.management.CoreNotificationType import org.apache.activemq.artemis.api.core.management.ManagementHelper import org.slf4j.MDC -import rx.Subscription import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method import java.time.Duration import java.util.* -import java.util.concurrent.* +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.Executors +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit import kotlin.concurrent.thread private typealias ObservableSubscriptionMap = Cache @@ -546,9 +562,3 @@ internal fun context(): InvocationContext = rpcContext().invocation * The [RpcAuthContext] includes permissions. */ fun rpcContext(): RpcAuthContext = CURRENT_RPC_CONTEXT.get() - -class ObservableSubscription( - val subscription: Subscription -) - - diff --git a/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt b/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt index 36c24b700b..3ae5c937a2 100644 --- a/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/NodeTest.kt @@ -21,8 +21,8 @@ import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.configureDatabase -import net.corda.testing.internal.createNodeInfoAndSigned -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.createNodeInfoAndSigned +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.apache.commons.lang3.SystemUtils import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt index 200b60742a..18fa7c40c4 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeFlowTests.kt @@ -25,6 +25,7 @@ import net.corda.core.utilities.days import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.toNonEmptySet import net.corda.core.utilities.unwrap +import net.corda.coretesting.internal.TEST_TX_TIME import net.corda.finance.DOLLARS import net.corda.finance.`issued by` import net.corda.finance.contracts.CommercialPaper @@ -41,7 +42,6 @@ import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter import net.corda.testing.internal.LogHelper -import net.corda.testing.internal.TEST_TX_TIME import net.corda.testing.internal.vault.VaultFiller import net.corda.testing.node.internal.* import net.corda.testing.node.ledger @@ -706,10 +706,10 @@ class TwoPartyTradeFlowTests(private val anonymous: Boolean) { notary: Party): Pair, List> { val ap = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { output(CommercialPaper.CP_PROGRAM_ID, "alice's paper", notary = notary, - contractState = CommercialPaper.State(issuer, owner, amount, TEST_TX_TIME + 7.days)) + contractState = CommercialPaper.State(issuer, owner, amount, net.corda.coretesting.internal.TEST_TX_TIME + 7.days)) command(issuer.party.owningKey, CommercialPaper.Commands.Issue()) if (!withError) - timeWindow(time = TEST_TX_TIME) + timeWindow(time = net.corda.coretesting.internal.TEST_TX_TIME) if (attachmentID != null) attachment(attachmentID) if (withError) { diff --git a/node/src/test/kotlin/net/corda/node/services/attachments/AttachmentTrustCalculatorTest.kt b/node/src/test/kotlin/net/corda/node/services/attachments/AttachmentTrustCalculatorTest.kt index be6cddb7ac..315e401f2c 100644 --- a/node/src/test/kotlin/net/corda/node/services/attachments/AttachmentTrustCalculatorTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/attachments/AttachmentTrustCalculatorTest.kt @@ -17,7 +17,7 @@ import net.corda.testing.core.internal.JarSignatureTestUtils.signJar import net.corda.testing.core.internal.SelfCleaningDir import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.configureDatabase -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.MockServices import org.assertj.core.api.Assertions.assertThat import org.junit.After diff --git a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt index 415400d0b4..4686238197 100644 --- a/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/events/NodeSchedulerServiceTest.kt @@ -17,9 +17,9 @@ import net.corda.node.services.statemachine.ExternalEvent import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.internal.configureDatabase -import net.corda.testing.internal.doLookup -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.spectator +import net.corda.coretesting.internal.doLookup +import net.corda.coretesting.internal.rigorousMock +import net.corda.coretesting.internal.spectator import net.corda.testing.node.MockServices import net.corda.testing.node.TestClock import org.junit.* diff --git a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt index c154620673..37df2cbe30 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/InMemoryIdentityServiceTests.kt @@ -11,8 +11,8 @@ import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.testing.core.* -import net.corda.testing.internal.DEV_INTERMEDIATE_CA -import net.corda.testing.internal.DEV_ROOT_CA +import net.corda.coretesting.internal.DEV_INTERMEDIATE_CA +import net.corda.coretesting.internal.DEV_ROOT_CA import org.junit.Rule import org.junit.Test import kotlin.test.assertEquals diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt index 71b236aaa9..333e356b1a 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt @@ -7,16 +7,20 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.services.UnknownAnonymousPartyException -import net.corda.node.services.persistence.PublicKeyToOwningIdentityCacheImpl import net.corda.core.serialization.serialize +import net.corda.coretesting.internal.DEV_INTERMEDIATE_CA +import net.corda.coretesting.internal.DEV_ROOT_CA +import net.corda.node.services.persistence.PublicKeyToOwningIdentityCacheImpl import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig -import net.corda.testing.core.* -import net.corda.testing.internal.DEV_INTERMEDIATE_CA -import net.corda.testing.internal.DEV_ROOT_CA +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.BOB_NAME +import net.corda.testing.core.SerializationEnvironmentRule +import net.corda.testing.core.TestIdentity +import net.corda.testing.core.getTestPartyAndCertificate import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.configureDatabase import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties @@ -26,7 +30,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertNull diff --git a/node/src/test/kotlin/net/corda/node/services/network/DBNetworkParametersStorageTest.kt b/node/src/test/kotlin/net/corda/node/services/network/DBNetworkParametersStorageTest.kt index 4d8736133e..0bb069a33f 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/DBNetworkParametersStorageTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/DBNetworkParametersStorageTest.kt @@ -15,8 +15,8 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.internal.DEV_INTERMEDIATE_CA -import net.corda.testing.internal.DEV_ROOT_CA +import net.corda.coretesting.internal.DEV_INTERMEDIATE_CA +import net.corda.coretesting.internal.DEV_ROOT_CA import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.configureDatabase import net.corda.testing.node.MockServices diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt index eb2f06c006..9fa13bf67e 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapClientTest.kt @@ -11,10 +11,10 @@ import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.internal.DEV_ROOT_CA -import net.corda.testing.internal.TestNodeInfoBuilder -import net.corda.testing.internal.createNodeInfoAndSigned -import net.corda.testing.internal.signWith +import net.corda.coretesting.internal.DEV_ROOT_CA +import net.corda.coretesting.internal.TestNodeInfoBuilder +import net.corda.coretesting.internal.createNodeInfoAndSigned +import net.corda.coretesting.internal.signWith import net.corda.testing.node.internal.network.NetworkMapServer import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt index a418b89cc9..dd47140877 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt @@ -17,6 +17,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.services.AttachmentId import net.corda.core.serialization.serialize import net.corda.core.utilities.millis +import net.corda.coretesting.internal.DEV_ROOT_CA import net.corda.node.VersionInfo import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.services.config.NetworkParameterAcceptanceSettings @@ -29,11 +30,9 @@ import net.corda.nodeapi.internal.network.SignedNetworkParameters import net.corda.nodeapi.internal.network.verifiedNetworkParametersCert import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.* -import net.corda.testing.internal.DEV_ROOT_CA -import net.corda.testing.internal.TestNodeInfoBuilder -import net.corda.testing.internal.createNodeInfoAndSigned +import net.corda.coretesting.internal.TestNodeInfoBuilder +import net.corda.coretesting.internal.createNodeInfoAndSigned import net.corda.testing.node.internal.MockKeyManagementService -import net.corda.testing.node.internal.MockPublicKeyToOwningIdentityCache import net.corda.testing.node.internal.network.NetworkMapServer import net.corda.testing.node.makeTestIdentityService import org.assertj.core.api.Assertions.assertThat diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt index 6a856d3246..f44331d296 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt @@ -11,7 +11,7 @@ import net.corda.node.internal.NetworkParametersReader import net.corda.nodeapi.internal.network.* import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.internal.DEV_ROOT_CA +import net.corda.coretesting.internal.DEV_ROOT_CA import net.corda.testing.node.internal.network.NetworkMapServer import org.assertj.core.api.Assertions.assertThat import org.junit.After diff --git a/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt index 99f08b5a17..6ec93664a3 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt @@ -11,9 +11,8 @@ import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.network.NodeInfoFilesCopier import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.internal.createNodeInfoAndSigned +import net.corda.coretesting.internal.createNodeInfoAndSigned import net.corda.testing.node.internal.MockKeyManagementService -import net.corda.testing.node.internal.MockPublicKeyToOwningIdentityCache import net.corda.testing.node.makeTestIdentityService import org.assertj.core.api.Assertions.assertThat import org.junit.Before diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt index aa0d828a7d..e7af37bb6f 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt @@ -24,7 +24,6 @@ import net.corda.nodeapi.exceptions.DuplicateAttachmentException import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.common.internal.testNetworkParameters -import net.corda.testing.core.internal.ContractJarTestUtils import net.corda.testing.core.internal.ContractJarTestUtils.makeTestContractJar import net.corda.testing.core.internal.ContractJarTestUtils.makeTestJar import net.corda.testing.core.internal.ContractJarTestUtils.makeTestSignedContractJar @@ -34,7 +33,7 @@ import net.corda.testing.core.internal.SelfCleaningDir import net.corda.testing.internal.LogHelper import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.configureDatabase -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.startFlow diff --git a/node/src/test/kotlin/net/corda/node/services/schema/PersistentStateServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/PersistentStateServiceTests.kt index 0b00c2adee..9f674c6233 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/PersistentStateServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/PersistentStateServiceTests.kt @@ -15,7 +15,7 @@ import net.corda.testing.contracts.DummyContract import net.corda.testing.core.TestIdentity import net.corda.testing.internal.LogHelper import net.corda.testing.internal.configureDatabase -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.junit.After import org.junit.Before diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index 1166a1a500..ff362766b4 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -15,6 +15,7 @@ import net.corda.core.node.services.vault.QueryCriteria.* import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.* +import net.corda.coretesting.internal.TEST_TX_TIME import net.corda.finance.* import net.corda.finance.contracts.CommercialPaper import net.corda.finance.contracts.Commodity @@ -31,7 +32,6 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseTransaction import net.corda.testing.core.* -import net.corda.testing.internal.TEST_TX_TIME import net.corda.testing.internal.chooseIdentity import net.corda.testing.internal.configureDatabase import net.corda.testing.internal.vault.* diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt index 959a407429..7e771e9904 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultSoftLockManagerTest.kt @@ -28,7 +28,7 @@ import net.corda.nodeapi.internal.cordapp.CordappLoader import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.core.singleIdentity import net.corda.testing.flows.registerCoreFlowFactory -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.enclosedCordapp import net.corda.testing.node.internal.startFlow diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationServiceTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationServiceTest.kt index bbc5751118..52db6d3e84 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationServiceTest.kt @@ -6,7 +6,7 @@ import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.whenever import net.corda.node.VersionInfo import net.corda.node.services.config.NetworkServicesConfig -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.junit.Test import java.io.InputStream diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index caeacc5b71..dde5082f6b 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -26,8 +26,8 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_KEY_AL import net.corda.nodeapi.internal.crypto.X509Utilities.createSelfSignedCACertificate import net.corda.testing.core.ALICE_NAME import net.corda.testing.internal.createDevIntermediateCaCertPath -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.rigorousMock +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import org.assertj.core.api.Assertions.* import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralSubtree diff --git a/samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index bb82a178eb..1b9c75d25b 100644 --- a/samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/cordapp/workflows-irs/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -14,7 +14,7 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.core.* import net.corda.testing.internal.configureDatabase -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.createMockCordaService diff --git a/samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/flow/TransactionGraphSearchTests.kt b/samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/flow/TransactionGraphSearchTests.kt index d17bdbfc49..e59a56eb59 100644 --- a/samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/flow/TransactionGraphSearchTests.kt +++ b/samples/trader-demo/workflows-trader/src/test/kotlin/net/corda/traderdemo/flow/TransactionGraphSearchTests.kt @@ -13,7 +13,7 @@ import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.TestIdentity import net.corda.testing.core.dummyCommand -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.MockServices import net.corda.testing.node.internal.MockTransactionStorage import org.junit.Rule diff --git a/serialization-tests/src/test/java/net/corda/serialization/internal/LambdaCheckpointSerializationTest.java b/serialization-tests/src/test/java/net/corda/serialization/internal/LambdaCheckpointSerializationTest.java index 0c9c9f2b5a..5b844d5b86 100644 --- a/serialization-tests/src/test/java/net/corda/serialization/internal/LambdaCheckpointSerializationTest.java +++ b/serialization-tests/src/test/java/net/corda/serialization/internal/LambdaCheckpointSerializationTest.java @@ -3,7 +3,7 @@ package net.corda.serialization.internal; import net.corda.core.serialization.*; import net.corda.core.serialization.internal.CheckpointSerializationContext; import net.corda.core.serialization.internal.CheckpointSerializer; -import net.corda.node.serialization.kryo.CordaClosureSerializer; +import net.corda.nodeapi.internal.serialization.kryo.CordaClosureSerializer; import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule; import org.junit.Before; import org.junit.Rule; diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/ContractAttachmentSerializerTest.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/ContractAttachmentSerializerTest.kt index fd854c14a0..ca341f68dd 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/ContractAttachmentSerializerTest.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/ContractAttachmentSerializerTest.kt @@ -8,7 +8,7 @@ import net.corda.core.serialization.internal.checkpointDeserialize import net.corda.core.serialization.internal.checkpointSerialize import net.corda.testing.contracts.DummyContract import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.node.MockServices import org.apache.commons.lang3.ArrayUtils.EMPTY_BYTE_ARRAY import org.assertj.core.api.Assertions.assertThat diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt index 5208324e7d..1fc4023ff9 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/CordaClassResolverTests.kt @@ -16,12 +16,12 @@ import net.corda.core.node.services.AttachmentStorage import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.internal.AttachmentsClassLoader import net.corda.core.serialization.internal.CheckpointSerializationContext -import net.corda.node.serialization.kryo.CordaClassResolver -import net.corda.node.serialization.kryo.CordaKryo +import net.corda.nodeapi.internal.serialization.kryo.CordaClassResolver +import net.corda.nodeapi.internal.serialization.kryo.CordaKryo import net.corda.node.services.attachments.NodeAttachmentTrustCalculator import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.internal.TestingNamedCacheFactory -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.internal.services.InternalMockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage import org.junit.Rule diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/ListsSerializationTest.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/ListsSerializationTest.kt index 4dc26f1cb7..75762aaa36 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/ListsSerializationTest.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/ListsSerializationTest.kt @@ -3,7 +3,7 @@ package net.corda.serialization.internal import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.util.DefaultClassResolver import net.corda.core.serialization.* -import net.corda.node.serialization.kryo.kryoMagic +import net.corda.nodeapi.internal.serialization.kryo.kryoMagic import net.corda.node.services.statemachine.DataSessionMessage import net.corda.serialization.internal.amqp.DeserializationInput import net.corda.serialization.internal.amqp.Envelope diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/MapsSerializationTest.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/MapsSerializationTest.kt index 5e5ee8a606..24fd901408 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/MapsSerializationTest.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/MapsSerializationTest.kt @@ -6,7 +6,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.node.serialization.kryo.kryoMagic +import net.corda.nodeapi.internal.serialization.kryo.kryoMagic import net.corda.node.services.statemachine.DataSessionMessage import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.amqpSpecific diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt index fbe7537199..2fc36c8976 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SerializationTokenTest.kt @@ -8,11 +8,11 @@ import net.corda.core.serialization.internal.CheckpointSerializationContext import net.corda.core.serialization.internal.checkpointDeserialize import net.corda.core.serialization.internal.checkpointSerialize import net.corda.core.utilities.OpaqueBytes -import net.corda.node.serialization.kryo.CordaClassResolver -import net.corda.node.serialization.kryo.CordaKryo -import net.corda.node.serialization.kryo.DefaultKryoCustomizer -import net.corda.node.serialization.kryo.kryoMagic -import net.corda.testing.internal.rigorousMock +import net.corda.nodeapi.internal.serialization.kryo.CordaClassResolver +import net.corda.nodeapi.internal.serialization.kryo.CordaKryo +import net.corda.nodeapi.internal.serialization.kryo.DefaultKryoCustomizer +import net.corda.nodeapi.internal.serialization.kryo.kryoMagic +import net.corda.coretesting.internal.rigorousMock import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule import org.assertj.core.api.Assertions.assertThat import org.junit.Before diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt index b0f9bac9d9..159e96fc1d 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/SetsSerializationTest.kt @@ -4,7 +4,7 @@ import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.util.DefaultClassResolver import net.corda.core.serialization.deserialize import net.corda.core.serialization.serialize -import net.corda.node.serialization.kryo.kryoMagic +import net.corda.nodeapi.internal.serialization.kryo.kryoMagic import net.corda.node.services.statemachine.DataSessionMessage import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.internal.kryoSpecific diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/AbstractAMQPSerializationSchemeTest.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/AbstractAMQPSerializationSchemeTest.kt index 79438f3b60..095cf8785e 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/AbstractAMQPSerializationSchemeTest.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/AbstractAMQPSerializationSchemeTest.kt @@ -8,7 +8,7 @@ import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.CordaSerializationMagic import net.corda.serialization.internal.SerializationContextImpl import net.corda.serialization.internal.amqp.testutils.serializationProperties -import net.corda.testing.internal.createTestSerializationEnv +import net.corda.coretesting.internal.createTestSerializationEnv import org.hamcrest.CoreMatchers import org.hamcrest.CoreMatchers.`is` import org.hamcrest.Matchers diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeQueryableStateTest.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeQueryableStateTest.kt index c37ebc6e1b..1aaeebd7af 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeQueryableStateTest.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/DeserializeQueryableStateTest.kt @@ -1,12 +1,12 @@ package net.corda.serialization.internal.amqp -import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme +import net.corda.nodeapi.internal.rpc.client.AMQPClientSerializationScheme import net.corda.core.identity.AbstractParty import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState import net.corda.core.serialization.SerializedBytes -import net.corda.node.serialization.amqp.AMQPServerSerializationScheme +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT import net.corda.serialization.internal.AMQP_RPC_SERVER_CONTEXT import net.corda.serialization.internal.AllWhitelist diff --git a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt index dade6b0f50..9dd1a398e8 100644 --- a/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt +++ b/serialization-tests/src/test/kotlin/net/corda/serialization/internal/amqp/SerializationOutputTests.kt @@ -18,7 +18,7 @@ import net.corda.core.internal.AbstractAttachment import net.corda.core.serialization.* import net.corda.core.transactions.LedgerTransaction import net.corda.core.utilities.OpaqueBytes -import net.corda.node.serialization.amqp.AMQPServerSerializationScheme +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme import net.corda.nodeapi.internal.crypto.ContentSignerBuilder import net.corda.serialization.internal.* import net.corda.serialization.internal.amqp.testutils.* @@ -27,7 +27,7 @@ import net.corda.testing.contracts.DummyContract import net.corda.testing.core.BOB_NAME import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.TestIdentity -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import org.apache.activemq.artemis.api.core.SimpleString import org.apache.qpid.proton.amqp.* import org.apache.qpid.proton.codec.DecoderImpl diff --git a/settings.gradle b/settings.gradle index 78f158b144..8a636cdf94 100644 --- a/settings.gradle +++ b/settings.gradle @@ -42,8 +42,9 @@ include 'test-utils' include 'test-db' include 'smoke-test-utils' include 'node-driver' +include 'core-test-utils' // Avoid making 'testing' a project, and allow build.gradle files to refer to these by their simple names: -['test-common', 'test-utils', 'test-cli', 'test-db', 'smoke-test-utils', 'node-driver'].each { +['test-common', 'core-test-utils', 'test-utils', 'test-cli', 'test-db', 'smoke-test-utils', 'node-driver'].each { project(":$it").projectDir = new File("$settingsDir/testing/$it") } include 'tools:explorer' diff --git a/testing/core-test-utils/build.gradle b/testing/core-test-utils/build.gradle new file mode 100644 index 0000000000..53954ea735 --- /dev/null +++ b/testing/core-test-utils/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'net.corda.plugins.publish-utils' + id 'net.corda.plugins.api-scanner' + id 'com.jfrog.artifactory' +} + +description 'Core test types and helpers for testing Corda' + +dependencies { + implementation project(':core') + implementation project(':test-common') + compile "org.jetbrains.kotlin:kotlin-test" +} + +jar { + baseName 'corda-core-test-utils' +} + +publish { + name jar.baseName +} \ No newline at end of file diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/InternalSerializationTestHelpers.kt similarity index 73% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/InternalSerializationTestHelpers.kt index a9572f62fd..6e2253814b 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalSerializationTestHelpers.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/InternalSerializationTestHelpers.kt @@ -1,14 +1,18 @@ -package net.corda.testing.internal +package net.corda.coretesting.internal -import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme +import net.corda.nodeapi.internal.rpc.client.AMQPClientSerializationScheme import net.corda.core.internal.createInstancesOfClassesImplementing import net.corda.core.serialization.SerializationCustomSerializer import net.corda.core.serialization.SerializationWhitelist import net.corda.core.serialization.internal.SerializationEnvironment -import net.corda.node.serialization.amqp.AMQPServerSerializationScheme -import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT -import net.corda.node.serialization.kryo.KryoCheckpointSerializer -import net.corda.serialization.internal.* +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.KRYO_CHECKPOINT_CONTEXT +import net.corda.nodeapi.internal.serialization.kryo.KryoCheckpointSerializer +import net.corda.serialization.internal.AMQP_P2P_CONTEXT +import net.corda.serialization.internal.AMQP_RPC_CLIENT_CONTEXT +import net.corda.serialization.internal.AMQP_RPC_SERVER_CONTEXT +import net.corda.serialization.internal.AMQP_STORAGE_CONTEXT +import net.corda.serialization.internal.SerializationFactoryImpl import net.corda.testing.common.internal.asContextEnv import java.util.ServiceLoader import java.util.concurrent.ConcurrentHashMap diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestConstants.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/InternalTestConstants.kt similarity index 91% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestConstants.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/InternalTestConstants.kt index 664cbce7ab..1d3a7c6134 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestConstants.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/InternalTestConstants.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.coretesting.internal import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import java.time.Instant diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/NettyTestClient.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/NettyTestClient.kt similarity index 98% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/NettyTestClient.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/NettyTestClient.kt index 532b7d1c94..185a289472 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/NettyTestClient.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/NettyTestClient.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.coretesting.internal import io.netty.bootstrap.Bootstrap import io.netty.channel.ChannelFuture diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/NettyTestHandler.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/NettyTestHandler.kt similarity index 98% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/NettyTestHandler.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/NettyTestHandler.kt index b62ed09805..9086938325 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/NettyTestHandler.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/NettyTestHandler.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.coretesting.internal import io.netty.buffer.ByteBuf import io.netty.buffer.Unpooled diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/NettyTestServer.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/NettyTestServer.kt similarity index 98% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/NettyTestServer.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/NettyTestServer.kt index a9effdd279..8fa9d23057 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/NettyTestServer.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/NettyTestServer.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.coretesting.internal import io.netty.bootstrap.ServerBootstrap import io.netty.channel.ChannelFuture diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/RigorousMock.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/RigorousMock.kt similarity index 98% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/RigorousMock.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/RigorousMock.kt index 72f4942635..fcd9b085aa 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/RigorousMock.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/RigorousMock.kt @@ -1,4 +1,5 @@ -package net.corda.testing.internal +@file: Suppress("MatchingDeclarationName") +package net.corda.coretesting.internal import com.nhaarman.mockito_kotlin.doAnswer import net.corda.core.utilities.contextLogger diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/TestNodeInfoBuilder.kt similarity index 96% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/TestNodeInfoBuilder.kt index e7f16ad0ab..4b0fed9ba0 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestNodeInfoBuilder.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/TestNodeInfoBuilder.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.coretesting.internal import net.corda.core.crypto.Crypto import net.corda.core.crypto.sign @@ -7,6 +7,8 @@ import net.corda.core.identity.PartyAndCertificate import net.corda.core.node.NodeInfo import net.corda.core.serialization.serialize import net.corda.core.utilities.NetworkHostAndPort +import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA +import net.corda.nodeapi.internal.DEV_ROOT_CA import net.corda.nodeapi.internal.NodeInfoAndSigned import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.createDevNodeCa diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestThreadFactory.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/TestThreadFactory.kt similarity index 94% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestThreadFactory.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/TestThreadFactory.kt index 0d25111ca4..d64a5f8d3b 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/TestThreadFactory.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/TestThreadFactory.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.coretesting.internal import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ThreadFactory diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/Matchers.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/Matchers.kt similarity index 98% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/Matchers.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/Matchers.kt index e65c1b1cdb..08e3d7c891 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/Matchers.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/Matchers.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal.matchers +package net.corda.coretesting.internal.matchers import com.natpryce.hamkrest.* diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/flow/FlowMatchers.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/flow/FlowMatchers.kt similarity index 69% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/flow/FlowMatchers.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/flow/FlowMatchers.kt index 2e66073fc9..42b27f10c9 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/flow/FlowMatchers.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/flow/FlowMatchers.kt @@ -1,14 +1,14 @@ -package net.corda.testing.internal.matchers.flow +package net.corda.coretesting.internal.matchers.flow import com.natpryce.hamkrest.Matcher import com.natpryce.hamkrest.equalTo import net.corda.core.internal.FlowStateMachine -import net.corda.testing.internal.matchers.* +import net.corda.coretesting.internal.matchers.* /** * Matches a Flow that succeeds with a result matched by the given matcher */ -fun willReturn(): Matcher> = net.corda.testing.internal.matchers.future.willReturn() +fun willReturn(): Matcher> = net.corda.coretesting.internal.matchers.future.willReturn() .extrude(FlowStateMachine::resultFuture) .redescribe { "is a flow that will return" } @@ -17,7 +17,7 @@ fun willReturn(expected: T): Matcher> = willReturn(equal /** * Matches a Flow that succeeds with a result matched by the given matcher */ -fun willReturn(successMatcher: Matcher) = net.corda.testing.internal.matchers.future.willReturn(successMatcher) +fun willReturn(successMatcher: Matcher) = net.corda.coretesting.internal.matchers.future.willReturn(successMatcher) .extrude(FlowStateMachine::resultFuture) .redescribe { "is a flow that will return with a value that ${successMatcher.description}" } @@ -25,7 +25,7 @@ fun willReturn(successMatcher: Matcher) = net.corda.testing.internal.matc * Matches a Flow that fails, with an exception matched by the given matcher. */ inline fun willThrow(failureMatcher: Matcher) = - net.corda.testing.internal.matchers.future.willThrow(failureMatcher) + net.corda.coretesting.internal.matchers.future.willThrow(failureMatcher) .extrude(FlowStateMachine<*>::resultFuture) .redescribe { "is a flow that will fail, throwing an exception that ${failureMatcher.description}" } @@ -33,6 +33,6 @@ inline fun willThrow(failureMatcher: Matcher) = * Matches a Flow that fails, with an exception of the specified type. */ inline fun willThrow() = - net.corda.testing.internal.matchers.future.willThrow() + net.corda.coretesting.internal.matchers.future.willThrow() .extrude(FlowStateMachine<*>::resultFuture) .redescribe { "is a flow that will fail with an exception of type ${E::class.java.simpleName}" } \ No newline at end of file diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/future/FutureMatchers.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/future/FutureMatchers.kt similarity index 95% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/future/FutureMatchers.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/future/FutureMatchers.kt index e1e42108c8..59d3c7ff0b 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/future/FutureMatchers.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/future/FutureMatchers.kt @@ -1,10 +1,10 @@ -package net.corda.testing.internal.matchers.future +package net.corda.coretesting.internal.matchers.future import com.natpryce.hamkrest.MatchResult import com.natpryce.hamkrest.Matcher import com.natpryce.hamkrest.equalTo import net.corda.core.utilities.getOrThrow -import net.corda.testing.internal.matchers.modifyMismatchDescription +import net.corda.coretesting.internal.matchers.modifyMismatchDescription import java.util.concurrent.Future /** diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/rpc/RpcMatchers.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/rpc/RpcMatchers.kt similarity index 65% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/rpc/RpcMatchers.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/rpc/RpcMatchers.kt index a5fb84997a..a6598ea5dc 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/matchers/rpc/RpcMatchers.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/matchers/rpc/RpcMatchers.kt @@ -1,21 +1,21 @@ -package net.corda.testing.internal.matchers.rpc +package net.corda.coretesting.internal.matchers.rpc import com.natpryce.hamkrest.Matcher import net.corda.core.messaging.FlowHandle -import net.corda.testing.internal.matchers.extrude -import net.corda.testing.internal.matchers.redescribe +import net.corda.coretesting.internal.matchers.extrude +import net.corda.coretesting.internal.matchers.redescribe /** * Matches a flow handle that succeeds with a result matched by the given matcher */ -fun willReturn() = net.corda.testing.internal.matchers.future.willReturn() +fun willReturn() = net.corda.coretesting.internal.matchers.future.willReturn() .extrude(FlowHandle::returnValue) .redescribe { "is an RPG flow handle that will return" } /** * Matches a flow handle that succeeds with a result matched by the given matcher */ -fun willReturn(successMatcher: Matcher) = net.corda.testing.internal.matchers.future.willReturn(successMatcher) +fun willReturn(successMatcher: Matcher) = net.corda.coretesting.internal.matchers.future.willReturn(successMatcher) .extrude(FlowHandle::returnValue) .redescribe { "is an RPG flow handle that will return a value that ${successMatcher.description}" } @@ -23,7 +23,7 @@ fun willReturn(successMatcher: Matcher) = net.corda.testing.internal.matc * Matches a flow handle that fails, with an exception matched by the given matcher. */ inline fun willThrow(failureMatcher: Matcher) = - net.corda.testing.internal.matchers.future.willThrow(failureMatcher) + net.corda.coretesting.internal.matchers.future.willThrow(failureMatcher) .extrude(FlowHandle<*>::returnValue) .redescribe { "is an RPG flow handle that will fail with an exception that ${failureMatcher.description}" } @@ -31,6 +31,6 @@ inline fun willThrow(failureMatcher: Matcher) = * Matches a flow handle that fails, with an exception of the specified type. */ inline fun willThrow() = - net.corda.testing.internal.matchers.future.willThrow() + net.corda.coretesting.internal.matchers.future.willThrow() .extrude(FlowHandle<*>::returnValue) .redescribe { "is an RPG flow handle that will fail with an exception of type ${E::class.java.simpleName}" } \ No newline at end of file diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/performance/Rate.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/performance/Rate.kt similarity index 94% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/performance/Rate.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/performance/Rate.kt index dd79aed436..0f880d962e 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/performance/Rate.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/performance/Rate.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal.performance +package net.corda.coretesting.internal.performance import java.time.Duration import java.time.temporal.ChronoUnit diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/stubs/CertificateStoreStubs.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/stubs/CertificateStoreStubs.kt similarity index 94% rename from testing/test-utils/src/main/kotlin/net/corda/testing/internal/stubs/CertificateStoreStubs.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/stubs/CertificateStoreStubs.kt index e180769418..c23d458a80 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/stubs/CertificateStoreStubs.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/stubs/CertificateStoreStubs.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal.stubs +package net.corda.coretesting.internal.stubs import net.corda.core.internal.div import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_PASS @@ -43,11 +43,13 @@ class CertificateStoreStubs { companion object { + @Suppress("LongParameterList") @JvmStatic fun withCertificatesDirectory(certificatesDirectory: Path, keyStoreFileName: String = KeyStore.DEFAULT_STORE_FILE_NAME, keyStorePassword: String = KeyStore.DEFAULT_STORE_PASSWORD, keyPassword: String = keyStorePassword, - trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD, trustStoreKeyPassword: String = TrustStore.DEFAULT_KEY_PASSWORD, - @Suppress("UNUSED_PARAMETER") useOpenSsl: Boolean = false): MutualSslConfiguration { + trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, + trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD, + trustStoreKeyPassword: String = TrustStore.DEFAULT_KEY_PASSWORD): MutualSslConfiguration { val keyStore = FileBasedCertificateStoreSupplier(certificatesDirectory / keyStoreFileName, keyStorePassword, keyPassword) val trustStore = FileBasedCertificateStoreSupplier(certificatesDirectory / trustStoreFileName, trustStorePassword, trustStoreKeyPassword) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt b/testing/core-test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt similarity index 100% rename from testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContract.kt diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt b/testing/core-test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt similarity index 95% rename from testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt index 331f613f0f..4bd26afe6d 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/testing/contracts/DummyContractV2.kt @@ -29,10 +29,11 @@ class DummyContractV2 : UpgradedContractWithLegacyConstraint("threadPoolExecutor").value = rigorousMock().also { + InVMConnector::class.staticField("threadPoolExecutor").value = rigorousMock() + .also { doAnswer { inVMExecutors.computeIfAbsent(effectiveSerializationEnv) { Executors.newCachedThreadPool(testThreadFactory(true)) // Close enough to what InVMConnector makes normally. diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestConstants.kt b/testing/core-test-utils/src/main/kotlin/net/corda/testing/core/TestConstants.kt similarity index 100% rename from testing/test-utils/src/main/kotlin/net/corda/testing/core/TestConstants.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/testing/core/TestConstants.kt diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt b/testing/core-test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt similarity index 98% rename from testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt index 54b6e7e605..b26e5cbd3f 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/testing/core/TestUtils.kt @@ -16,8 +16,8 @@ import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509Utilities -import net.corda.testing.internal.DEV_INTERMEDIATE_CA -import net.corda.testing.internal.DEV_ROOT_CA +import net.corda.coretesting.internal.DEV_INTERMEDIATE_CA +import net.corda.coretesting.internal.DEV_ROOT_CA import java.math.BigInteger import java.security.KeyPair import java.security.PublicKey diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/CheckpointSerializationTestHelpers.kt b/testing/core-test-utils/src/main/kotlin/net/corda/testing/core/internal/CheckpointSerializationTestHelpers.kt similarity index 89% rename from testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/CheckpointSerializationTestHelpers.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/testing/core/internal/CheckpointSerializationTestHelpers.kt index c1ad09e30e..78157094e8 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/CheckpointSerializationTestHelpers.kt +++ b/testing/core-test-utils/src/main/kotlin/net/corda/testing/core/internal/CheckpointSerializationTestHelpers.kt @@ -8,10 +8,10 @@ import net.corda.core.serialization.internal.SerializationEnvironment import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.testing.common.internal.asContextEnv import net.corda.testing.core.SerializationEnvironmentRule -import net.corda.testing.internal.createTestSerializationEnv -import net.corda.testing.internal.inVMExecutors -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.testThreadFactory +import net.corda.coretesting.internal.createTestSerializationEnv +import net.corda.coretesting.internal.inVMExecutors +import net.corda.coretesting.internal.rigorousMock +import net.corda.coretesting.internal.testThreadFactory import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnector import org.junit.rules.TestRule import org.junit.runner.Description @@ -28,7 +28,8 @@ class CheckpointSerializationEnvironmentRule(private val inheritable: Boolean = companion object { init { // Can't turn it off, and it creates threads that do serialization, so hack it: - InVMConnector::class.staticField("threadPoolExecutor").value = rigorousMock().also { + InVMConnector::class.staticField("threadPoolExecutor").value = rigorousMock() + .also { doAnswer { inVMExecutors.computeIfAbsent(effectiveSerializationEnv) { Executors.newCachedThreadPool(testThreadFactory(true)) // Close enough to what InVMConnector makes normally. diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/ContractJarTestUtils.kt b/testing/core-test-utils/src/main/kotlin/net/corda/testing/core/internal/ContractJarTestUtils.kt similarity index 100% rename from testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/ContractJarTestUtils.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/testing/core/internal/ContractJarTestUtils.kt diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/JarSignatureTestUtils.kt b/testing/core-test-utils/src/main/kotlin/net/corda/testing/core/internal/JarSignatureTestUtils.kt similarity index 100% rename from testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/JarSignatureTestUtils.kt rename to testing/core-test-utils/src/main/kotlin/net/corda/testing/core/internal/JarSignatureTestUtils.kt diff --git a/testing/test-utils/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker b/testing/core-test-utils/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker similarity index 100% rename from testing/test-utils/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker rename to testing/core-test-utils/src/main/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/testing/test-utils/src/test/kotlin/net/corda/testing/TestIdentityTests.kt b/testing/core-test-utils/src/test/kotlin/net/corda/coretesting/TestIdentityTests.kt similarity index 96% rename from testing/test-utils/src/test/kotlin/net/corda/testing/TestIdentityTests.kt rename to testing/core-test-utils/src/test/kotlin/net/corda/coretesting/TestIdentityTests.kt index 72d71f1adc..d4492119d9 100644 --- a/testing/test-utils/src/test/kotlin/net/corda/testing/TestIdentityTests.kt +++ b/testing/core-test-utils/src/test/kotlin/net/corda/coretesting/TestIdentityTests.kt @@ -1,4 +1,4 @@ -package net.corda.testing +package net.corda.coretesting import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME diff --git a/testing/test-utils/src/test/kotlin/net/corda/testing/internal/MatcherTests.kt b/testing/core-test-utils/src/test/kotlin/net/corda/coretesting/internal/MatcherTests.kt similarity index 95% rename from testing/test-utils/src/test/kotlin/net/corda/testing/internal/MatcherTests.kt rename to testing/core-test-utils/src/test/kotlin/net/corda/coretesting/internal/MatcherTests.kt index e7ac9a404b..5715276378 100644 --- a/testing/test-utils/src/test/kotlin/net/corda/testing/internal/MatcherTests.kt +++ b/testing/core-test-utils/src/test/kotlin/net/corda/coretesting/internal/MatcherTests.kt @@ -1,8 +1,8 @@ -package net.corda.testing.internal +package net.corda.coretesting.internal import com.natpryce.hamkrest.MatchResult import com.natpryce.hamkrest.equalTo -import net.corda.testing.internal.matchers.hasEntries +import net.corda.coretesting.internal.matchers.hasEntries import org.junit.Test import kotlin.test.assertEquals diff --git a/testing/test-utils/src/test/kotlin/net/corda/testing/internal/RigorousMockTest.kt b/testing/core-test-utils/src/test/kotlin/net/corda/coretesting/internal/RigorousMockTest.kt similarity index 99% rename from testing/test-utils/src/test/kotlin/net/corda/testing/internal/RigorousMockTest.kt rename to testing/core-test-utils/src/test/kotlin/net/corda/coretesting/internal/RigorousMockTest.kt index dc17245347..b2c9128544 100644 --- a/testing/test-utils/src/test/kotlin/net/corda/testing/internal/RigorousMockTest.kt +++ b/testing/core-test-utils/src/test/kotlin/net/corda/coretesting/internal/RigorousMockTest.kt @@ -1,4 +1,4 @@ -package net.corda.testing.internal +package net.corda.coretesting.internal import org.assertj.core.api.Assertions.catchThrowable import org.hamcrest.Matchers.isA diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index b46bfaeaae..ab767c3ff2 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -41,7 +41,7 @@ import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.contextTransaction import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.TestIdentity -import net.corda.testing.internal.DEV_ROOT_CA +import net.corda.coretesting.internal.DEV_ROOT_CA import net.corda.testing.internal.MockCordappProvider import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.configureDatabase 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 b7e5c9123f..0cba4f498f 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 @@ -73,7 +73,7 @@ import net.corda.testing.driver.* import net.corda.testing.driver.internal.InProcessImpl import net.corda.testing.driver.internal.NodeHandleInternal import net.corda.testing.driver.internal.OutOfProcessImpl -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.testing.node.ClusterSpec import net.corda.testing.node.NotarySpec import okhttp3.OkHttpClient diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index 29425cab4e..1e7e81c2f5 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt @@ -51,9 +51,9 @@ import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.common.internal.testNetworkParameters -import net.corda.testing.internal.rigorousMock -import net.corda.testing.internal.stubs.CertificateStoreStubs -import net.corda.testing.internal.testThreadFactory +import net.corda.coretesting.internal.rigorousMock +import net.corda.coretesting.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.testThreadFactory import net.corda.testing.node.* import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import org.apache.activemq.artemis.utils.ReusableLatch diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalTestUtils.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalTestUtils.kt index 633aa98b83..0793a740ba 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalTestUtils.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalTestUtils.kt @@ -26,8 +26,8 @@ import net.corda.node.services.messaging.Message import net.corda.testing.driver.DriverDSL import net.corda.testing.driver.NodeHandle import net.corda.testing.internal.chooseIdentity -import net.corda.testing.internal.createTestSerializationEnv -import net.corda.testing.internal.inVMExecutors +import net.corda.coretesting.internal.createTestSerializationEnv +import net.corda.coretesting.internal.inVMExecutors import net.corda.testing.node.InMemoryMessagingNetwork import net.corda.testing.node.TestCordapp import net.corda.testing.node.User diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index 3c43c99a97..b53a83c97f 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -22,7 +22,7 @@ import net.corda.nodeapi.internal.network.NetworkParametersCopier import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.driver.internal.incrementalPortAllocation -import net.corda.testing.internal.testThreadFactory +import net.corda.coretesting.internal.testThreadFactory import net.corda.testing.node.User import org.apache.commons.lang3.SystemUtils import org.apache.logging.log4j.Level diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt index d6eb12b0b1..0bb4000529 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt @@ -3,7 +3,7 @@ package net.corda.testing.node.internal import net.corda.client.mock.Generator import net.corda.client.rpc.CordaRPCClientConfiguration import net.corda.client.rpc.internal.RPCClient -import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme +import net.corda.nodeapi.internal.rpc.client.AMQPClientSerializationScheme import net.corda.core.concurrent.CordaFuture import net.corda.core.context.AuthServiceId import net.corda.core.context.Trace diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Injectors.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Injectors.kt index 4e173b657c..d1656fc86d 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Injectors.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/performance/Injectors.kt @@ -3,7 +3,7 @@ package net.corda.testing.node.internal.performance import com.codahale.metrics.Gauge import com.codahale.metrics.MetricRegistry import com.google.common.base.Stopwatch -import net.corda.testing.internal.performance.Rate +import net.corda.coretesting.internal.performance.Rate import net.corda.testing.node.internal.ShutdownManager import java.time.Duration import java.util.* diff --git a/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt b/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt index 5299204c2a..3a00a7b2c0 100644 --- a/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt +++ b/testing/node-driver/src/test/kotlin/net/corda/testing/node/internal/InternalMockNetworkTests.kt @@ -3,7 +3,7 @@ package net.corda.testing.node.internal import net.corda.core.messaging.AllPossibleRecipients import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.node.services.messaging.Message -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After import org.junit.Test diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt index 0d4bc8af8f..d39d6817ea 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt @@ -2,7 +2,7 @@ package net.corda.smoketesting import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCConnection -import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme +import net.corda.nodeapi.internal.rpc.client.AMQPClientSerializationScheme import net.corda.core.identity.Party import net.corda.core.internal.* import net.corda.core.node.NotaryInfo diff --git a/testing/test-common/build.gradle b/testing/test-common/build.gradle index f522d75d40..3914277b77 100644 --- a/testing/test-common/build.gradle +++ b/testing/test-common/build.gradle @@ -5,8 +5,6 @@ apply plugin: 'com.jfrog.artifactory' dependencies { compile project(':core') compile project(':node-api') - compile project(':tools:cliutils') - compile project(":common-logging") // Unit testing helpers. compile "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" diff --git a/testing/test-utils/build.gradle b/testing/test-utils/build.gradle index 797c607c70..2f07b49d69 100644 --- a/testing/test-utils/build.gradle +++ b/testing/test-utils/build.gradle @@ -9,16 +9,13 @@ description 'Testing utilities for Corda' dependencies { compile project(':test-common') + compile project(':core-test-utils') compile(project(':node')) { // The Node only needs this for binary compatibility with Cordapps written in Kotlin 1.1. exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jre8' } compile project(':client:mock') - compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" - compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - compile "com.google.guava:guava:$guava_version" // Guava: Google test library (collections test suite) diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index 8869068400..0d4698b58e 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -17,6 +17,8 @@ import net.corda.core.schemas.MappedSchema import net.corda.core.serialization.internal.effectiveSerializationEnv import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.loggerFor +import net.corda.coretesting.internal.asTestContextEnv +import net.corda.coretesting.internal.createTestSerializationEnv import net.corda.node.internal.createCordaPersistence import net.corda.node.internal.security.RPCSecurityManagerImpl import net.corda.node.internal.startHikariPool @@ -38,7 +40,7 @@ import net.corda.serialization.internal.amqp.AMQP_ENABLED import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.TestIdentity -import net.corda.testing.internal.stubs.CertificateStoreStubs +import net.corda.coretesting.internal.stubs.CertificateStoreStubs import java.io.ByteArrayOutputStream import java.io.IOException import java.net.ServerSocket diff --git a/tools/checkpoint-agent/build.gradle b/tools/checkpoint-agent/build.gradle index 65e92220f2..33fc683eb6 100644 --- a/tools/checkpoint-agent/build.gradle +++ b/tools/checkpoint-agent/build.gradle @@ -34,7 +34,7 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" compile "javassist:javassist:$javaassist_version" - compile "com.esotericsoftware:kryo:4.0.0" + compile "com.esotericsoftware:kryo:$kryo_version" compile "co.paralleluniverse:quasar-core:$quasar_version" compile (project(':core')) { diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/DemoBench.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/DemoBench.kt index dfecae05ac..396a105c04 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/DemoBench.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/DemoBench.kt @@ -1,7 +1,7 @@ package net.corda.demobench import javafx.scene.image.Image -import net.corda.client.rpc.internal.serialization.amqp.AMQPClientSerializationScheme +import net.corda.nodeapi.internal.rpc.client.AMQPClientSerializationScheme import net.corda.core.serialization.internal.SerializationEnvironment import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.demobench.views.DemoBenchView diff --git a/tools/demobench/src/test/kotlin/net/corda/demobench/pty/ZeroFilterTest.kt b/tools/demobench/src/test/kotlin/net/corda/demobench/pty/ZeroFilterTest.kt index 62fb3d3ef6..063efa3a2a 100644 --- a/tools/demobench/src/test/kotlin/net/corda/demobench/pty/ZeroFilterTest.kt +++ b/tools/demobench/src/test/kotlin/net/corda/demobench/pty/ZeroFilterTest.kt @@ -3,7 +3,7 @@ package net.corda.demobench.pty import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.verify import com.nhaarman.mockito_kotlin.whenever -import net.corda.testing.internal.rigorousMock +import net.corda.coretesting.internal.rigorousMock import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test diff --git a/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/serialization/SerializationHelper.kt b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/serialization/SerializationHelper.kt index c019d666b5..301b1cfa68 100644 --- a/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/serialization/SerializationHelper.kt +++ b/tools/network-builder/src/main/kotlin/net/corda/networkbuilder/serialization/SerializationHelper.kt @@ -2,9 +2,9 @@ package net.corda.networkbuilder.serialization import net.corda.core.serialization.internal.SerializationEnvironment import net.corda.core.serialization.internal.nodeSerializationEnv -import net.corda.node.serialization.amqp.AMQPServerSerializationScheme -import net.corda.node.serialization.kryo.KRYO_CHECKPOINT_CONTEXT -import net.corda.node.serialization.kryo.KryoCheckpointSerializer +import net.corda.nodeapi.internal.serialization.amqp.AMQPServerSerializationScheme +import net.corda.nodeapi.internal.serialization.kryo.KRYO_CHECKPOINT_CONTEXT +import net.corda.nodeapi.internal.serialization.kryo.KryoCheckpointSerializer import net.corda.serialization.internal.AMQP_P2P_CONTEXT import net.corda.serialization.internal.AMQP_STORAGE_CONTEXT import net.corda.serialization.internal.SerializationFactoryImpl diff --git a/tools/shell/src/test/java/net/corda/tools/shell/InteractiveShellJavaTest.java b/tools/shell/src/test/java/net/corda/tools/shell/InteractiveShellJavaTest.java index 5d7cabe089..7304eaac6b 100644 --- a/tools/shell/src/test/java/net/corda/tools/shell/InteractiveShellJavaTest.java +++ b/tools/shell/src/test/java/net/corda/tools/shell/InteractiveShellJavaTest.java @@ -19,9 +19,9 @@ import net.corda.core.internal.concurrent.CordaFutureImplKt; import net.corda.core.internal.concurrent.OpenFuture; import net.corda.core.messaging.FlowProgressHandleImpl; import net.corda.core.utilities.ProgressTracker; +import net.corda.coretesting.internal.InternalTestConstantsKt; import net.corda.node.services.identity.InMemoryIdentityService; import net.corda.testing.core.TestIdentity; -import net.corda.testing.internal.InternalTestConstantsKt; import org.jetbrains.annotations.Nullable; import org.junit.Test; import rx.Observable; diff --git a/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt b/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt index 4d4a49c169..279c0c7df4 100644 --- a/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt +++ b/tools/shell/src/test/kotlin/net/corda/tools/shell/InteractiveShellTest.kt @@ -23,12 +23,12 @@ import net.corda.core.messaging.FlowProgressHandleImpl import net.corda.core.node.NodeInfo import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.ProgressTracker +import net.corda.coretesting.internal.DEV_ROOT_CA import net.corda.node.services.identity.InMemoryIdentityService import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.TestIdentity import net.corda.testing.core.getTestPartyAndCertificate -import net.corda.testing.internal.DEV_ROOT_CA import org.crsh.command.InvocationContext import org.crsh.text.Color import org.crsh.text.Decoration From 20c50408261e91dabaee942a6625a1b3a66262f2 Mon Sep 17 00:00:00 2001 From: Stefano Franz Date: Wed, 4 Mar 2020 09:52:19 +0000 Subject: [PATCH 64/83] add ability to exit after config generation as is described in the docs (#6023) --- docker/src/bash/generate-config.sh | 188 +++++++++++++++-------------- docs/source/docker-image.rst | 2 +- 2 files changed, 99 insertions(+), 91 deletions(-) diff --git a/docker/src/bash/generate-config.sh b/docker/src/bash/generate-config.sh index 9ed216f65c..9a5ea55d8d 100755 --- a/docker/src/bash/generate-config.sh +++ b/docker/src/bash/generate-config.sh @@ -1,19 +1,23 @@ #!/usr/bin/env bash +GENERATE_TEST_NET=0 +GENERATE_GENERIC=0 +EXIT_ON_GENERATE=0 + die() { - printf '%s\n' "$1" >&2 - exit 1 + printf '%s\n' "$1" >&2 + exit 1 } -show_help(){ - echo "usage: generate-config <--testnet>|<--generic>" - echo -e "\t --testnet is used to generate config and certificates for joining TestNet" - echo -e "\t --generic is used to generate config and certificates for joining an existing Corda Compatibility Zone" +show_help() { + echo "usage: generate-config <--testnet>|<--generic>" + echo -e "\t --testnet is used to generate config and certificates for joining TestNet" + echo -e "\t --generic is used to generate config and certificates for joining an existing Corda Compatibility Zone" } function generateTestnetConfig() { - : ${RPC_PASSWORD=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)} - RPC_PASSWORD=${RPC_PASSWORD} \ + : ${RPC_PASSWORD=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)} + RPC_PASSWORD=${RPC_PASSWORD} \ DB_PASSWORD=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) \ MY_PUBLIC_ADDRESS=${MY_PUBLIC_ADDRESS} \ MY_P2P_PORT=${MY_P2P_PORT} \ @@ -24,101 +28,105 @@ function generateTestnetConfig() { java -jar config-exporter.jar "TEST-NET-COMBINE" "node.conf" "/opt/corda/starting-node.conf" "${CONFIG_FOLDER}/node.conf" } -function generateGenericCZConfig(){ - 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'} - : ${MY_EMAIL_ADDRESS:? '$MY_EMAIL_ADDRESS, the email to use when joining must be set as an environment variable'} - : ${NETWORK_TRUST_PASSWORD=:? '$NETWORK_TRUST_PASSWORD, the password to the network store to use when joining must be set as environment variable'} +function generateGenericCZConfig() { + 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'} + : ${MY_EMAIL_ADDRESS:? '$MY_EMAIL_ADDRESS, the email to use when joining must be set as an environment variable'} + : ${NETWORK_TRUST_PASSWORD=:? '$NETWORK_TRUST_PASSWORD, the password to the network store to use when joining must be set as environment variable'} - if [[ ! -f ${CERTIFICATES_FOLDER}/${TRUST_STORE_NAME} ]]; then - die "Network Trust Root file not found" - fi - : ${RPC_PASSWORD=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)} - RPC_PASSWORD=${RPC_PASSWORD} \ - DB_PASSWORD=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) \ - MY_PUBLIC_ADDRESS=${MY_PUBLIC_ADDRESS} \ - MY_P2P_PORT=${MY_P2P_PORT} \ - 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" + if [[ ! -f ${CERTIFICATES_FOLDER}/${TRUST_STORE_NAME} ]]; then + die "Network Trust Root file not found" + fi + : ${RPC_PASSWORD=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)} + RPC_PASSWORD=${RPC_PASSWORD} \ + DB_PASSWORD=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) \ + MY_PUBLIC_ADDRESS=${MY_PUBLIC_ADDRESS} \ + MY_P2P_PORT=${MY_P2P_PORT} \ + 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 "Successfully registered with ${DOORMAN_URL}, starting corda" + if [[ ${EXIT_ON_GENERATE} == 1 ]]; then + exit 0 + else + run-corda 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 "Successfully registered with ${DOORMAN_URL}, starting corda" && \ - run-corda } function downloadTestnetCerts() { - if [[ ! -f ${CERTIFICATES_FOLDER}/certs.zip ]]; then - : ${ONE_TIME_DOWNLOAD_KEY:? '$ONE_TIME_DOWNLOAD_KEY must be set as environment variable'} - : ${LOCALITY:? '$LOCALITY (the locality used when registering for Testnet) must be set as environment variable'} - : ${COUNTRY:? '$COUNTRY (the country used when registering for Testnet) must be set as environment variable'} - curl \ - -X POST "https://onboarder.prod.ws.r3.com/api/user/node/generate/one-time-key/redeem/$ONE_TIME_DOWNLOAD_KEY" \ - -o "${CERTIFICATES_FOLDER}/certs.zip" - fi - rm -rf ${CERTIFICATES_FOLDER}/*.jks - unzip ${CERTIFICATES_FOLDER}/certs.zip + if [[ ! -f ${CERTIFICATES_FOLDER}/certs.zip ]]; then + : ${ONE_TIME_DOWNLOAD_KEY:? '$ONE_TIME_DOWNLOAD_KEY must be set as environment variable'} + : ${LOCALITY:? '$LOCALITY (the locality used when registering for Testnet) must be set as environment variable'} + : ${COUNTRY:? '$COUNTRY (the country used when registering for Testnet) must be set as environment variable'} + curl \ + -X POST "https://onboarder.prod.ws.r3.com/api/user/node/generate/one-time-key/redeem/$ONE_TIME_DOWNLOAD_KEY" \ + -o "${CERTIFICATES_FOLDER}/certs.zip" + fi + rm -rf ${CERTIFICATES_FOLDER}/*.jks + unzip ${CERTIFICATES_FOLDER}/certs.zip } -GENERATE_TEST_NET=0 -GENERATE_GENERIC=0 - while :; do - case $1 in - -h|-\?|--help) - show_help # Display a usage synopsis. - exit - ;; - -t|--testnet) - if [[ ${GENERATE_GENERIC} = 0 ]]; then - GENERATE_TEST_NET=1 - else - die 'ERROR: cannot generate config for multiple networks' - fi - ;; - -g|--generic) - if [[ ${GENERATE_TEST_NET} = 0 ]]; then - GENERATE_GENERIC=1 - else - die 'ERROR: cannot generate config for multiple networks' - fi - ;; - --) # End of all options. - shift - break - ;; - -?*) - printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 - ;; - *) # Default case: No more options, so break out of the loop. - break - esac + case $1 in + -h | -\? | --help) + show_help # Display a usage synopsis. + exit + ;; + -t | --testnet) + if [[ ${GENERATE_GENERIC} == 0 ]]; then + GENERATE_TEST_NET=1 + else + die 'ERROR: cannot generate config for multiple networks' + fi + ;; + -g | --generic) + if [[ ${GENERATE_TEST_NET} == 0 ]]; then + GENERATE_GENERIC=1 + else + die 'ERROR: cannot generate config for multiple networks' + fi + ;; + -e | --exit-on-generate) + if [[ ${EXIT_ON_GENERATE} == 0 ]]; then + EXIT_ON_GENERATE=1 + else + die 'ERROR: cannot set exit on generate flag' + fi + ;; + --) # End of all options. shift + break + ;; + -?*) + printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 + ;; + *) # Default case: No more options, so break out of the loop. + break ;; + esac + shift done : ${TRUST_STORE_NAME="network-root-truststore.jks"} : ${JVM_ARGS='-Xmx4g -Xms2g -XX:+UseG1GC'} - -if [[ ${GENERATE_TEST_NET} == 1 ]] -then - : ${MY_PUBLIC_ADDRESS:? 'MY_PUBLIC_ADDRESS must be set as environment variable'} - downloadTestnetCerts - generateTestnetConfig -elif [[ ${GENERATE_GENERIC} == 1 ]] -then - : ${MY_PUBLIC_ADDRESS:? 'MY_PUBLIC_ADDRESS must be set as environment variable'} - generateGenericCZConfig +if [[ ${GENERATE_TEST_NET} == 1 ]]; then + : ${MY_PUBLIC_ADDRESS:? 'MY_PUBLIC_ADDRESS must be set as environment variable'} + downloadTestnetCerts + generateTestnetConfig +elif [[ ${GENERATE_GENERIC} == 1 ]]; then + : ${MY_PUBLIC_ADDRESS:? 'MY_PUBLIC_ADDRESS must be set as environment variable'} + generateGenericCZConfig else - show_help - die "No Valid Configuration requested" + show_help + die "No Valid Configuration requested" fi - diff --git a/docs/source/docker-image.rst b/docs/source/docker-image.rst index 7286918831..8a72fcd1a3 100644 --- a/docs/source/docker-image.rst +++ b/docs/source/docker-image.rst @@ -132,7 +132,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-java1.8-|corda_version_lower|:latest config-generator --generic + corda/corda-zulu-java1.8-|corda_version_lower|:latest config-generator --generic --exit-on-generate Several environment variables must also be passed to the container to allow it to register: From e006b871c8b09f3ef0cb35faaf7ae7d29e30b19b Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 4 Mar 2020 10:09:40 +0000 Subject: [PATCH 65/83] CORDA-3644: Scan the CorDapp classloader directly for SerializationWhitelist. (#6014) * CORDA-3644: Scan the CorDapp classloader directly for SerializationWhitelist. * CORDA-3644: Filter CorDapps from out-of-process node classpaths by their manifest attributes. Also exclude directories and blatant test artifacts. * Fix IRS Demo - its "tests" artifact had a non-standard classifier of "test". --- .../test/TutorialFlowAsyncOperationTest.java | 21 +++---- .../test/TutorialFlowAsyncOperationTest.kt | 1 - .../node/logging/ErrorCodeLoggingTests.kt | 14 +++-- .../cordapp/JarScanningCordappLoader.kt | 23 ++++--- .../cordapp/workflows-irs/build.gradle | 2 +- .../testing/node/internal/DriverDSLImpl.kt | 63 ++++++++++++++++--- 6 files changed, 86 insertions(+), 38 deletions(-) diff --git a/docs/source/example-code/src/integration-test/java/net/corda/docs/java/tutorial/test/TutorialFlowAsyncOperationTest.java b/docs/source/example-code/src/integration-test/java/net/corda/docs/java/tutorial/test/TutorialFlowAsyncOperationTest.java index 96629b6533..9336e2b104 100644 --- a/docs/source/example-code/src/integration-test/java/net/corda/docs/java/tutorial/test/TutorialFlowAsyncOperationTest.java +++ b/docs/source/example-code/src/integration-test/java/net/corda/docs/java/tutorial/test/TutorialFlowAsyncOperationTest.java @@ -1,6 +1,5 @@ package net.corda.docs.java.tutorial.test; -import kotlin.Unit; import net.corda.client.rpc.CordaRPCClient; import net.corda.core.messaging.CordaRPCOps; import net.corda.core.utilities.KotlinUtilsKt; @@ -10,24 +9,24 @@ import net.corda.testing.driver.*; import net.corda.testing.node.User; import org.junit.Test; -import java.util.Collections; -import java.util.HashSet; import java.util.concurrent.Future; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; import static net.corda.testing.core.TestConstants.ALICE_NAME; +import static net.corda.testing.driver.Driver.driver; +import static net.corda.testing.node.internal.InternalTestUtilsKt.cordappWithPackages; import static org.junit.Assert.assertEquals; -public final class TutorialFlowAsyncOperationTest { +public class TutorialFlowAsyncOperationTest { // DOCSTART summingWorks @Test - public final void summingWorks() { - Driver.driver(new DriverParameters(), (DriverDSL dsl) -> { - User aliceUser = new User("aliceUser", "testPassword1", - new HashSet<>(Collections.singletonList(Permissions.all())) - ); + public void summingWorks() { + driver(new DriverParameters(singletonList(cordappWithPackages("net.corda.docs.java.tutorial.flowstatemachines"))), (DriverDSL dsl) -> { + User aliceUser = new User("aliceUser", "testPassword1", singleton(Permissions.all())); Future aliceFuture = dsl.startNode(new NodeParameters() .withProvidedName(ALICE_NAME) - .withRpcUsers(Collections.singletonList(aliceUser)) + .withRpcUsers(singletonList(aliceUser)) ); NodeHandle alice = KotlinUtilsKt.getOrThrow(aliceFuture, null); CordaRPCClient aliceClient = new CordaRPCClient(alice.getRpcAddress()); @@ -35,7 +34,7 @@ public final class TutorialFlowAsyncOperationTest { Future answerFuture = aliceProxy.startFlowDynamic(ExampleSummingFlow.class).getReturnValue(); int answer = KotlinUtilsKt.getOrThrow(answerFuture, null); assertEquals(3, answer); - return Unit.INSTANCE; + return null; }); } // DOCEND summingWorks diff --git a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/kotlin/tutorial/test/TutorialFlowAsyncOperationTest.kt b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/kotlin/tutorial/test/TutorialFlowAsyncOperationTest.kt index 7ea1ec3735..5c87b0ce1e 100644 --- a/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/kotlin/tutorial/test/TutorialFlowAsyncOperationTest.kt +++ b/docs/source/example-code/src/integration-test/kotlin/net/corda/docs/kotlin/tutorial/test/TutorialFlowAsyncOperationTest.kt @@ -10,7 +10,6 @@ import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.driver import net.corda.testing.node.User import net.corda.testing.node.internal.cordappWithPackages -import net.corda.testing.node.internal.findCordapp import org.junit.Test import kotlin.test.assertEquals diff --git a/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt b/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt index c28dbabb5e..e1db95e528 100644 --- a/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt @@ -1,6 +1,5 @@ package net.corda.node.logging -import net.corda.core.flows.FlowException import net.corda.core.flows.FlowLogic import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.StartableByRPC @@ -23,7 +22,13 @@ class ErrorCodeLoggingTests { node.rpc.startFlow(::MyFlow).waitForCompletion() val logFile = node.logFile() - val linesWithErrorCode = logFile.useLines { lines -> lines.filter { line -> line.contains("[errorCode=") }.filter { line -> line.contains("moreInformationAt=https://errors.corda.net/") }.toList() } + val linesWithErrorCode = logFile.useLines { lines -> + lines.filter { line -> + line.contains("[errorCode=") + }.filter { line -> + line.contains("moreInformationAt=https://errors.corda.net/") + }.toList() + } assertThat(linesWithErrorCode).isNotEmpty } @@ -35,10 +40,11 @@ class ErrorCodeLoggingTests { fun `When logging is set to error level, there are no other levels logged after node startup`() { driver(DriverParameters(notarySpecs = emptyList())) { val node = startNode(startInSameProcess = false, logLevelOverride = "ERROR").getOrThrow() - node.rpc.startFlow(::MyFlow).waitForCompletion() val logFile = node.logFile() + val lengthAfterStart = logFile.length() + node.rpc.startFlow(::MyFlow).waitForCompletion() // An exception thrown in a flow will log at the "INFO" level. - assertThat(logFile.length()).isEqualTo(0) + assertThat(logFile.length()).isEqualTo(lengthAfterStart) } } diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt index b3a93e5655..a8fef21fc4 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/JarScanningCordappLoader.kt @@ -22,7 +22,6 @@ import net.corda.node.VersionInfo import net.corda.nodeapi.internal.cordapp.CordappLoader import net.corda.nodeapi.internal.coreContractClasses import net.corda.serialization.internal.DefaultWhitelist -import org.apache.commons.collections4.map.LRUMap import java.lang.reflect.Modifier import java.math.BigInteger import java.net.URL @@ -293,9 +292,7 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: } private fun findWhitelists(cordappJarPath: RestrictedURL): List { - val whitelists = URLClassLoader(arrayOf(cordappJarPath.url)).use { - ServiceLoader.load(SerializationWhitelist::class.java, it).toList() - } + val whitelists = ServiceLoader.load(SerializationWhitelist::class.java, appClassLoader).toList() return whitelists.filter { it.javaClass.location == cordappJarPath.url && it.javaClass.name.startsWith(cordappJarPath.qualifiedNamePrefix) } + DefaultWhitelist // Always add the DefaultWhitelist to the whitelist for an app. @@ -309,19 +306,21 @@ class JarScanningCordappLoader private constructor(private val cordappJarPaths: return scanResult.getClassesWithSuperclass(MappedSchema::class).instances().toSet() } - private val cachedScanResult = LRUMap(1000) - private fun scanCordapp(cordappJarPath: RestrictedURL): RestrictedScanResult { - logger.info("Scanning CorDapp in ${cordappJarPath.url}") - return cachedScanResult.computeIfAbsent(cordappJarPath) { - val scanResult = ClassGraph().addClassLoader(appClassLoader).overrideClasspath(cordappJarPath.url).enableAllInfo().pooledScan() - RestrictedScanResult(scanResult, cordappJarPath.qualifiedNamePrefix) - } + val cordappElement = cordappJarPath.url.toString() + logger.info("Scanning CorDapp in $cordappElement") + val scanResult = ClassGraph() + .filterClasspathElements { elt -> elt == cordappElement } + .overrideClassLoaders(appClassLoader) + .ignoreParentClassLoaders() + .enableAllInfo() + .pooledScan() + return RestrictedScanResult(scanResult, cordappJarPath.qualifiedNamePrefix) } private fun loadClass(className: String, type: KClass): Class? { return try { - appClassLoader.loadClass(className).asSubclass(type.java) + Class.forName(className, false, appClassLoader).asSubclass(type.java) } catch (e: ClassCastException) { logger.warn("As $className must be a sub-type of ${type.java.name}") null diff --git a/samples/irs-demo/cordapp/workflows-irs/build.gradle b/samples/irs-demo/cordapp/workflows-irs/build.gradle index d34d6a9dda..ce09b2a803 100644 --- a/samples/irs-demo/cordapp/workflows-irs/build.gradle +++ b/samples/irs-demo/cordapp/workflows-irs/build.gradle @@ -56,7 +56,7 @@ jar { } task testJar(type: Jar) { - classifier "test" + classifier "tests" from sourceSets.main.output from sourceSets.test.output } 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 48c602d359..c3c5037470 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 @@ -21,8 +21,20 @@ import net.corda.core.internal.concurrent.fork import net.corda.core.internal.concurrent.map import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.transpose +import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_NAME +import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_LICENCE +import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VENDOR +import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VERSION +import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_NAME +import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_LICENCE +import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VENDOR +import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VERSION +import net.corda.core.internal.cordapp.CordappImpl.Companion.MIN_PLATFORM_VERSION +import net.corda.core.internal.cordapp.CordappImpl.Companion.TARGET_PLATFORM_VERSION +import net.corda.core.internal.cordapp.get import net.corda.core.internal.createDirectories import net.corda.core.internal.div +import net.corda.core.internal.isRegularFile import net.corda.core.internal.list import net.corda.core.internal.packageName_ import net.corda.core.internal.readObject @@ -80,24 +92,26 @@ import okhttp3.OkHttpClient import okhttp3.Request import rx.Subscription import rx.schedulers.Schedulers -import java.io.File import java.net.ConnectException import java.net.URL import java.net.URLClassLoader +import java.nio.file.Files import java.nio.file.Path +import java.nio.file.Paths import java.security.cert.X509Certificate import java.time.Duration import java.time.Instant import java.time.ZoneOffset.UTC import java.time.ZonedDateTime import java.time.format.DateTimeFormatter -import java.util.Random -import java.util.UUID +import java.util.* +import java.util.Collections.unmodifiableList import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException import java.util.concurrent.atomic.AtomicInteger +import java.util.jar.JarInputStream import kotlin.collections.ArrayList import kotlin.collections.HashMap import kotlin.collections.HashSet @@ -792,6 +806,17 @@ class DriverDSLImpl( Permissions.invokeRpc(CordaRPCOps::killFlow) ) + private val CORDAPP_MANIFEST_ATTRIBUTES: List = unmodifiableList(listOf( + CORDAPP_CONTRACT_NAME, + CORDAPP_CONTRACT_LICENCE, + CORDAPP_CONTRACT_VENDOR, + CORDAPP_CONTRACT_VERSION, + CORDAPP_WORKFLOW_NAME, + CORDAPP_WORKFLOW_LICENCE, + CORDAPP_WORKFLOW_VENDOR, + CORDAPP_WORKFLOW_VERSION + )) + /** * Add the DJVM's sources to the node's configuration file. * These will all be ignored unless devMode is also true. @@ -923,12 +948,11 @@ class DriverDSLImpl( // The following dependencies are excluded from the classpath of the created JVM, // so that the environment resembles a real one as close as possible. - // These are either classes that will be added as attachments to the node (i.e. samples, finance, opengamma etc.) - // or irrelevant testing libraries (test, corda-mock etc.). - // TODO: There is pending work to fix this issue without custom blacklisting. See: https://r3-cev.atlassian.net/browse/CORDA-2164. - val exclude = listOf("samples", "finance", "integrationTest", "test", "corda-mock", "com.opengamma.strata") - val cp = ProcessUtilities.defaultClassPath.filterNot { cpEntry -> - exclude.any { token -> cpEntry.contains("${File.separatorChar}$token") } || cpEntry.endsWith("-tests.jar") + val cp = ProcessUtilities.defaultClassPath.filter { cpEntry -> + val cpPathEntry = Paths.get(cpEntry) + cpPathEntry.isRegularFile() + && !isTestArtifact(cpPathEntry.fileName.toString()) + && !cpPathEntry.isCorDapp } return ProcessUtilities.startJavaProcess( @@ -944,6 +968,27 @@ class DriverDSLImpl( ) } + // Obvious test artifacts. This is NOT intended to be an exhaustive list! + // It is only intended to remove those FEW jars which BLATANTLY do not + // belong inside a Corda Node. + private fun isTestArtifact(name: String): Boolean { + return name.endsWith("-tests.jar") + || name.endsWith("-test.jar") + || name.startsWith("corda-mock") + || name.startsWith("junit") + || name.startsWith("testng") + || name.startsWith("mockito") + } + + // Identify CorDapp JARs by their attributes in MANIFEST.MF. + private val Path.isCorDapp: Boolean get() { + return JarInputStream(Files.newInputStream(this).buffered()).use { jar -> + val manifest = jar.manifest ?: return false + CORDAPP_MANIFEST_ATTRIBUTES.any { manifest[it] != null } + || (manifest[TARGET_PLATFORM_VERSION] != null && manifest[MIN_PLATFORM_VERSION] != null) + } + } + private fun startWebserver(handle: NodeHandleInternal, debugPort: Int?, maximumHeapSize: String): Process { val className = "net.corda.webserver.WebServer" writeConfig(handle.baseDirectory, "web-server.conf", handle.toWebServerConfig()) From ba68970549f1f0653e6d6d1e36fb634a06c68ad7 Mon Sep 17 00:00:00 2001 From: Andrei Palade <2119610+palade@users.noreply.github.com> Date: Wed, 4 Mar 2020 11:43:05 +0000 Subject: [PATCH 66/83] Updated error message to provide consistent logging (#6015) --- .../kotlin/net/corda/node/internal/NetworkParametersReader.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt b/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt index b6df8efa29..0bab5cb88e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NetworkParametersReader.kt @@ -29,8 +29,8 @@ class NetworkParametersReader(private val trustRoot: X509Certificate, "parameters advertised by network map. Please update node to use correct network parameters file." ) class OldParams(previousParametersHash: SecureHash, advertisedParametersHash: SecureHash) : Error( - "Node uses parameters with hash: $previousParametersHash but network map is advertising: " + - "$advertisedParametersHash. Please update node to use correct network parameters file." + """Node is using network parameters with hash $previousParametersHash but the network map is advertising $advertisedParametersHash. + To resolve this mismatch, and move to the current parameters, delete the $NETWORK_PARAMS_FILE_NAME file from the node's directory and restart.""" ) } From bc410b4c993972951651fabca0d2ae9b99bd1ff8 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 4 Mar 2020 16:18:59 +0000 Subject: [PATCH 67/83] CORDA-3377: Upgrade to DJVM 1.0-RC10. (#6021) --- constants.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constants.properties b/constants.properties index d43a8afbe8..10bc33dcab 100644 --- a/constants.properties +++ b/constants.properties @@ -30,7 +30,7 @@ snakeYamlVersion=1.19 caffeineVersion=2.7.0 metricsVersion=4.1.0 metricsNewRelicVersion=1.1.1 -djvmVersion=1.0-RC09 +djvmVersion=1.0-RC10 deterministicRtVersion=1.0-RC02 openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4 openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4 From e6390916269b9a074cd14b32d59819c43bb0a394 Mon Sep 17 00:00:00 2001 From: Joseph Zuniga-Daly <59851625+josephzunigadaly@users.noreply.github.com> Date: Wed, 4 Mar 2020 16:40:01 +0000 Subject: [PATCH 68/83] Delete extra characters at end of key-concepts-ledger.rst (#6028) --- docs/source/key-concepts-ledger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/key-concepts-ledger.rst b/docs/source/key-concepts-ledger.rst index 76c1b3513e..17dcf151f4 100644 --- a/docs/source/key-concepts-ledger.rst +++ b/docs/source/key-concepts-ledger.rst @@ -65,4 +65,4 @@ Corda guarantees that whenever one of these facts is shared by multiple nodes on **Note:** Not all on-ledger facts are shared between peers. For example, Alice's fact 11 is not shared with Bob. Fact 11 could, in fact, not be shared with any other node at all. If this is the case, it is deemed a unilateral fact. -**Note:** Although there is no central ledger, it is possible to broadcast a basic fact to all participants should you wish to. You would do this by using the network map service to loop over all parties.De +**Note:** Although there is no central ledger, it is possible to broadcast a basic fact to all participants should you wish to. You would do this by using the network map service to loop over all parties. From e611064c9ead98c42887830aee14209a7152fadc Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 4 Mar 2020 17:23:41 +0000 Subject: [PATCH 69/83] Fix compilation error due to rigorousMock moving into coretesting. --- .../nodeapi/internal/protonwrapper/engine/EventProcessorTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessorTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessorTest.kt index 6ad42f25c2..9a8aee79c2 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessorTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/engine/EventProcessorTest.kt @@ -8,11 +8,11 @@ import io.netty.channel.Channel import io.netty.channel.ChannelFuture import io.netty.channel.DefaultEventLoop import io.netty.channel.EventLoop +import net.corda.coretesting.internal.rigorousMock import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.messages.impl.SendableMessageImpl import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME -import net.corda.testing.internal.rigorousMock import org.apache.qpid.proton.amqp.transport.Begin import org.apache.qpid.proton.amqp.transport.Open import org.apache.qpid.proton.engine.impl.TransportImpl From 9a406839fade1dea5f0cebd7584b54b492709289 Mon Sep 17 00:00:00 2001 From: Matthew Nesbit Date: Wed, 4 Mar 2020 17:59:15 +0000 Subject: [PATCH 70/83] ENT-4494 split nodeapi tests (#6024) * Split out node-api tests that require test-utils/node-driver * Add node-api test artefacts to publication list. * Make test-common a transient dependency - downstream tests assume that it's available. * Switch dependencies to java-library * Fix magic package name for cordapp scanning in test --- node-api-tests/build.gradle | 19 ++++++ ...tachmentsClassLoaderStaticContractTests.kt | 11 ++-- .../internal/CordaPersistenceTest.kt | 2 +- .../internal/crypto/X509UtilitiesTest.kt | 10 ++- .../network/NetworkBootstrapperTest.kt | 14 +++- .../persistence/MissingSchemaMigrationTest.kt | 5 +- .../serialization/kryo/KryoAttachmentTest.kt | 65 +++++++++++++++++++ node-api/build.gradle | 4 +- .../internal/network/NetworkBootstrapper.kt | 2 +- .../protonwrapper/netty/SSLHelperTest.kt | 2 +- .../internal/serialization/kryo/KryoTests.kt | 15 ----- .../services/messaging/MQSecurityTest.kt | 2 +- .../services/messaging/SimpleMQClient.kt | 2 +- .../transactions/UniquenessProviderTests.kt | 2 +- settings.gradle | 1 + testing/core-test-utils/build.gradle | 5 +- .../coretesting/internal/CoreTestUtils.kt | 21 ++++++ .../testing/internal/InternalTestUtils.kt | 13 ---- 18 files changed, 148 insertions(+), 47 deletions(-) create mode 100644 node-api-tests/build.gradle rename {node-api/src/test/kotlin/net/corda/nodeapi => node-api-tests/src/test/kotlin/net/corda/nodeapitests}/internal/AttachmentsClassLoaderStaticContractTests.kt (91%) rename {node-api/src/test/kotlin/net/corda/nodeapi => node-api-tests/src/test/kotlin/net/corda/nodeapitests}/internal/CordaPersistenceTest.kt (98%) rename {node-api/src/test/kotlin/net/corda/nodeapi => node-api-tests/src/test/kotlin/net/corda/nodeapitests}/internal/crypto/X509UtilitiesTest.kt (98%) rename {node-api/src/test/kotlin/net/corda/nodeapi => node-api-tests/src/test/kotlin/net/corda/nodeapitests}/internal/network/NetworkBootstrapperTest.kt (96%) rename {node-api/src/test/kotlin/net/corda/nodeapi => node-api-tests/src/test/kotlin/net/corda/nodeapitests}/internal/persistence/MissingSchemaMigrationTest.kt (92%) create mode 100644 node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/serialization/kryo/KryoAttachmentTest.kt create mode 100644 testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/CoreTestUtils.kt diff --git a/node-api-tests/build.gradle b/node-api-tests/build.gradle new file mode 100644 index 0000000000..50cf05a864 --- /dev/null +++ b/node-api-tests/build.gradle @@ -0,0 +1,19 @@ +apply plugin: 'kotlin' + +description 'NodeAPI tests that require node etc' + +dependencies { + testCompile project(":node-api") + testCompile project(path: ':node-api', configuration:'testArtifacts') + testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" + testImplementation "junit:junit:$junit_version" + + testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}" + testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" + // Unit testing helpers. + testCompile "org.assertj:assertj-core:$assertj_version" + testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + testCompile project(':node-driver') + testCompile project(':test-utils') +} diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/AttachmentsClassLoaderStaticContractTests.kt similarity index 91% rename from node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt rename to node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/AttachmentsClassLoaderStaticContractTests.kt index 27276cb615..500e0e2873 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/AttachmentsClassLoaderStaticContractTests.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/AttachmentsClassLoaderStaticContractTests.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal +package net.corda.nodeapitests.internal import com.nhaarman.mockito_kotlin.any import com.nhaarman.mockito_kotlin.doReturn @@ -19,7 +19,7 @@ import net.corda.core.transactions.TransactionBuilder import net.corda.nodeapi.internal.cordapp.CordappLoader import net.corda.node.internal.cordapp.CordappProviderImpl import net.corda.node.internal.cordapp.JarScanningCordappLoader -import net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests.AttachmentDummyContract.Companion.ATTACHMENT_PROGRAM_ID +import net.corda.nodeapitests.internal.AttachmentsClassLoaderStaticContractTests.AttachmentDummyContract.Companion.ATTACHMENT_PROGRAM_ID import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.SerializationEnvironmentRule @@ -45,7 +45,7 @@ class AttachmentsClassLoaderStaticContractTests { class AttachmentDummyContract : Contract { companion object { - const val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests\$AttachmentDummyContract" + const val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapitests.internal.AttachmentsClassLoaderStaticContractTests\$AttachmentDummyContract" } data class State(val magicNumber: Int = 0) : ContractState { @@ -75,7 +75,7 @@ class AttachmentsClassLoaderStaticContractTests { } private val serviceHub get() = rigorousMock().also { - val cordappProviderImpl = CordappProviderImpl(cordappLoaderForPackages(listOf("net.corda.nodeapi.internal")), MockCordappConfigProvider(), MockAttachmentStorage()) + val cordappProviderImpl = CordappProviderImpl(cordappLoaderForPackages(listOf("net.corda.nodeapitests.internal")), MockCordappConfigProvider(), MockAttachmentStorage()) cordappProviderImpl.start() doReturn(cordappProviderImpl).whenever(it).cordappProvider doReturn(networkParametersService).whenever(it).networkParametersService @@ -95,7 +95,8 @@ class AttachmentsClassLoaderStaticContractTests { @Test(timeout=300_000) fun `test serialization of WireTransaction with statically loaded contract`() { - val tx = AttachmentDummyContract().generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) + val tx = AttachmentDummyContract() + .generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val wireTransaction = tx.toWireTransaction(serviceHub) val bytes = wireTransaction.serialize() val copiedWireTransaction = bytes.deserialize() diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/CordaPersistenceTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/CordaPersistenceTest.kt similarity index 98% rename from node-api/src/test/kotlin/net/corda/nodeapi/internal/CordaPersistenceTest.kt rename to node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/CordaPersistenceTest.kt index 7b7cea187f..0ed8476f0c 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/CordaPersistenceTest.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/CordaPersistenceTest.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal +package net.corda.nodeapitests.internal import net.corda.node.services.schema.NodeSchemaService import net.corda.nodeapi.internal.persistence.DatabaseConfig diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt similarity index 98% rename from node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt rename to node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt index f548fb0487..61b936d5d2 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/X509UtilitiesTest.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal.crypto +package net.corda.nodeapitests.internal.crypto import io.netty.handler.ssl.ClientAuth @@ -42,6 +42,14 @@ import net.corda.coretesting.internal.NettyTestHandler import net.corda.coretesting.internal.NettyTestServer import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.coretesting.internal.stubs.CertificateStoreStubs +import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509Utilities +import net.corda.nodeapi.internal.crypto.checkValidity +import net.corda.nodeapi.internal.crypto.getSupportedKey +import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore +import net.corda.nodeapi.internal.crypto.save +import net.corda.nodeapi.internal.crypto.toBc +import net.corda.nodeapi.internal.crypto.x509 import net.i2p.crypto.eddsa.EdDSAPrivateKey import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x509.* diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/network/NetworkBootstrapperTest.kt similarity index 96% rename from node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt rename to node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/network/NetworkBootstrapperTest.kt index 4d94be99a6..a06626a792 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapperTest.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/network/NetworkBootstrapperTest.kt @@ -1,4 +1,4 @@ -package net.corda.nodeapi.internal.network +package net.corda.nodeapitests.internal.network import com.typesafe.config.ConfigFactory import net.corda.core.crypto.secureRandomBytes @@ -24,6 +24,14 @@ import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.TestIdentity import net.corda.coretesting.internal.createNodeInfoAndSigned +import net.corda.nodeapi.internal.network.CopyCordapps +import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME +import net.corda.nodeapi.internal.network.NetworkBootstrapper +import net.corda.nodeapi.internal.network.NetworkParametersOverrides +import net.corda.nodeapi.internal.network.PackageOwner +import net.corda.nodeapi.internal.network.SignedNetworkParameters +import net.corda.nodeapi.internal.network.TestContractsJar +import net.corda.nodeapi.internal.network.verifiedNetworkParametersCert import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.After @@ -72,7 +80,9 @@ class NetworkBootstrapperTest { private val bootstrapper = NetworkBootstrapper( initSerEnv = false, - embeddedCordaJar = { fakeEmbeddedCordaJar.toUri().toURL() }, + embeddedCordaJar = { + fakeEmbeddedCordaJar.toUri().toURL() + }, nodeInfosGenerator = { nodeDirs -> nodeDirs.map { nodeDir -> val name = nodeDir.fakeNodeConfig.myLegalName diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/MissingSchemaMigrationTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/MissingSchemaMigrationTest.kt similarity index 92% rename from node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/MissingSchemaMigrationTest.kt rename to node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/MissingSchemaMigrationTest.kt index 1c975cd93a..b81a0eaceb 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/persistence/MissingSchemaMigrationTest.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/persistence/MissingSchemaMigrationTest.kt @@ -1,7 +1,10 @@ -package net.corda.nodeapi.internal.persistence +package net.corda.nodeapitests.internal.persistence import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState +import net.corda.nodeapi.internal.persistence.DatabaseConfig +import net.corda.nodeapi.internal.persistence.MissingMigrationException +import net.corda.nodeapi.internal.persistence.SchemaMigration import net.corda.node.internal.DataSourceFactory import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.schema.NodeSchemaService diff --git a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/serialization/kryo/KryoAttachmentTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/serialization/kryo/KryoAttachmentTest.kt new file mode 100644 index 0000000000..ea256b786b --- /dev/null +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/serialization/kryo/KryoAttachmentTest.kt @@ -0,0 +1,65 @@ +package net.corda.nodeapitests.internal.serialization.kryo + +import com.nhaarman.mockito_kotlin.doReturn +import com.nhaarman.mockito_kotlin.whenever +import net.corda.core.crypto.SecureHash +import net.corda.core.serialization.EncodingWhitelist +import net.corda.core.serialization.internal.CheckpointSerializationContext +import net.corda.core.serialization.internal.checkpointDeserialize +import net.corda.core.serialization.internal.checkpointSerialize +import net.corda.coretesting.internal.rigorousMock +import net.corda.node.services.persistence.NodeAttachmentService +import net.corda.serialization.internal.AllWhitelist +import net.corda.serialization.internal.CheckpointSerializationContextImpl +import net.corda.serialization.internal.CordaSerializationEncoding +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.TestIdentity +import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import java.io.InputStream + +@RunWith(Parameterized::class) +class KryoAttachmentTest(private val compression: CordaSerializationEncoding?) { + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun compression() = arrayOf(null) + CordaSerializationEncoding.values() + } + + + @get:Rule + val serializationRule = CheckpointSerializationEnvironmentRule() + private lateinit var context: CheckpointSerializationContext + + @Before + fun setup() { + context = CheckpointSerializationContextImpl( + javaClass.classLoader, + AllWhitelist, + emptyMap(), + true, + compression, + rigorousMock().also { + if (compression != null) doReturn(true).whenever(it).acceptEncoding(compression) + }) + } + + @Test(timeout=300_000) + fun `HashCheckingStream (de)serialize`() { + val rubbish = ByteArray(12345) { (it * it * 0.12345).toByte() } + val readRubbishStream: InputStream = NodeAttachmentService.HashCheckingStream( + SecureHash.sha256(rubbish), + rubbish.size, + rubbish.inputStream() + ).checkpointSerialize(context).checkpointDeserialize(context) + for (i in 0..12344) { + Assert.assertEquals(rubbish[i], readRubbishStream.read().toByte()) + } + Assert.assertEquals(-1, readRubbishStream.read()) + } +} \ No newline at end of file diff --git a/node-api/build.gradle b/node-api/build.gradle index 3f1797aa94..2f5f774b9a 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -54,8 +54,7 @@ dependencies { // Unit testing helpers. testCompile "org.assertj:assertj-core:$assertj_version" testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - testCompile project(':node-driver') - testCompile project(':test-utils') + testCompile project(':core-test-utils') compile ("org.apache.activemq:artemis-amqp-protocol:${artemis_version}") { // Gains our proton-j version from core module. @@ -74,6 +73,7 @@ task testJar(type: Jar) { artifacts { testArtifacts testJar + publish testJar } jar { diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt index bbeff4adce..d2f8da84c3 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -54,7 +54,7 @@ import kotlin.streams.toList // TODO Move this to tools:bootstrapper class NetworkBootstrapper @VisibleForTesting -internal constructor(private val initSerEnv: Boolean, +constructor(private val initSerEnv: Boolean, private val embeddedCordaJar: () -> URL, private val nodeInfosGenerator: (List) -> List, private val contractsJarConverter: (Path) -> ContractsJar) : NetworkBootstrapperWithOverridableParameters { diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelperTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelperTest.kt index 00a2bc3650..3802a357b0 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelperTest.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/SSLHelperTest.kt @@ -3,8 +3,8 @@ package net.corda.nodeapi.internal.protonwrapper.netty import net.corda.core.crypto.SecureHash import net.corda.core.identity.CordaX500Name import net.corda.core.utilities.NetworkHostAndPort +import net.corda.coretesting.internal.configureTestSSL import net.corda.nodeapi.internal.config.CertificateStore -import net.corda.testing.internal.configureTestSSL import org.junit.Test import javax.net.ssl.KeyManagerFactory import javax.net.ssl.SNIHostName diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt index 1fae69d049..c0142f8105 100644 --- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt +++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt @@ -18,7 +18,6 @@ import net.corda.core.serialization.internal.checkpointSerialize import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.sequence -import net.corda.node.services.persistence.NodeAttachmentService import net.corda.serialization.internal.* import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.TestIdentity @@ -198,20 +197,6 @@ class KryoTests(private val compression: CordaSerializationEncoding?) { assertTrue(logger === logger2) } - @Test(timeout=300_000) - fun `HashCheckingStream (de)serialize`() { - val rubbish = ByteArray(12345) { (it * it * 0.12345).toByte() } - val readRubbishStream: InputStream = NodeAttachmentService.HashCheckingStream( - SecureHash.sha256(rubbish), - rubbish.size, - rubbish.inputStream() - ).checkpointSerialize(context).checkpointDeserialize(context) - for (i in 0..12344) { - assertEquals(rubbish[i], readRubbishStream.read().toByte()) - } - assertEquals(-1, readRubbishStream.read()) - } - @CordaSerializable private data class Person(val name: String, val birthday: Instant?) diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt index 92e8c72287..2586a67ced 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/MQSecurityTest.kt @@ -14,6 +14,7 @@ import net.corda.core.messaging.CordaRPCOps import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.unwrap +import net.corda.coretesting.internal.configureTestSSL import net.corda.node.internal.NodeWithInfo import net.corda.nodeapi.RPCApi import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.INTERNAL_PREFIX @@ -22,7 +23,6 @@ import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.BOB_NAME import net.corda.testing.core.singleIdentity -import net.corda.testing.internal.configureTestSSL import net.corda.testing.node.User import net.corda.testing.node.internal.NodeBasedTest import net.corda.testing.node.internal.startFlow diff --git a/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt b/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt index 44e59852a9..fa5fc09d53 100644 --- a/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt +++ b/node/src/integration-test/kotlin/net/corda/services/messaging/SimpleMQClient.kt @@ -3,9 +3,9 @@ package net.corda.services.messaging import net.corda.core.identity.CordaX500Name import net.corda.core.serialization.internal.nodeSerializationEnv import net.corda.core.utilities.NetworkHostAndPort +import net.corda.coretesting.internal.configureTestSSL import net.corda.nodeapi.internal.ArtemisTcpTransport.Companion.p2pConnectorTcpTransport import net.corda.nodeapi.internal.config.MutualSslConfiguration -import net.corda.testing.internal.configureTestSSL import org.apache.activemq.artemis.api.core.client.* /** diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/UniquenessProviderTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/UniquenessProviderTests.kt index bd59890436..b33b093467 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/UniquenessProviderTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/UniquenessProviderTests.kt @@ -16,6 +16,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.notary.UniquenessProvider import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.minutes +import net.corda.coretesting.internal.configureTestSSL import net.corda.node.services.schema.NodeSchemaService import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.persistence.CordaPersistence @@ -29,7 +30,6 @@ import net.corda.testing.core.generateStateRef import net.corda.testing.internal.LogHelper import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.configureDatabase -import net.corda.testing.internal.configureTestSSL import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.TestClock import net.corda.testing.node.internal.MockKeyManagementService diff --git a/settings.gradle b/settings.gradle index 8a636cdf94..2fbb64d38e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,6 +18,7 @@ include 'core' include 'core-tests' include 'docs' include 'node-api' +include 'node-api-tests' include 'node' include 'node:capsule' include 'node:djvm' diff --git a/testing/core-test-utils/build.gradle b/testing/core-test-utils/build.gradle index 53954ea735..c58fe75c7d 100644 --- a/testing/core-test-utils/build.gradle +++ b/testing/core-test-utils/build.gradle @@ -3,14 +3,15 @@ plugins { id 'net.corda.plugins.publish-utils' id 'net.corda.plugins.api-scanner' id 'com.jfrog.artifactory' + id 'java-library' } description 'Core test types and helpers for testing Corda' dependencies { implementation project(':core') - implementation project(':test-common') - compile "org.jetbrains.kotlin:kotlin-test" + api project(':test-common') + api "org.jetbrains.kotlin:kotlin-test" } jar { diff --git a/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/CoreTestUtils.kt b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/CoreTestUtils.kt new file mode 100644 index 0000000000..66b8810232 --- /dev/null +++ b/testing/core-test-utils/src/main/kotlin/net/corda/coretesting/internal/CoreTestUtils.kt @@ -0,0 +1,21 @@ +package net.corda.coretesting.internal + +import net.corda.core.identity.CordaX500Name +import net.corda.coretesting.internal.stubs.CertificateStoreStubs +import net.corda.nodeapi.internal.config.MutualSslConfiguration +import net.corda.nodeapi.internal.loadDevCaTrustStore +import net.corda.nodeapi.internal.registerDevP2pCertificates +import java.nio.file.Files + +fun configureTestSSL(legalName: CordaX500Name): MutualSslConfiguration { + + val certificatesDirectory = Files.createTempDirectory("certs") + val config = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) + if (config.trustStore.getOptional() == null) { + loadDevCaTrustStore().copyTo(config.trustStore.get(true)) + } + if (config.keyStore.getOptional() == null) { + config.keyStore.get(true).registerDevP2pCertificates(legalName) + } + return config +} diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt index 0d4698b58e..84e7091d1f 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/internal/InternalTestUtils.kt @@ -67,19 +67,6 @@ inline fun T.amqpSpecific(reason: String, function: () -> Unit loggerFor().info("Ignoring AMQP specific test, reason: $reason") } -fun configureTestSSL(legalName: CordaX500Name): MutualSslConfiguration { - - val certificatesDirectory = Files.createTempDirectory("certs") - val config = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory) - if (config.trustStore.getOptional() == null) { - loadDevCaTrustStore().copyTo(config.trustStore.get(true)) - } - if (config.keyStore.getOptional() == null) { - config.keyStore.get(true).registerDevP2pCertificates(legalName) - } - return config -} - private val defaultRootCaName = X500Principal("CN=Corda Root CA,O=R3 Ltd,L=London,C=GB") private val defaultIntermediateCaName = X500Principal("CN=Corda Intermediate CA,O=R3 Ltd,L=London,C=GB") From 2911548a2e44956ade5867ffbd53b199a5eb938d Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Wed, 4 Mar 2020 20:56:17 +0000 Subject: [PATCH 71/83] Restore Node's missing dependency on jansi. --- node/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/node/build.gradle b/node/build.gradle index cfc7e38706..ec7c5df061 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -109,6 +109,7 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" + compile "org.fusesource.jansi:jansi:$jansi_version" compile "com.google.guava:guava:$guava_version" // For caches rather than guava From 4c944f048ce193117ebe9ae1d196c92193bb6967 Mon Sep 17 00:00:00 2001 From: Ritu Gupta Date: Thu, 5 Mar 2020 12:12:05 +0000 Subject: [PATCH 72/83] [NOTICK] The list added for Bugs and Storys without Epic link --- docs/source/release-notes.rst | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index ff9c00a567..6c0309d3ad 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -63,6 +63,43 @@ For more information on platform version, please see :doc:`versioning`. For more Issues Fixed ~~~~~~~~~~~~ +* A failure response from Doorman during initial registration causes a class cast exception [`CORDA-2744 `_] +* Add an exception for Unrecoverable RPC errors [`CORDA-3192 `_] +* Fix the misleading Flow has been waiting message [`CORDA-3197 `_] +* Update Quasar agent so that we can exclude entire ClassLoaders from being instrumented [`CORDA-3228 `_] +* Don't fail on liquibase errors when using H2 [`CORDA-3302 `_] +* Exceptions thrown in raw vault observers can cause critical issues [`CORDA-3329 `_] +* Migration from Corda 3.x to 4.x for PostgreSQL require a manual workaround [`CORDA-3348 `_] +* Prepare DJVM library for 1.0 release [`CORDA-3377 `_] +* Improve node configuration override documentation [`CORDA-3386 `_] +* Allow EvolutionSerializer to handle primitive types becoming nullable [`CORDA-3390 `_] +* Fix caching of local AMQPSerializer [`CORDA-3392 `_] +* Fixed NPE in BlobInspector [`CORDA-3396 `_] +* Update DemoBench so that using the DJVM is configurable [`CORDA-3406 `_] +* Scanning for Custom Serializers in the context of transaction verification is broken [`CORDA-3464 `_] +* Allow EvolutionSerializer to handle boxed types becoming primitive [`CORDA-3469 `_] +* Create interface to perform transactional operations from custom CordaServices [`CORDA-3471 `_] +* Fix typo in node database table documentation [`CORDA-3476 `_] +* Fix node database page [`CORDA-3477 `_] +* Add timestamp column to NODE_TRANSACTIONS table [`CORDA-3479 `_] +* Support adding new mandatory field and removal of optional [`CORDA-3489 `_] +* Fix link to network builder [`CORDA-3495 `_] +* Provide option for user to specify custom serializers without classpath scanning [`CORDA-3501 `_] +* The CordaRPCClientConfiguration is not respected when GracefulReconnect is used [`CORDA-3507 `_] +* Fix for Could not start flow as connection failed error on starting flow via ShellCli if user is not authorized to use this flow [`CORDA-3513 `_] +* Support whitelists and custom serializers inside the DJVM [`CORDA-3523 `_] +* Load DJVM serialization types more precisely to avoid runtime warnings [`CORDA-3536 `_] +* Use the config values for reconnecting retry interval and max reconnect attempts [`CORDA-3542 `_] +* SSH memory leak and security [`CORDA-3520 `_] +* Remove support for outdated ciphers and algorithms from SSH [`CORDA-3550 `_] +* Deserialization using the DJVM creates too many SerializerFactory objects [`CORDA-3552 `_] +* Allow initial registration errors to propagate up so the node exits with a failure code [`CORDA-3558 `_] +* Remove reference to man run [`CORDA-3559 `_] +* Always add TestCordapps to the classpath when building _driverSerializationEnv [`CORDA-3566 `_] +* Use the connectionMaxRetryInterval configuration when reconnection the RPC client [`CORDA-3576 `_] +* Update docs for X500 name and SSH hostkey [`CORDA-3585 `_] +* hashLookup command help misspelling [`CORDA-3587 `_] +* Exit the InteractiveShell on shutdown command [`CORDA-3593 `_]* A failure response from Doorman during initial registration causes a class cast exception [`CORDA-2744 `_] .. _release_notes_v4_3: From 2c2d7e938a38129900826b4ffc332c7ee7b7f0e4 Mon Sep 17 00:00:00 2001 From: Ritu Gupta Date: Thu, 5 Mar 2020 15:06:20 +0000 Subject: [PATCH 73/83] [NOTICK] The list added for Bugs and Storys without Epic link --- docs/source/release-notes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/release-notes.rst b/docs/source/release-notes.rst index 6c0309d3ad..67291c790e 100644 --- a/docs/source/release-notes.rst +++ b/docs/source/release-notes.rst @@ -99,7 +99,7 @@ Issues Fixed * Use the connectionMaxRetryInterval configuration when reconnection the RPC client [`CORDA-3576 `_] * Update docs for X500 name and SSH hostkey [`CORDA-3585 `_] * hashLookup command help misspelling [`CORDA-3587 `_] -* Exit the InteractiveShell on shutdown command [`CORDA-3593 `_]* A failure response from Doorman during initial registration causes a class cast exception [`CORDA-2744 `_] +* Exit the InteractiveShell on shutdown command [`CORDA-3593 `_] .. _release_notes_v4_3: From c3a59f5293edca6ff966cb4b3214b31a9dea6af0 Mon Sep 17 00:00:00 2001 From: Stefano Franz Date: Thu, 5 Mar 2020 15:38:56 +0000 Subject: [PATCH 74/83] EG-464 Corda returns incorrect exit code in case if node is started with unknown/missing option (#6010) * EG-464 Corda returns incorrect exit code in case if node is started with unknown/missing option * fixed empty else block * We should not use the parent's exception handler as it will quit and we have our own one * SampleCordaCliWrapper implemented and tests to verify error handling. * addressing code review comments --- .../CordaCliWrapperErrorHandlingTests.kt | 50 +++++++++++++++++++ .../internal/RunCordaNodeReturnCodeTests.kt | 36 +++++++++++++ .../node/internal/SampleCordaCliWrapper.kt | 38 ++++++++++++++ .../testing/node/internal/ProcessUtilities.kt | 5 +- .../net/corda/cliutils/CordaCliWrapper.kt | 33 ++++++++---- 5 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/CordaCliWrapperErrorHandlingTests.kt create mode 100644 testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/RunCordaNodeReturnCodeTests.kt create mode 100644 testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/SampleCordaCliWrapper.kt diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/CordaCliWrapperErrorHandlingTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/CordaCliWrapperErrorHandlingTests.kt new file mode 100644 index 0000000000..76dad5b90e --- /dev/null +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/CordaCliWrapperErrorHandlingTests.kt @@ -0,0 +1,50 @@ +package net.corda.testing.node.internal + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.matchesPattern +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import java.io.BufferedReader +import java.io.InputStreamReader +import java.util.stream.Collectors + + +@RunWith(value = Parameterized::class) +class CordaCliWrapperErrorHandlingTests(val arguments: List, val outputRegexPattern: String) { + + companion object { + val className = "net.corda.testing.node.internal.SampleCordaCliWrapper" + + private val stackTraceRegex = "^.+Exception[^\\n]++(\\s+at .++)+[\\s\\S]*" + private val exceptionWithoutStackTraceRegex ="${className}(\\s+.+)" + private val emptyStringRegex = "^$" + + @JvmStatic + @Parameterized.Parameters + fun data() = listOf( + arrayOf(listOf("--throw-exception", "--verbose"), stackTraceRegex), + arrayOf(listOf("--throw-exception"), exceptionWithoutStackTraceRegex), + arrayOf(listOf("--sample-command"), emptyStringRegex) + ) + } + + @Test(timeout=300_000) + fun `Run CordaCliWrapper sample app with arguments and check error output matches regExp`() { + + val process = ProcessUtilities.startJavaProcess( + className = className, + arguments = arguments, + inheritIO = false) + + process.waitFor() + + val processErrorOutput = BufferedReader( + InputStreamReader(process.errorStream)) + .lines() + .collect(Collectors.joining("\n")) + .toString() + + assertThat(processErrorOutput, matchesPattern(outputRegexPattern)) + } +} \ No newline at end of file diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/RunCordaNodeReturnCodeTests.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/RunCordaNodeReturnCodeTests.kt new file mode 100644 index 0000000000..67bc7e880b --- /dev/null +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/RunCordaNodeReturnCodeTests.kt @@ -0,0 +1,36 @@ +package net.corda.testing.node.internal + + +import net.corda.cliutils.ExitCodes +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +import kotlin.test.assertEquals +@RunWith(value = Parameterized::class) +class RunCordaNodeReturnCodeTests(val argument: String, val exitCode: Int){ + + companion object { + @JvmStatic + @Parameterized.Parameters + fun data() = listOf( + arrayOf("--nonExistingOption", ExitCodes.FAILURE), + arrayOf("--help", ExitCodes.SUCCESS), + arrayOf("validate-configuration", ExitCodes.FAILURE),//Should fail as there is no node.conf + arrayOf("initial-registration", ExitCodes.FAILURE) //Missing required option + ) + } + + @Test(timeout=300_000) + fun runCordaWithArgumentAndAssertExitCode() { + + val process = ProcessUtilities.startJavaProcess( + className = "net.corda.node.Corda", + arguments = listOf(argument) + ) + process.waitFor() + assertEquals(exitCode, process.exitValue()) + } + + +} \ No newline at end of file diff --git a/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/SampleCordaCliWrapper.kt b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/SampleCordaCliWrapper.kt new file mode 100644 index 0000000000..8b91efec2b --- /dev/null +++ b/testing/node-driver/src/integration-test/kotlin/net/corda/testing/node/internal/SampleCordaCliWrapper.kt @@ -0,0 +1,38 @@ +package net.corda.testing.node.internal + +import net.corda.cliutils.CordaCliWrapper +import net.corda.cliutils.ExitCodes +import net.corda.cliutils.start +import picocli.CommandLine + +class SampleCordaCliWrapperException(message: String) : Exception(message) +class SampleCordaCliWrapper: CordaCliWrapper("sampleCliWrapper", "Sample corda cliWrapper app") { + + + companion object { + @JvmStatic + fun main(args: Array) { + SampleCordaCliWrapper().start(args) + } + } + + @CommandLine.Option(names = ["--sample-command"], + description = [ "Sample command. Prints a message to the console."]) + var sampleCommand: Boolean? = null + + @CommandLine.Option(names = ["--throw-exception"], description = ["Specify this to throw an exception"]) + var throwException: Boolean? = null + + override fun runProgram(): Int { + + + if (throwException!=null) { + throw SampleCordaCliWrapperException("net.corda.testing.node.internal.SampleCordaCliWrapper test exception") + } + if (sampleCommand!=null) { + System.out.println("Sample command invoked.") + } + return ExitCodes.SUCCESS + } + +} \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt index 9acff0d020..c4a04ecb2a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt @@ -37,7 +37,8 @@ object ProcessUtilities { extraJvmArguments: List = emptyList(), maximumHeapSize: String? = null, identifier: String = "", - environmentVariables: Map = emptyMap() + environmentVariables: Map = emptyMap(), + inheritIO: Boolean = true ): Process { val command = mutableListOf().apply { add(javaPath) @@ -49,7 +50,7 @@ object ProcessUtilities { addAll(arguments) } return ProcessBuilder(command).apply { - inheritIO() + if (inheritIO) inheritIO() environment().putAll(environmentVariables) environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator) if (workingDirectory != null) { diff --git a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt index 8bb4130a64..a2ab753251 100644 --- a/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt +++ b/tools/cliutils/src/main/kotlin/net/corda/cliutils/CordaCliWrapper.kt @@ -64,15 +64,33 @@ fun CordaCliWrapper.start(args: Array) { // This line makes sure ANSI escapes work on Windows, where they aren't supported out of the box. AnsiConsole.systemInstall() - try { val defaultAnsiMode = if (CordaSystemUtils.isOsWindows()) { Help.Ansi.ON } else { Help.Ansi.AUTO } + + val exceptionHandler = object : DefaultExceptionHandler>() { + + override fun handleParseException(ex: ParameterException?, args: Array?): List { + super.handleParseException(ex, args) + return listOf(ExitCodes.FAILURE) + } + override fun handleExecutionException(ex: ExecutionException, parseResult: ParseResult?): List { + + val throwable = ex.cause ?: ex + if (this@start.verbose || this@start.subCommands.any { it.verbose }) { + throwable.printStackTrace() + } + printError(throwable.rootMessage ?: "Use --verbose for more details") + return listOf(ExitCodes.FAILURE) + } + } @Suppress("SpreadOperator") val results = cmd.parseWithHandlers(RunLast().useOut(System.out).useAnsi(defaultAnsiMode), - DefaultExceptionHandler>().useErr(System.err).useAnsi(defaultAnsiMode), *args) + exceptionHandler.useErr(System.err).useAnsi(defaultAnsiMode), *args) + + // If an error code has been returned, use this and exit results?.firstOrNull()?.let { if (it is Int) { @@ -81,17 +99,10 @@ fun CordaCliWrapper.start(args: Array) { exitProcess(ExitCodes.FAILURE) } } + // If no results returned, picocli ran something without invoking the main program, e.g. --help or --version, so exit successfully exitProcess(ExitCodes.SUCCESS) - } catch (e: ExecutionException) { - val throwable = e.cause ?: e - if (this.verbose || this.subCommands.any { it.verbose }) { - throwable.printStackTrace() - } else { - } - printError(throwable.rootMessage ?: "Use --verbose for more details") - exitProcess(ExitCodes.FAILURE) - } + } @Command(mixinStandardHelpOptions = true, From e5a8888232ae087ec69a1ba9d26441114860024c Mon Sep 17 00:00:00 2001 From: Rick Parker Date: Thu, 5 Mar 2020 17:39:55 +0000 Subject: [PATCH 75/83] CORDA-3644: Add Corda-Testing tag to test artifacts' MANIFEST.MF. (#6032) --- client/mock/build.gradle | 8 ++++++- .../dbfailure/dbfcontracts/build.gradle | 7 ++++++- .../dbfailure/dbfworkflows/build.gradle | 7 ++++++- testing/node-driver/build.gradle | 5 +++++ .../testing/node/internal/DriverDSLImpl.kt | 21 +++++++++++++++---- testing/smoke-test-utils/build.gradle | 8 +++++++ testing/test-cli/build.gradle | 10 +++++---- testing/test-common/build.gradle | 5 +++++ testing/test-db/build.gradle | 5 +++++ testing/test-utils/build.gradle | 5 +++++ 10 files changed, 70 insertions(+), 11 deletions(-) diff --git a/client/mock/build.gradle b/client/mock/build.gradle index 3cebd33402..712ada2bf1 100644 --- a/client/mock/build.gradle +++ b/client/mock/build.gradle @@ -29,7 +29,13 @@ dependencies { jar { baseName 'corda-mock' manifest { - attributes 'Automatic-Module-Name': 'net.corda.client.mock' + attributes( + 'Automatic-Module-Name': 'net.corda.client.mock', + + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + 'Corda-Testing': true + ) } } diff --git a/testing/cordapps/dbfailure/dbfcontracts/build.gradle b/testing/cordapps/dbfailure/dbfcontracts/build.gradle index 76dd8b8082..8767f08a31 100644 --- a/testing/cordapps/dbfailure/dbfcontracts/build.gradle +++ b/testing/cordapps/dbfailure/dbfcontracts/build.gradle @@ -13,6 +13,11 @@ dependencies { compile project(":core") } -jar{ +jar { baseName "testing-dbfailure-contracts" + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } } \ No newline at end of file diff --git a/testing/cordapps/dbfailure/dbfworkflows/build.gradle b/testing/cordapps/dbfailure/dbfworkflows/build.gradle index 571a3cb3a5..26e0058cc5 100644 --- a/testing/cordapps/dbfailure/dbfworkflows/build.gradle +++ b/testing/cordapps/dbfailure/dbfworkflows/build.gradle @@ -7,6 +7,11 @@ dependencies { compile project(":testing:cordapps:dbfailure:dbfcontracts") } -jar{ +jar { baseName "testing-dbfailure-workflows" + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } } \ No newline at end of file diff --git a/testing/node-driver/build.gradle b/testing/node-driver/build.gradle index 152abc037d..02664e0a38 100644 --- a/testing/node-driver/build.gradle +++ b/testing/node-driver/build.gradle @@ -66,6 +66,11 @@ task integrationTest(type: Test) { jar { baseName 'corda-node-driver' + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } } publish { 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 c3c5037470..744f455c6b 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 @@ -1,3 +1,4 @@ +@file:Suppress("TooManyFunctions") package net.corda.testing.node.internal import co.paralleluniverse.fibers.instrument.JavaAgent @@ -112,6 +113,7 @@ import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException import java.util.concurrent.atomic.AtomicInteger import java.util.jar.JarInputStream +import java.util.jar.Manifest import kotlin.collections.ArrayList import kotlin.collections.HashMap import kotlin.collections.HashSet @@ -806,6 +808,8 @@ class DriverDSLImpl( Permissions.invokeRpc(CordaRPCOps::killFlow) ) + private const val CORDA_TESTING_ATTRIBUTE = "Corda-Testing" + private val CORDAPP_MANIFEST_ATTRIBUTES: List = unmodifiableList(listOf( CORDAPP_CONTRACT_NAME, CORDAPP_CONTRACT_LICENCE, @@ -952,7 +956,7 @@ class DriverDSLImpl( val cpPathEntry = Paths.get(cpEntry) cpPathEntry.isRegularFile() && !isTestArtifact(cpPathEntry.fileName.toString()) - && !cpPathEntry.isCorDapp + && !cpPathEntry.isExcludedJar } return ProcessUtilities.startJavaProcess( @@ -980,12 +984,21 @@ class DriverDSLImpl( || name.startsWith("mockito") } + // Identify Corda's own testing framework by attribute in MANIFEST.MF. + private fun isTestArtifact(manifest: Manifest): Boolean { + return manifest[CORDA_TESTING_ATTRIBUTE] != null + } + // Identify CorDapp JARs by their attributes in MANIFEST.MF. - private val Path.isCorDapp: Boolean get() { + private fun isCordapp(manifest: Manifest): Boolean { + return CORDAPP_MANIFEST_ATTRIBUTES.any { manifest[it] != null } + || (manifest[TARGET_PLATFORM_VERSION] != null && manifest[MIN_PLATFORM_VERSION] != null) + } + + private val Path.isExcludedJar: Boolean get() { return JarInputStream(Files.newInputStream(this).buffered()).use { jar -> val manifest = jar.manifest ?: return false - CORDAPP_MANIFEST_ATTRIBUTES.any { manifest[it] != null } - || (manifest[TARGET_PLATFORM_VERSION] != null && manifest[MIN_PLATFORM_VERSION] != null) + isCordapp(manifest) || isTestArtifact(manifest) } } diff --git a/testing/smoke-test-utils/build.gradle b/testing/smoke-test-utils/build.gradle index 0868951903..0a1023a061 100644 --- a/testing/smoke-test-utils/build.gradle +++ b/testing/smoke-test-utils/build.gradle @@ -7,3 +7,11 @@ dependencies { compile project(':test-common') compile project(':client:rpc') } + +tasks.named('jar', Jar) { + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } +} \ No newline at end of file diff --git a/testing/test-cli/build.gradle b/testing/test-cli/build.gradle index ac45c22be9..a668c605f4 100644 --- a/testing/test-cli/build.gradle +++ b/testing/test-cli/build.gradle @@ -14,10 +14,12 @@ dependencies { testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}" testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" - } -compileKotlin { - kotlinOptions { - languageVersion = "1.2" + +tasks.named('jar', Jar) { + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) } } \ No newline at end of file diff --git a/testing/test-common/build.gradle b/testing/test-common/build.gradle index f522d75d40..f3d667e49a 100644 --- a/testing/test-common/build.gradle +++ b/testing/test-common/build.gradle @@ -25,6 +25,11 @@ dependencies { jar { baseName 'corda-test-common' + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } } publish { diff --git a/testing/test-db/build.gradle b/testing/test-db/build.gradle index f4e906addb..3be30d52fe 100644 --- a/testing/test-db/build.gradle +++ b/testing/test-db/build.gradle @@ -16,6 +16,11 @@ dependencies { jar { baseName 'corda-test-db' + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } } publish { diff --git a/testing/test-utils/build.gradle b/testing/test-utils/build.gradle index 797c607c70..f8a85f6607 100644 --- a/testing/test-utils/build.gradle +++ b/testing/test-utils/build.gradle @@ -36,6 +36,11 @@ dependencies { jar { baseName 'corda-test-utils' + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } } publish { From 5b50ef49bcca2d1be8bb0604e1be167e3bb5da24 Mon Sep 17 00:00:00 2001 From: Adel El-Beik <48713346+adelel1@users.noreply.github.com> Date: Fri, 6 Mar 2020 09:14:36 +0000 Subject: [PATCH 76/83] CORDA-3651: addManifest now uses separate files for reading and writing. (#6026) * CORDA-3651: addManifest now uses separate files for reading and writing. * CORDA-3651: The jar scanning loader now closes itsself. Co-authored-by: Adel El-Beik --- .../internal/cordapp/CordappProviderImplTests.kt | 15 +++++++++++---- .../core/internal/JarSignatureTestUtils.kt | 5 ++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt index f981b11e24..2bd8a10258 100644 --- a/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt +++ b/node/src/test/kotlin/net/corda/node/internal/cordapp/CordappProviderImplTests.kt @@ -18,6 +18,7 @@ import java.io.FileOutputStream import java.lang.IllegalStateException import java.net.URL import java.nio.file.Files +import java.util.Arrays.asList import java.util.jar.JarOutputStream import java.util.zip.Deflater.NO_COMPRESSION import java.util.zip.ZipEntry @@ -200,8 +201,11 @@ class CordappProviderImplTests { val duplicateJarPath = signedJarPath.parent.resolve("duplicate-" + signedJarPath.fileName) Files.copy(signedJarPath, duplicateJarPath) - assertFailsWith { - newCordappProvider(signedJarPath.toUri().toURL(), duplicateJarPath.toUri().toURL()) + val urls = asList(signedJarPath.toUri().toURL(), duplicateJarPath.toUri().toURL()) + JarScanningCordappLoader.fromJarUrls(urls, VersionInfo.UNKNOWN).use { + assertFailsWith { + CordappProviderImpl(it, stubConfigProvider, attachmentStore).apply { start() } + } } } } @@ -212,8 +216,11 @@ class CordappProviderImplTests { SelfCleaningDir().use { file -> val jarA = ContractJarTestUtils.makeTestContractJar(file.path, listOf("com.example.MyContract", "com.example.AnotherContractForA"), generateManifest = false, jarFileName = "sampleA.jar") val jarB = ContractJarTestUtils.makeTestContractJar(file.path, listOf("com.example.MyContract", "com.example.AnotherContractForB"), generateManifest = false, jarFileName = "sampleB.jar") - assertFailsWith { - newCordappProvider(jarA.toUri().toURL(), jarB.toUri().toURL()) + val urls = asList(jarA.toUri().toURL(), jarB.toUri().toURL()) + JarScanningCordappLoader.fromJarUrls(urls, VersionInfo.UNKNOWN).use { + assertFailsWith { + CordappProviderImpl(it, stubConfigProvider, attachmentStore).apply { start() } + } } } } diff --git a/testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/JarSignatureTestUtils.kt b/testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/JarSignatureTestUtils.kt index 00a827c1e1..4c21340f84 100644 --- a/testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/JarSignatureTestUtils.kt +++ b/testing/test-utils/src/main/kotlin/net/corda/testing/core/internal/JarSignatureTestUtils.kt @@ -12,6 +12,7 @@ import java.nio.file.Files import java.nio.file.NoSuchFileException import java.nio.file.Path import java.nio.file.Paths +import java.nio.file.StandardCopyOption.REPLACE_EXISTING import java.security.PublicKey import java.util.jar.Attributes import java.util.jar.JarInputStream @@ -88,12 +89,13 @@ object JarSignatureTestUtils { JarInputStream(FileInputStream((this / fileName).toFile())).use(JarSignatureCollector::collectSigners) fun Path.addManifest(fileName: String, vararg entries: Pair) { + val outputFile = this / (fileName + "Output") JarInputStream(FileInputStream((this / fileName).toFile())).use { input -> val manifest = input.manifest ?: Manifest() entries.forEach { (attributeName, value) -> manifest.mainAttributes[attributeName] = value } - val output = JarOutputStream(FileOutputStream((this / fileName).toFile()), manifest) + val output = JarOutputStream(FileOutputStream(outputFile.toFile()), manifest) var entry = input.nextEntry val buffer = ByteArray(1 shl 14) while (true) { @@ -108,5 +110,6 @@ object JarSignatureTestUtils { } output.close() } + Files.copy(outputFile, this / fileName, REPLACE_EXISTING) } } From 00b55399cb5e930a052e49b71e71cd0132d2034f Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Sun, 8 Mar 2020 20:14:22 +0000 Subject: [PATCH 77/83] NOTICK: Better use of generics for DJVM Collection and Map serializers. --- .../djvm/deserializers/CreateCollection.kt | 36 ++++++++++--------- .../djvm/deserializers/CreateMap.kt | 36 ++++++++++--------- .../deserializers/OptionalDeserializer.kt | 4 +-- .../SandboxCollectionSerializer.kt | 6 ++-- .../djvm/serializers/SandboxMapSerializer.kt | 6 ++-- 5 files changed, 47 insertions(+), 41 deletions(-) diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCollection.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCollection.kt index 18a2933ec7..be69a73970 100644 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCollection.kt +++ b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateCollection.kt @@ -1,14 +1,18 @@ package net.corda.serialization.djvm.deserializers import net.corda.core.utilities.NonEmptySet -import java.util.Collections +import java.util.Collections.unmodifiableCollection +import java.util.Collections.unmodifiableList +import java.util.Collections.unmodifiableNavigableSet +import java.util.Collections.unmodifiableSet +import java.util.Collections.unmodifiableSortedSet import java.util.NavigableSet import java.util.SortedSet import java.util.TreeSet import java.util.function.Function -class CreateCollection : Function, Collection> { - private val concreteConstructors: Map>, (Array) -> Collection> = mapOf( +class CreateCollection : Function, Collection> { + private val concreteConstructors: Map>, (Array) -> Collection> = mapOf( List::class.java to ::createList, Set::class.java to ::createSet, SortedSet::class.java to ::createSortedSet, @@ -17,34 +21,34 @@ class CreateCollection : Function, Collection> { NonEmptySet::class.java to ::createNonEmptySet ) - private fun createList(values: Array): List { - return Collections.unmodifiableList(values.toCollection(ArrayList())) + private fun createList(values: Array): List { + return unmodifiableList(values.toCollection(ArrayList())) } - private fun createSet(values: Array): Set { - return Collections.unmodifiableSet(values.toCollection(LinkedHashSet())) + private fun createSet(values: Array): Set { + return unmodifiableSet(values.toCollection(LinkedHashSet())) } - private fun createSortedSet(values: Array): SortedSet { - return Collections.unmodifiableSortedSet(values.toCollection(TreeSet())) + private fun createSortedSet(values: Array): SortedSet { + return unmodifiableSortedSet(values.toCollection(TreeSet())) } - private fun createNavigableSet(values: Array): NavigableSet { - return Collections.unmodifiableNavigableSet(values.toCollection(TreeSet())) + private fun createNavigableSet(values: Array): NavigableSet { + return unmodifiableNavigableSet(values.toCollection(TreeSet())) } - private fun createCollection(values: Array): Collection { - return Collections.unmodifiableCollection(values.toCollection(ArrayList())) + private fun createCollection(values: Array): Collection { + return unmodifiableCollection(values.toCollection(ArrayList())) } - private fun createNonEmptySet(values: Array): NonEmptySet { + private fun createNonEmptySet(values: Array): NonEmptySet { return NonEmptySet.copyOf(values.toCollection(ArrayList())) } @Suppress("unchecked_cast") - override fun apply(inputs: Array): Collection { + override fun apply(inputs: Array): Collection { val collectionClass = inputs[0] as Class> - val args = inputs[1] as Array + val args = inputs[1] as Array return concreteConstructors[collectionClass]?.invoke(args)!! } } diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateMap.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateMap.kt index 6abc37901e..6759bc8d2c 100644 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateMap.kt +++ b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/CreateMap.kt @@ -1,14 +1,16 @@ package net.corda.serialization.djvm.deserializers -import java.util.Collections +import java.util.Collections.unmodifiableMap +import java.util.Collections.unmodifiableNavigableMap +import java.util.Collections.unmodifiableSortedMap import java.util.EnumMap import java.util.NavigableMap import java.util.SortedMap import java.util.TreeMap import java.util.function.Function -class CreateMap : Function, Map> { - private val concreteConstructors: Map>, (Array>) -> Map> = mapOf( +class CreateMap : Function, Map> { + private val concreteConstructors: Map>, (Array>) -> Map> = mapOf( Map::class.java to ::createMap, SortedMap::class.java to ::createSortedMap, LinkedHashMap::class.java to ::createLinkedHashMap, @@ -17,36 +19,36 @@ class CreateMap : Function, Map> { EnumMap::class.java to ::createEnumMap ) - private fun createMap(values: Array>): Map { - return Collections.unmodifiableMap(values.map { it[0] to it[1] }.toMap()) + private fun createMap(values: Array>): Map { + return unmodifiableMap(values.associate { it[0] to it[1] }) } - private fun createSortedMap(values: Array>): SortedMap { - return Collections.unmodifiableSortedMap(createTreeMap(values)) + private fun createSortedMap(values: Array>): SortedMap { + return unmodifiableSortedMap(createTreeMap(values)) } - private fun createNavigableMap(values: Array>): NavigableMap { - return Collections.unmodifiableNavigableMap(createTreeMap(values)) + private fun createNavigableMap(values: Array>): NavigableMap { + return unmodifiableNavigableMap(createTreeMap(values)) } - private fun createLinkedHashMap(values: Array>): LinkedHashMap { - return values.map { it[0] to it[1] }.toMap(LinkedHashMap()) + private fun createLinkedHashMap(values: Array>): LinkedHashMap { + return values.associateTo(LinkedHashMap()) { it[0] to it[1] } } - private fun createTreeMap(values: Array>): TreeMap { - return values.map { it[0] to it[1] }.toMap(TreeMap()) + private fun createTreeMap(values: Array>): TreeMap { + return values.associateTo(TreeMap()) { it[0] to it[1] } } - private fun createEnumMap(values: Array>): Map { - val map = values.map { it[0] to it[1] }.toMap() + private fun createEnumMap(values: Array>): Map { + val map = values.associate { it[0] to it[1] } @Suppress("unchecked_cast") return EnumMap(map as Map) as Map } @Suppress("unchecked_cast") - override fun apply(inputs: Array): Map { + override fun apply(inputs: Array): Map { val mapClass = inputs[0] as Class> - val args = inputs[1] as Array> + val args = inputs[1] as Array> return concreteConstructors[mapClass]?.invoke(args)!! } } diff --git a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OptionalDeserializer.kt b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OptionalDeserializer.kt index c9f93b43a1..0d8d511952 100644 --- a/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OptionalDeserializer.kt +++ b/serialization-djvm/deserializers/src/main/kotlin/net/corda/serialization/djvm/deserializers/OptionalDeserializer.kt @@ -4,8 +4,8 @@ import net.corda.serialization.internal.amqp.custom.OptionalSerializer.OptionalP import java.util.Optional import java.util.function.Function -class OptionalDeserializer : Function> { - override fun apply(proxy: OptionalProxy): Optional { +class OptionalDeserializer : Function> { + override fun apply(proxy: OptionalProxy): Optional { return Optional.ofNullable(proxy.item) } } diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt index df2ea33e95..5c5fa25c19 100644 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt +++ b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxCollectionSerializer.kt @@ -30,8 +30,8 @@ class SandboxCollectionSerializer( private val localFactory: LocalSerializerFactory ) : CustomSerializer.Implements(clazz = classLoader.toSandboxAnyClass(Collection::class.java)) { @Suppress("unchecked_cast") - private val creator: Function, out Any?> - = taskFactory.apply(CreateCollection::class.java) as Function, out Any?> + private val creator: Function, out Any?> + = taskFactory.apply(CreateCollection::class.java) as Function, out Any?> private val unsupportedTypes: Set> = listOf( EnumSet::class.java @@ -87,7 +87,7 @@ class SandboxCollectionSerializer( private class ConcreteCollectionSerializer( declaredType: ParameterizedType, private val matchingType: Map.Entry, Class>>, - private val creator: Function, out Any?>, + private val creator: Function, out Any?>, factory: LocalSerializerFactory ) : AMQPSerializer { override val type: ParameterizedType = declaredType diff --git a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt index 4234571181..6803ff4612 100644 --- a/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt +++ b/serialization-djvm/src/main/kotlin/net/corda/serialization/djvm/serializers/SandboxMapSerializer.kt @@ -30,8 +30,8 @@ class SandboxMapSerializer( private val localFactory: LocalSerializerFactory ) : CustomSerializer.Implements(clazz = classLoader.toSandboxAnyClass(Map::class.java)) { @Suppress("unchecked_cast") - private val creator: Function, out Any?> - = taskFactory.apply(CreateMap::class.java) as Function, out Any?> + private val creator: Function, out Any?> + = taskFactory.apply(CreateMap::class.java) as Function, out Any?> // The order matters here - the first match should be the most specific one. // Kotlin preserves the ordering for us by associating into a LinkedHashMap. @@ -77,7 +77,7 @@ class SandboxMapSerializer( private class ConcreteMapSerializer( declaredType: ParameterizedType, private val matchingType: Map.Entry, Class>>, - private val creator: Function, out Any?>, + private val creator: Function, out Any?>, factory: LocalSerializerFactory ) : AMQPSerializer { override val type: ParameterizedType = declaredType From 217926092d5632f17a8746e0df2329787c556108 Mon Sep 17 00:00:00 2001 From: Joseph Zuniga-Daly <59851625+josephzunigadaly@users.noreply.github.com> Date: Mon, 9 Mar 2020 11:40:51 +0000 Subject: [PATCH 78/83] ENT-4334 Configuration option to exclude packages from Quasar instrumentation (#6005) * Add quasarExcludePackages configuration option * Change quasarExcludePackages to quasar.excludePackages * Add doc comment for QuasarConfiguration * Use forEach method * Fix rule violation (TooGenericExceptionThrown) * Mention new configuration option in changelog * Remove outer section from new quasarExcludePackages configuration option --- docs/source/changelog.rst | 2 + docs/source/corda-configuration-file.rst | 14 +++- .../net/corda/node/internal/AbstractNode.kt | 11 ++++ .../node/services/config/NodeConfiguration.kt | 2 + .../services/config/NodeConfigurationImpl.kt | 4 +- .../schema/v1/V1NodeConfigurationSpec.kt | 4 +- .../internal/QuasarExcludePackagesTest.kt | 64 +++++++++++++++++++ .../test-config-quasarexcludepackages.conf | 33 ++++++++++ .../node/internal/InternalMockNetwork.kt | 1 + 9 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 node/src/test/kotlin/net/corda/node/internal/QuasarExcludePackagesTest.kt create mode 100644 node/src/test/resources/test-config-quasarexcludepackages.conf diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index e335464ee0..dfe00c134a 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -109,6 +109,8 @@ Unreleased options, as well as ``extensions.sshd`` configuration entry, have been removed from the standalone shell. Available alternatives are either to use the standalone shell directly, or connect to the node embedded shell via SSH. +* Added new node configuration option to exclude packages from Quasar instrumentation. + .. _changelog_v4.1: Version 4.1 diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index c3642f38b2..5a6af7ff2e 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -558,7 +558,19 @@ p2pAddress *Default:* not defined - + +quasarExcludePackages + A list of packages to exclude from Quasar instrumentation. Wildcards are allowed, for example ``org.xml**``. + + **Important: Do not change unless requested by support.** + + *Default:* empty list + + Example configuration: + + .. parsed-literal:: + quasarExcludePackages=["org.xml**", "org.yaml**"] + rpcAddress (deprecated) The address of the RPC system on which RPC requests can be made to the node. If not provided then the node will run without RPC. 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 496b9c0f7f..22bc371d6f 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -1,5 +1,6 @@ package net.corda.node.internal +import co.paralleluniverse.fibers.instrument.Retransform import com.codahale.metrics.MetricRegistry import com.google.common.collect.MutableClassToInstanceMap import com.google.common.util.concurrent.MoreExecutors @@ -227,6 +228,8 @@ abstract class AbstractNode(val configuration: NodeConfiguration, MoreExecutors.shutdownAndAwaitTermination(it, 50, SECONDS) } } + + quasarExcludePackages(configuration) } private val notaryLoader = configuration.notary?.let { @@ -420,6 +423,14 @@ abstract class AbstractNode(val configuration: NodeConfiguration, return validateKeyStores() } + private fun quasarExcludePackages(nodeConfiguration: NodeConfiguration) { + val quasarInstrumentor = Retransform.getInstrumentor() + + nodeConfiguration.quasarExcludePackages.forEach { packageExclude -> + quasarInstrumentor.addExcludedPackage(packageExclude) + } + } + open fun generateAndSaveNodeInfo(): NodeInfo { check(started == null) { "Node has already been started" } log.info("Generating nodeInfo ...") diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt index 42b55271b6..010e40007f 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt @@ -90,6 +90,8 @@ interface NodeConfiguration : ConfigurationWithOptionsContainer { val flowExternalOperationThreadPoolSize: Int + val quasarExcludePackages: List + companion object { // default to at least 8MB and a bit extra for larger heap sizes val defaultTransactionCacheSize: Long = 8.MB + getAdditionalCacheMemory() diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfigurationImpl.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfigurationImpl.kt index 2f00d2eab3..521b98d56c 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfigurationImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfigurationImpl.kt @@ -82,7 +82,8 @@ data class NodeConfigurationImpl( Defaults.networkParameterAcceptanceSettings, override val blacklistedAttachmentSigningKeys: List = Defaults.blacklistedAttachmentSigningKeys, override val configurationWithOptions: ConfigurationWithOptions, - override val flowExternalOperationThreadPoolSize: Int = Defaults.flowExternalOperationThreadPoolSize + override val flowExternalOperationThreadPoolSize: Int = Defaults.flowExternalOperationThreadPoolSize, + override val quasarExcludePackages: List = Defaults.quasarExcludePackages ) : NodeConfiguration { internal object Defaults { val jmxMonitoringHttpPort: Int? = null @@ -119,6 +120,7 @@ data class NodeConfigurationImpl( val networkParameterAcceptanceSettings: NetworkParameterAcceptanceSettings = NetworkParameterAcceptanceSettings() val blacklistedAttachmentSigningKeys: List = emptyList() const val flowExternalOperationThreadPoolSize: Int = 1 + val quasarExcludePackages: List = emptyList() fun cordappsDirectories(baseDirectory: Path) = listOf(baseDirectory / CORDAPPS_DIR_NAME_DEFAULT) diff --git a/node/src/main/kotlin/net/corda/node/services/config/schema/v1/V1NodeConfigurationSpec.kt b/node/src/main/kotlin/net/corda/node/services/config/schema/v1/V1NodeConfigurationSpec.kt index e83a23e7c2..7f7b66bc9f 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/schema/v1/V1NodeConfigurationSpec.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/schema/v1/V1NodeConfigurationSpec.kt @@ -64,6 +64,7 @@ internal object V1NodeConfigurationSpec : Configuration.Specification(), nodeConfiguration.quasarExcludePackages) + } + + + @Test(timeout=300_000) + fun `quasarExcludePackages is read from configuration`() { + + // Arrange + val config = getConfig("test-config-quasarexcludepackages.conf") + + // Act + val nodeConfiguration :NodeConfiguration = V1NodeConfigurationSpec.parse(config).value() + + // Assert + Assert.assertEquals(listOf("net.corda.node.internal.QuasarExcludePackagesTest**"), nodeConfiguration.quasarExcludePackages) + } + + @Test(timeout=300_000) + fun `quasarExcludePackages is passed through to QuasarInstrumentor`() { + + // Arrange + val config = getConfig("test-config-quasarexcludepackages.conf") + val nodeConfiguration :NodeConfiguration = V1NodeConfigurationSpec.parse(config).value() + + // Act + val node = Node(nodeConfiguration, VersionInfo.UNKNOWN) + node.stop() + + // Assert + Assert.assertTrue(Retransform.getInstrumentor().isExcluded("net.corda.node.internal.QuasarExcludePackagesTest.Test")) + } + + class ResourceMissingException(message: String) : Exception(message) + + private fun getConfig(cfgName: String): Config { + val resource = this::class.java.classLoader.getResource(cfgName) ?: throw ResourceMissingException("Resource not found") + val path = resource.toPath() + return ConfigHelper.loadConfig(path.parent, path) + } +} diff --git a/node/src/test/resources/test-config-quasarexcludepackages.conf b/node/src/test/resources/test-config-quasarexcludepackages.conf new file mode 100644 index 0000000000..512a2a228c --- /dev/null +++ b/node/src/test/resources/test-config-quasarexcludepackages.conf @@ -0,0 +1,33 @@ +myLegalName = "O=Alice Corp, L=Madrid, C=ES" +emailAddress = "admin@company.com" +keyStorePassword = "cordacadevpass" +trustStorePassword = "trustpass" +crlCheckSoftFail = true +baseDirectory = "/opt/corda" +cordappDirectories = ["./myCorDapps1", "./myCorDapps2"] +dataSourceProperties = { + dataSourceClassName = org.h2.jdbcx.JdbcDataSource + dataSource.url = "jdbc:h2:file:blah" + dataSource.user = "sa" + dataSource.password = "" +} +database = { + transactionIsolationLevel = "REPEATABLE_READ" + exportHibernateJMXStatistics = "false" +} +p2pAddress = "localhost:2233" +h2port = 0 +useTestClock = false +verifierType = InMemory +rpcSettings = { + address = "locahost:3418" + adminAddress = "localhost:3419" + useSsl = false + standAloneBroker = false +} +flowTimeout { + timeout = 30 seconds + maxRestartCount = 3 + backoffBase = 2.0 +} +quasarExcludePackages = [ "net.corda.node.internal.QuasarExcludePackagesTest**" ] diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt index 1e7e81c2f5..464e08966f 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalMockNetwork.kt @@ -461,6 +461,7 @@ open class InternalMockNetwork(cordappPackages: List = emptyList(), doReturn(makeTestDataSourceProperties("node_${id}_net_$networkId")).whenever(it).dataSourceProperties doReturn(emptyList()).whenever(it).extraNetworkMapKeys doReturn(listOf(baseDirectory / "cordapps")).whenever(it).cordappDirectories + doReturn(emptyList()).whenever(it).quasarExcludePackages parameters.configOverrides(it) } From 8ea6564cb9ea30b52b8daabf4ada464e58be04ec Mon Sep 17 00:00:00 2001 From: Walter Oggioni <6357328+woggioni@users.noreply.github.com> Date: Mon, 9 Mar 2020 14:05:28 +0000 Subject: [PATCH 79/83] fixed unformatted and hardcoded link (#6035) --- docs/source/generating-a-node.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/generating-a-node.rst b/docs/source/generating-a-node.rst index 14a2dc9c82..98d9bdcf46 100644 --- a/docs/source/generating-a-node.rst +++ b/docs/source/generating-a-node.rst @@ -12,7 +12,7 @@ A node can be created manually by creating a folder that contains the following * A folder entitled ``cordapps`` containing any CorDapp JARs you want the node to load -* An up-to-date version of the ``network-parameters`` file ([see docs:](https://docs.corda.net/network-map.html#network-parameters)) generated by the bootstrapper tool +* An up-to-date version of the ``network-parameters`` file (:ref:`see docs `) generated by the bootstrapper tool * **Optional:** A webserver JAR entitled ``corda-webserver-|corda_version|.jar`` that will connect to the node via RPC From dcf659e6432465e64e44cb8af21ae0451b50b32d Mon Sep 17 00:00:00 2001 From: Dimos Raptis Date: Mon, 9 Mar 2020 17:09:13 +0000 Subject: [PATCH 80/83] [CORDA-3628] - Implement sendAll API (#5990) * [CORDA-3628] - Implement sendAll API * detekt * Some minor refactorings and docs * Eliminate warnings * Address Rick's comments * Switch sendAll to use a set --- .../kotlin/net/corda/core/flows/FlowLogic.kt | 48 ++++ docs/source/api-flows.rst | 12 +- docs/source/api-persistence.rst | 1 + .../services/messaging/MessagingExecutor.kt | 5 + .../node/services/statemachine/Action.kt | 11 + .../statemachine/ActionExecutorImpl.kt | 8 + .../services/statemachine/FlowMessaging.kt | 19 +- .../transitions/StartedFlowTransition.kt | 80 +++--- .../statemachine/FlowFrameworkTests.kt | 12 +- .../FlowParallelMessagingTests.kt | 261 ++++++++++++++++++ 10 files changed, 413 insertions(+), 44 deletions(-) create mode 100644 node/src/test/kotlin/net/corda/node/services/statemachine/FlowParallelMessagingTests.kt diff --git a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt index 8f97963deb..6a45d1b0b6 100644 --- a/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt +++ b/core/src/main/kotlin/net/corda/core/flows/FlowLogic.kt @@ -25,6 +25,7 @@ import net.corda.core.node.NodeInfo import net.corda.core.node.ServiceHub import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.SerializationDefaults +import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.serialize import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker @@ -317,6 +318,53 @@ abstract class FlowLogic { return castMapValuesToKnownType(receiveAllMap(associateSessionsToReceiveType(receiveType, sessions))) } + /** + * Queues the given [payload] for sending to the provided [sessions] and continues without suspending. + * + * Note that the other parties may receive the message at some arbitrary later point or not at all: if one of the provided [sessions] + * is offline then message delivery will be retried until the corresponding node comes back or until the message is older than the + * network's event horizon time. + * + * @param payload the payload to send. + * @param sessions the sessions to send the provided payload to. + * @param maySkipCheckpoint whether checkpointing should be skipped. + */ + @Suspendable + @JvmOverloads + fun sendAll(payload: Any, sessions: Set, maySkipCheckpoint: Boolean = false) { + val sessionToPayload = sessions.map { it to payload }.toMap() + return sendAll(sessionToPayload, maySkipCheckpoint) + } + + /** + * Queues the given payloads for sending to the provided sessions and continues without suspending. + * + * Note that the other parties may receive the message at some arbitrary later point or not at all: if one of the provided [sessions] + * is offline then message delivery will be retried until the corresponding node comes back or until the message is older than the + * network's event horizon time. + * + * @param payloadsPerSession a mapping that contains the payload to be sent to each session. + * @param maySkipCheckpoint whether checkpointing should be skipped. + */ + @Suspendable + @JvmOverloads + fun sendAll(payloadsPerSession: Map, maySkipCheckpoint: Boolean = false) { + val request = FlowIORequest.Send( + sessionToMessage = serializePayloads(payloadsPerSession) + ) + stateMachine.suspend(request, maySkipCheckpoint) + } + + @Suspendable + private fun serializePayloads(payloadsPerSession: Map): Map> { + val cachedSerializedPayloads = mutableMapOf>() + + return payloadsPerSession.mapValues { (_, payload) -> + cachedSerializedPayloads[payload] ?: payload.serialize(context = SerializationDefaults.P2P_CONTEXT).also { cachedSerializedPayloads[payload] = it } + } + } + + /** * Invokes the given subflow. This function returns once the subflow completes successfully with the result * returned by that subflow's [call] method. If the subflow has a progress tracker, it is attached to the diff --git a/docs/source/api-flows.rst b/docs/source/api-flows.rst index 27de7b5c28..257690b290 100644 --- a/docs/source/api-flows.rst +++ b/docs/source/api-flows.rst @@ -264,14 +264,18 @@ In order to create a communication session between your initiator flow and the r * ``sendAndReceive(receiveType: Class, payload: Any): R`` * Sends the ``payload`` object and receives an object of type ``receiveType`` back -In addition ``FlowLogic`` provides functions that batch receives: +In addition ``FlowLogic`` provides functions that can receive messages from multiple sessions and send messages to multiple sessions: * ``receiveAllMap(sessions: Map>): Map>`` - Receives from all ``FlowSession`` objects specified in the passed in map. The received types may differ. + * Receives from all ``FlowSession`` objects specified in the passed in map. The received types may differ. * ``receiveAll(receiveType: Class, sessions: List): List>`` - Receives from all ``FlowSession`` objects specified in the passed in list. The received types must be the same. + * Receives from all ``FlowSession`` objects specified in the passed in list. The received types must be the same. +* ``sendAll(payload: Any, sessions: Set)`` + * Sends the ``payload`` object to all the provided ``FlowSession``\s. +* ``sendAll(payloadsPerSession: Map)`` + * Sends a potentially different payload to each ``FlowSession``, as specified by the provided ``payloadsPerSession``. -The batched functions are implemented more efficiently by the flow framework. +.. note:: It's more efficient to call ``sendAndReceive`` instead of calling ``send`` and then ``receive``. It's also more efficient to call ``sendAll``/``receiveAll`` instead of multiple ``send``/``receive`` respectively. InitiateFlow ~~~~~~~~~~~~ diff --git a/docs/source/api-persistence.rst b/docs/source/api-persistence.rst index e9442dff03..ab2a745671 100644 --- a/docs/source/api-persistence.rst +++ b/docs/source/api-persistence.rst @@ -460,6 +460,7 @@ Please note that suspendable flow operations such as: * ``FlowSession.send`` * ``FlowSession.receive`` * ``FlowLogic.receiveAll`` +* ``FlowLogic.sendAll`` * ``FlowLogic.sleep`` * ``FlowLogic.subFlow`` diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/MessagingExecutor.kt b/node/src/main/kotlin/net/corda/node/services/messaging/MessagingExecutor.kt index 2f46010730..96a75c6e59 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/MessagingExecutor.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/MessagingExecutor.kt @@ -53,6 +53,11 @@ class MessagingExecutor( producer.send(SimpleString(mqAddress), artemisMessage) } + @Synchronized + fun send(messages: Map) { + messages.forEach { recipients, message -> send(message, recipients) } + } + @Synchronized fun acknowledge(message: ClientMessage) { log.debug { diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/Action.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/Action.kt index 925fdb27e1..14f8229fec 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/Action.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/Action.kt @@ -37,6 +37,17 @@ sealed class Action { val deduplicationId: SenderDeduplicationId ) : Action() + /** + * Send session messages to multiple destinations. + * + * @property sendInitial session messages to send in order to establish a session. + * @property sendExisting session messages to send to existing sessions. + */ + data class SendMultiple( + val sendInitial: List, + val sendExisting: List + ): Action() + /** * Persist the specified [checkpoint]. */ diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/ActionExecutorImpl.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/ActionExecutorImpl.kt index 3ffdd4b709..9af0caab1f 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/ActionExecutorImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/ActionExecutorImpl.kt @@ -68,6 +68,7 @@ class ActionExecutorImpl( is Action.RemoveCheckpoint -> executeRemoveCheckpoint(action) is Action.SendInitial -> executeSendInitial(action) is Action.SendExisting -> executeSendExisting(action) + is Action.SendMultiple -> executeSendMultiple(action) is Action.AddSessionBinding -> executeAddSessionBinding(action) is Action.RemoveSessionBindings -> executeRemoveSessionBindings(action) is Action.SignalFlowHasStarted -> executeSignalFlowHasStarted(action) @@ -191,6 +192,13 @@ class ActionExecutorImpl( flowMessaging.sendSessionMessage(action.peerParty, action.message, action.deduplicationId) } + @Suspendable + private fun executeSendMultiple(action: Action.SendMultiple) { + val messages = action.sendInitial.map { Message(it.destination, it.initialise, it.deduplicationId) } + + action.sendExisting.map { Message(it.peerParty, it.message, it.deduplicationId) } + flowMessaging.sendSessionMessages(messages) + } + @Suspendable private fun executeAddSessionBinding(action: Action.AddSessionBinding) { stateMachineManager.addSessionBinding(action.flowId, action.sessionId) diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowMessaging.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowMessaging.kt index 8963c7616f..da371d6d25 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/FlowMessaging.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/FlowMessaging.kt @@ -13,6 +13,7 @@ import net.corda.core.utilities.contextLogger import net.corda.core.utilities.trace import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.messaging.DeduplicationHandler +import net.corda.node.services.messaging.MessagingService import net.corda.node.services.messaging.ReceivedMessage import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2PMessagingHeaders import java.io.NotSerializableException @@ -27,12 +28,17 @@ interface FlowMessaging { @Suspendable fun sendSessionMessage(destination: Destination, message: SessionMessage, deduplicationId: SenderDeduplicationId) + @Suspendable + fun sendSessionMessages(messageData: List) + /** * Start the messaging using the [onMessage] message handler. */ fun start(onMessage: (ReceivedMessage, deduplicationHandler: DeduplicationHandler) -> Unit) } +data class Message(val destination: Destination, val sessionMessage: SessionMessage, val dedupId: SenderDeduplicationId) + /** * Implementation of [FlowMessaging] using a [ServiceHubInternal] to do the messaging and routing. */ @@ -51,6 +57,17 @@ class FlowMessagingImpl(val serviceHub: ServiceHubInternal): FlowMessaging { @Suspendable override fun sendSessionMessage(destination: Destination, message: SessionMessage, deduplicationId: SenderDeduplicationId) { + val addressedMessage = createMessage(destination, message, deduplicationId) + serviceHub.networkService.send(addressedMessage.message, addressedMessage.target, addressedMessage.sequenceKey) + } + + @Suspendable + override fun sendSessionMessages(messageData: List) { + val addressedMessages = messageData.map { createMessage(it.destination, it.sessionMessage, it.dedupId) } + serviceHub.networkService.send(addressedMessages) + } + + private fun createMessage(destination: Destination, message: SessionMessage, deduplicationId: SenderDeduplicationId): MessagingService.AddressedMessage { val party = if (destination is Party) { log.trace { "Sending message $deduplicationId $message to $destination" } destination @@ -69,7 +86,7 @@ class FlowMessagingImpl(val serviceHub: ServiceHubInternal): FlowMessaging { is InitialSessionMessage -> message.initiatorSessionId is ExistingSessionMessage -> message.recipientSessionId } - serviceHub.networkService.send(networkMessage, address, sequenceKey = sequenceKey) + return MessagingService.AddressedMessage(networkMessage, address, sequenceKey) } private fun SessionMessage.additionalHeaders(target: Party): Map { diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/transitions/StartedFlowTransition.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/transitions/StartedFlowTransition.kt index 3269a87a2f..6f9956b692 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/transitions/StartedFlowTransition.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/transitions/StartedFlowTransition.kt @@ -7,6 +7,7 @@ import net.corda.core.internal.FlowIORequest import net.corda.core.serialization.SerializedBytes import net.corda.core.utilities.toNonEmptySet import net.corda.node.services.statemachine.* +import java.lang.IllegalStateException /** * This transition describes what should happen with a specific [FlowIORequest]. Note that at this time the request @@ -246,46 +247,49 @@ class StartedFlowTransition( val checkpoint = startingState.checkpoint val newSessions = LinkedHashMap(checkpoint.sessions) var index = 0 - for ((sourceSessionId, message) in sourceSessionIdToMessage) { - val existingSessionState = checkpoint.sessions[sourceSessionId] - if (existingSessionState == null) { - return freshErrorTransition(CannotFindSessionException(sourceSessionId)) - } else { - val sessionMessage = DataSessionMessage(message) - val deduplicationId = DeduplicationId.createForNormal(checkpoint, index++, existingSessionState) - when (existingSessionState) { - is SessionState.Uninitiated -> { - val initialMessage = createInitialSessionMessage(existingSessionState.initiatingSubFlow, sourceSessionId, existingSessionState.additionalEntropy, message) - actions.add(Action.SendInitial(existingSessionState.destination, initialMessage, SenderDeduplicationId(deduplicationId, startingState.senderUUID))) - newSessions[sourceSessionId] = SessionState.Initiating( - bufferedMessages = emptyList(), - rejectionError = null, - deduplicationSeed = existingSessionState.deduplicationSeed - ) - Unit - } - is SessionState.Initiating -> { - // We're initiating this session, buffer the message - val newBufferedMessages = existingSessionState.bufferedMessages + Pair(deduplicationId, sessionMessage) - newSessions[sourceSessionId] = existingSessionState.copy(bufferedMessages = newBufferedMessages) - } - is SessionState.Initiated -> { - when (existingSessionState.initiatedState) { - is InitiatedSessionState.Live -> { - val sinkSessionId = existingSessionState.initiatedState.peerSinkSessionId - val existingMessage = ExistingSessionMessage(sinkSessionId, sessionMessage) - actions.add(Action.SendExisting(existingSessionState.peerParty, existingMessage, SenderDeduplicationId(deduplicationId, startingState.senderUUID))) - Unit - } - InitiatedSessionState.Ended -> { - return freshErrorTransition(IllegalStateException("Tried to send to ended session $sourceSessionId")) - } - } - } - } + for ((sourceSessionId, _) in sourceSessionIdToMessage) { + val existingSessionState = checkpoint.sessions[sourceSessionId] ?: return freshErrorTransition(CannotFindSessionException(sourceSessionId)) + if (existingSessionState is SessionState.Initiated && existingSessionState.initiatedState is InitiatedSessionState.Ended) { + return freshErrorTransition(IllegalStateException("Tried to send to ended session $sourceSessionId")) } - } + + val messagesByType = sourceSessionIdToMessage.toList() + .map { (sourceSessionId, message) -> Triple(sourceSessionId, checkpoint.sessions[sourceSessionId]!!, message) } + .groupBy { it.second::class } + + val sendInitialActions = messagesByType[SessionState.Uninitiated::class]?.map { (sourceSessionId, sessionState, message) -> + val uninitiatedSessionState = sessionState as SessionState.Uninitiated + val deduplicationId = DeduplicationId.createForNormal(checkpoint, index++, sessionState) + val initialMessage = createInitialSessionMessage(uninitiatedSessionState.initiatingSubFlow, sourceSessionId, uninitiatedSessionState.additionalEntropy, message) + newSessions[sourceSessionId] = SessionState.Initiating( + bufferedMessages = emptyList(), + rejectionError = null, + deduplicationSeed = uninitiatedSessionState.deduplicationSeed + ) + Action.SendInitial(uninitiatedSessionState.destination, initialMessage, SenderDeduplicationId(deduplicationId, startingState.senderUUID)) + } ?: emptyList() + messagesByType[SessionState.Initiating::class]?.forEach { (sourceSessionId, sessionState, message) -> + val initiatingSessionState = sessionState as SessionState.Initiating + val sessionMessage = DataSessionMessage(message) + val deduplicationId = DeduplicationId.createForNormal(checkpoint, index++, initiatingSessionState) + val newBufferedMessages = initiatingSessionState.bufferedMessages + Pair(deduplicationId, sessionMessage) + newSessions[sourceSessionId] = initiatingSessionState.copy(bufferedMessages = newBufferedMessages) + } + val sendExistingActions = messagesByType[SessionState.Initiated::class]?.mapNotNull {(_, sessionState, message) -> + val initiatedSessionState = sessionState as SessionState.Initiated + if (initiatedSessionState.initiatedState !is InitiatedSessionState.Live) + null + else { + val sessionMessage = DataSessionMessage(message) + val deduplicationId = DeduplicationId.createForNormal(checkpoint, index++, initiatedSessionState) + val sinkSessionId = initiatedSessionState.initiatedState.peerSinkSessionId + val existingMessage = ExistingSessionMessage(sinkSessionId, sessionMessage) + Action.SendExisting(initiatedSessionState.peerParty, existingMessage, SenderDeduplicationId(deduplicationId, startingState.senderUUID)) + } + } ?: emptyList() + + actions.add(Action.SendMultiple(sendInitialActions, sendExistingActions)) currentState = currentState.copy(checkpoint = checkpoint.copy(sessions = newSessions)) } 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 a4f77c5390..ce92f2954b 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 @@ -536,6 +536,16 @@ class FlowFrameworkTests { assertThat(result.getOrThrow()).isEqualTo("HelloHello") } + @Test(timeout=300_000) + fun `initiating flow with anonymous party at the same node`() { + val anonymousBob = bobNode.services.keyManagementService.freshKeyAndCert(bobNode.info.legalIdentitiesAndCerts.single(), false) + val bobResponderFlow = bobNode.registerCordappFlowFactory(SendAndReceiveFlow::class) { SingleInlinedSubFlow(it) } + val result = bobNode.services.startFlow(SendAndReceiveFlow(anonymousBob.party.anonymise(), "Hello")).resultFuture + mockNet.runNetwork() + bobResponderFlow.getOrThrow() + assertThat(result.getOrThrow()).isEqualTo("HelloHello") + } + //region Helpers private val normalEnd = ExistingSessionMessage(SessionId(0), EndSessionMessage) // NormalSessionEnd(0) @@ -888,4 +898,4 @@ internal class ExceptionFlow(val exception: () -> E) : FlowLogic< exceptionThrown = exception() throw exceptionThrown } -} +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowParallelMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowParallelMessagingTests.kt new file mode 100644 index 0000000000..53f7d588ea --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowParallelMessagingTests.kt @@ -0,0 +1,261 @@ +package net.corda.node.services.statemachine + +import co.paralleluniverse.fibers.Suspendable +import net.corda.core.flows.Destination +import net.corda.core.flows.FlowException +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.FlowSession +import net.corda.core.flows.InitiatedBy +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.StartableByRPC +import net.corda.core.flows.UnexpectedFlowEndException +import net.corda.core.identity.Party +import net.corda.core.identity.PartyAndCertificate +import net.corda.core.serialization.CordaSerializable +import net.corda.core.utilities.getOrThrow +import net.corda.core.utilities.unwrap +import net.corda.testing.core.ALICE_NAME +import net.corda.testing.core.singleIdentity +import net.corda.testing.node.internal.InternalMockNetwork +import net.corda.testing.node.internal.InternalMockNodeParameters +import net.corda.testing.node.internal.TestStartedNode +import net.corda.testing.node.internal.enclosedCordapp +import net.corda.testing.node.internal.startFlow +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.AfterClass +import org.junit.BeforeClass +import org.junit.Test +import kotlin.test.assertEquals + +class FlowParallelMessagingTests { + + companion object { + + private lateinit var mockNet: InternalMockNetwork + private lateinit var senderNode: TestStartedNode + private lateinit var recipientNode1: TestStartedNode + private lateinit var recipientNode2: TestStartedNode + private lateinit var notaryIdentity: Party + private lateinit var senderParty: Party + private lateinit var recipientParty1: Party + private lateinit var recipientParty2: Party + + @BeforeClass + @JvmStatic + fun setup() { + mockNet = InternalMockNetwork( + cordappsForAllNodes = listOf(enclosedCordapp()) + ) + + senderNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME.copy(organisation = "SenderNode"))) + recipientNode1 = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME.copy(organisation = "RecipientNode1"))) + recipientNode2 = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME.copy(organisation = "RecipientNode2"))) + + notaryIdentity = mockNet.defaultNotaryIdentity + senderParty = senderNode.info.singleIdentity() + recipientParty1 = recipientNode1.info.singleIdentity() + recipientParty2 = recipientNode2.info.singleIdentity() + } + + @AfterClass + @JvmStatic + fun cleanUp() { + mockNet.stopNodes() + } + } + + + @Test(timeout=300_000) + fun `messages can be exchanged in parallel using sendAll & receiveAll between multiple parties successfully`() { + val messages = mapOf( + recipientParty1 to MessageType.REPLY, + recipientParty2 to MessageType.REPLY + ) + val flow = senderNode.services.startFlow(SenderFlow(messages)) + + mockNet.runNetwork() + val result = flow.resultFuture.getOrThrow() + + assertEquals("ok", result) + } + + @Test(timeout=300_000) + fun `flow exceptions from counterparties during receiveAll are handled properly`() { + val messages = mapOf( + recipientParty1 to MessageType.REPLY, + recipientParty2 to MessageType.GRACEFUL_FAILURE + ) + val flow = senderNode.services.startFlow(SenderFlow(messages)) + + mockNet.runNetwork() + assertThatThrownBy{ flow.resultFuture.getOrThrow() } + .isInstanceOf(FlowException::class.java) + .hasMessage("graceful failure") + } + + @Test(timeout=300_000) + fun `runtime exceptions from counterparties during receiveAll are handled properly`() { + val messages = mapOf( + recipientParty1 to MessageType.REPLY, + recipientParty2 to MessageType.CRASH + ) + val flow = senderNode.services.startFlow(SenderFlow(messages)) + + mockNet.runNetwork() + assertThatThrownBy{ flow.resultFuture.getOrThrow() } + .isInstanceOf(UnexpectedFlowEndException::class.java) + } + + @Test(timeout=300_000) + fun `initial session messages and existing session messages can be sent together using sendAll`() { + val flow = senderNode.services.startFlow(StagedSenderFlow(listOf(recipientParty1, recipientParty2))) + + mockNet.runNetwork() + val result = flow.resultFuture.getOrThrow() + + assertEquals("ok", result) + } + + @Test(timeout=300_000) + fun `messages can be exchanged successfully even between anonymous parties`() { + val senderAnonymousParty = senderNode.createConfidentialIdentity(senderParty) + val firstRecipientAnonymousParty = recipientNode1.createConfidentialIdentity(recipientParty1) + senderNode.verifyAndRegister(firstRecipientAnonymousParty) + val secondRecipientAnonymousParty = recipientNode2.createConfidentialIdentity(recipientParty2) + senderNode.verifyAndRegister(secondRecipientAnonymousParty) + + val messages = mapOf( + senderAnonymousParty.party.anonymise() to MessageType.REPLY, + firstRecipientAnonymousParty.party.anonymise() to MessageType.REPLY, + secondRecipientAnonymousParty.party.anonymise() to MessageType.REPLY + ) + + val flow = senderNode.services.startFlow(SenderFlow(messages)) + + mockNet.runNetwork() + val result = flow.resultFuture.getOrThrow() + + assertEquals("ok", result) + } + + @Test(timeout=300_000) + fun `a flow cannot invoke receiveAll with duplicate sessions`() { + val flow = senderNode.services.startFlow(InvalidReceiveFlow(listOf(recipientParty1), String::class.java)) + + mockNet.runNetwork() + + assertThatThrownBy{ flow.resultFuture.getOrThrow() } + .isInstanceOf(java.lang.IllegalArgumentException::class.java) + .hasMessage("A flow session can only appear once as argument.") + } + + fun TestStartedNode.createConfidentialIdentity(party: Party) = + services.keyManagementService.freshKeyAndCert(services.myInfo.legalIdentitiesAndCerts.single { it.name == party.name }, false) + + fun TestStartedNode.verifyAndRegister(identity: PartyAndCertificate) = + services.identityService.verifyAndRegisterIdentity(identity) + + @StartableByRPC + @InitiatingFlow + class SenderFlow(private val parties: Map): FlowLogic() { + @Suspendable + override fun call(): String { + val messagesPerSession = parties.toList().map { (party, messageType) -> + val session = initiateFlow(party) + Pair(session, messageType) + }.toMap() + + sendAll(messagesPerSession) + val messages = receiveAll(String::class.java, messagesPerSession.keys.toList()) + + messages.map { it.unwrap { payload -> assertEquals("pong", payload) } } + + return "ok" + } + } + + @Suppress("TooGenericExceptionThrown") + @InitiatedBy(SenderFlow::class) + class RecipientFlow(private val otherPartySession: FlowSession): FlowLogic() { + @Suspendable + override fun call(): String { + val msg = otherPartySession.receive().unwrap { it } + when (msg) { + MessageType.REPLY -> otherPartySession.send("pong") + MessageType.GRACEFUL_FAILURE -> throw FlowException("graceful failure") + MessageType.CRASH -> throw RuntimeException("crash") + } + + return "ok" + } + } + + @StartableByRPC + @InitiatingFlow + class StagedSenderFlow(private val parties: List): FlowLogic() { + @Suspendable + override fun call(): String { + if (parties.size < 2) { + throw IllegalArgumentException("at least two parties required for staged execution") + } + + val sessions = parties.map { initiateFlow(it) }.toSet() + + sessions.first().send(StagedMessageType.INITIAL_RECIPIENT) + sessions.first().receive().unwrap{ payload -> assertEquals("pong", payload) } + + sendAll(StagedMessageType.REGULAR_RECIPIENT, sessions) + val messages = receiveAll(String::class.java, sessions.toList()) + + messages.map { it.unwrap { payload -> assertEquals("pong", payload) } } + + return "ok" + } + } + + @InitiatedBy(StagedSenderFlow::class) + class StagedRecipientFlow(private val otherPartySession: FlowSession): FlowLogic() { + @Suspendable + override fun call(): String { + val msg = otherPartySession.receive().unwrap { it } + when (msg) { + StagedMessageType.INITIAL_RECIPIENT -> { + otherPartySession.send("pong") + otherPartySession.receive().unwrap { payload -> assertEquals(StagedMessageType.REGULAR_RECIPIENT, payload) } + otherPartySession.send("pong") + } + StagedMessageType.REGULAR_RECIPIENT -> otherPartySession.send("pong") + } + + return "ok" + } + } + + @StartableByRPC + @InitiatingFlow + class InvalidReceiveFlow(private val parties: List, private val payloadType: Class): FlowLogic() { + @Suspendable + override fun call(): String { + val sessions = parties.flatMap { party -> + val session = initiateFlow(party) + listOf(session, session) + } + receiveAll(payloadType, sessions) + return "ok" + } + } + + @CordaSerializable + enum class MessageType { + REPLY, + GRACEFUL_FAILURE, + CRASH + } + + @CordaSerializable + enum class StagedMessageType { + INITIAL_RECIPIENT, + REGULAR_RECIPIENT + } + +} \ No newline at end of file From a18c544de1a3ada23aac5d4aea2b85cf048f753a Mon Sep 17 00:00:00 2001 From: Joseph Zuniga-Daly Date: Mon, 9 Mar 2020 17:17:36 +0000 Subject: [PATCH 81/83] Don't stop the node inside the unit test The test node had not been started and is causing tests to fail. --- .../net/corda/node/internal/QuasarExcludePackagesTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/node/src/test/kotlin/net/corda/node/internal/QuasarExcludePackagesTest.kt b/node/src/test/kotlin/net/corda/node/internal/QuasarExcludePackagesTest.kt index bd6def9c21..4b9feece3a 100644 --- a/node/src/test/kotlin/net/corda/node/internal/QuasarExcludePackagesTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/QuasarExcludePackagesTest.kt @@ -47,8 +47,7 @@ class QuasarExcludePackagesTest { val nodeConfiguration :NodeConfiguration = V1NodeConfigurationSpec.parse(config).value() // Act - val node = Node(nodeConfiguration, VersionInfo.UNKNOWN) - node.stop() + Node(nodeConfiguration, VersionInfo.UNKNOWN) // Assert Assert.assertTrue(Retransform.getInstrumentor().isExcluded("net.corda.node.internal.QuasarExcludePackagesTest.Test")) From 35f7cd349fe59ba5344263a138ef96ebe26aee10 Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Mon, 9 Mar 2020 21:26:53 +0000 Subject: [PATCH 82/83] CORDA-3644: Add Corda-Testing tag to core-test-utils manifest. --- testing/core-test-utils/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testing/core-test-utils/build.gradle b/testing/core-test-utils/build.gradle index c58fe75c7d..5d44264336 100644 --- a/testing/core-test-utils/build.gradle +++ b/testing/core-test-utils/build.gradle @@ -16,6 +16,11 @@ dependencies { jar { baseName 'corda-core-test-utils' + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } } publish { From 67e3e09a4edb41d17b8adfa669d671c690c5db33 Mon Sep 17 00:00:00 2001 From: Dimitris Gounaris <17044221+dgounaris@users.noreply.github.com> Date: Wed, 11 Mar 2020 10:51:38 +0000 Subject: [PATCH 83/83] TM-209: Dependx introduction for test running optimization (#6036) * Add dependx step to build, in monitor state * Bump plugin version * Update plugin version Co-authored-by: Dimitris Gounaris --- Jenkinsfile | 4 ++++ build.gradle | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index fa039b4fdc..ee46820d74 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -45,6 +45,8 @@ pipeline { "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + "-Dgit.branch=\"\${GIT_BRANCH}\" " + "-Dgit.target.branch=\"\${CHANGE_TARGET}\" " + + "-Ddependx.branch.origin=${env.GIT_COMMIT} " + + "-Ddependx.branch.target=${CHANGE_TARGET} " + " allParallelIntegrationTest --stacktrace" } } @@ -58,6 +60,8 @@ pipeline { "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + "-Dgit.branch=\"\${GIT_BRANCH}\" " + "-Dgit.target.branch=\"\${CHANGE_TARGET}\" " + + "-Ddependx.branch.origin=${env.GIT_COMMIT} " + + "-Ddependx.branch.target=${CHANGE_TARGET} " + " allParallelUnitTest --stacktrace" } } diff --git a/build.gradle b/build.gradle index 5a026b3d7a..e7c3f3cd24 100644 --- a/build.gradle +++ b/build.gradle @@ -187,6 +187,7 @@ buildscript { // See https://github.com/corda/gradle-capsule-plugin classpath "us.kirchmeier:gradle-capsule-plugin:1.0.4_r3" classpath group: "com.r3.testing", name: "gradle-distributed-testing-plugin", version: "1.2-LOCAL-K8S-SHARED-CACHE-SNAPSHOT", changing: true + classpath group: "com.r3.dependx", name: "gradle-dependx", version: "0.1.12", changing: true classpath "com.bmuschko:gradle-docker-plugin:5.0.0" } } @@ -204,6 +205,7 @@ apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'maven-publish' apply plugin: 'com.jfrog.artifactory' apply plugin: "com.bmuschko.docker-remote-api" +apply plugin: "com.r3.dependx.dependxies" // If the command line project option -PversionFromGit is added to the gradle invocation, we'll resolve @@ -583,6 +585,11 @@ artifactory { } } +dependxiesModule { + mode = "monitor" + skipTasks = "test,integrationTest,smokeTest,slowIntegrationTest" +} + task generateApi(type: net.corda.plugins.GenerateApi) { baseName = "api-corda" } @@ -629,6 +636,7 @@ buildScan { } task allParallelIntegrationTest(type: ParallelTestGroup) { + dependsOn dependxiesModule podLogLevel PodLogLevel.INFO testGroups "integrationTest" numberOfShards 10 @@ -639,6 +647,7 @@ task allParallelIntegrationTest(type: ParallelTestGroup) { nodeTaints "big" } task allParallelUnitTest(type: ParallelTestGroup) { + dependsOn dependxiesModule podLogLevel PodLogLevel.INFO testGroups "test" numberOfShards 10 @@ -649,6 +658,7 @@ task allParallelUnitTest(type: ParallelTestGroup) { nodeTaints "small" } task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) { + dependsOn dependxiesModule testGroups "test", "integrationTest" numberOfShards 15 streamOutput false @@ -659,6 +669,7 @@ task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) { } task parallelRegressionTest(type: ParallelTestGroup) { testGroups "test", "integrationTest", "smokeTest" + dependsOn dependxiesModule numberOfShards 15 streamOutput false coresPerFork 2 @@ -668,6 +679,7 @@ task parallelRegressionTest(type: ParallelTestGroup) { } task allParallelSmokeTest(type: ParallelTestGroup) { testGroups "smokeTest" + dependsOn dependxiesModule numberOfShards 4 streamOutput false coresPerFork 6 @@ -677,6 +689,7 @@ task allParallelSmokeTest(type: ParallelTestGroup) { } task allParallelSlowIntegrationTest(type: ParallelTestGroup) { testGroups "slowIntegrationTest" + dependsOn dependxiesModule numberOfShards 4 streamOutput false coresPerFork 6