Merge branch 'release/os/4.12' into merge-release/os/4.11-release/os/4.12-2024-09-24-389

This commit is contained in:
Chris Cochrane 2024-09-25 10:06:18 +01:00 committed by GitHub
commit 42ec266ed6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
759 changed files with 14378 additions and 12305 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +0,0 @@
FROM azul/zulu-openjdk:11.0.14
RUN apt-get update && apt-get install -y curl apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-common \
wget
ARG USER="stresstester"
RUN useradd -m ${USER}

View File

@ -1,213 +0,0 @@
#!groovy
/**
* Jenkins pipeline to build Corda OS release with JDK11
*/
/**
* Kill already started job.
* Assume new commit takes precendence and results from previous
* unfinished builds are not required.
* This feature doesn't play well with disableConcurrentBuilds() option
*/
@Library('corda-shared-build-pipeline-steps')
import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
/**
* Sense environment
*/
boolean isReleaseTag = (env.TAG_NAME =~ /^release.*JDK11$/)
/**
* Common Gradle arguments for all Gradle executions
*/
String COMMON_GRADLE_PARAMS = [
'--no-daemon',
'--stacktrace',
'--info',
'-Pcompilation.warningsAsErrors=false',
'-Ptests.failFast=true',
].join(' ')
/**
* The name of subfolders to run tests previously on Another Agent and Same Agent
*/
String sameAgentFolder = 'sameAgent'
String anotherAgentFolder = 'anotherAgent'
pipeline {
agent {
dockerfile {
label 'standard'
additionalBuildArgs '--build-arg USER="${USER}"' // DON'T change quotation - USER variable is substituted by SHELL!!!!
filename "${sameAgentFolder}/.ci/dev/compatibility/DockerfileJDK11"
}
}
/*
* List options in alphabetical order
*/
options {
buildDiscarder(logRotator(daysToKeepStr: '14', artifactDaysToKeepStr: '14'))
checkoutToSubdirectory "${sameAgentFolder}"
parallelsAlwaysFailFast()
timeout(time: 6, unit: 'HOURS')
timestamps()
}
/*
* List environment variables in alphabetical order
*/
environment {
ARTIFACTORY_BUILD_NAME = "Corda :: Publish :: Publish JDK 11 Release to Artifactory :: ${env.BRANCH_NAME}"
ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials')
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
}
stages {
stage('Compile') {
steps {
dir(sameAgentFolder) {
authenticateGradleWrapper()
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
'clean',
'jar'
].join(' ')
}
}
}
stage('Copy') {
steps {
sh "rm -rf ${anotherAgentFolder} && mkdir -p ${anotherAgentFolder} && cd ${sameAgentFolder} && cp -aR . ../${anotherAgentFolder}"
}
}
stage('All Tests') {
parallel {
stage('Another agent') {
post {
always {
dir(anotherAgentFolder) {
archiveArtifacts artifacts: '**/*.log', fingerprint: false
junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true
}
}
}
stages {
stage('Unit Test') {
steps {
dir(anotherAgentFolder) {
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
'test'
].join(' ')
}
}
}
stage('Smoke Test') {
steps {
dir(anotherAgentFolder) {
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
'smokeTest'
].join(' ')
}
}
}
stage('Slow Integration Test') {
steps {
dir(anotherAgentFolder) {
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
'slowIntegrationTest'
].join(' ')
}
}
}
}
}
stage('Same agent') {
post {
always {
dir(sameAgentFolder) {
archiveArtifacts artifacts: '**/*.log', fingerprint: false
junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true
}
}
}
stages {
stage('Integration Test') {
steps {
dir(sameAgentFolder) {
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
'integrationTest'
].join(' ')
}
}
}
stage('Deploy Node') {
steps {
dir(sameAgentFolder) {
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
'deployNode'
].join(' ')
}
}
}
}
}
}
}
stage('Publish to Artifactory') {
when {
expression { isReleaseTag }
}
steps {
dir(sameAgentFolder) {
rtServer(
id: 'R3-Artifactory',
url: 'https://software.r3.com/artifactory',
credentialsId: 'artifactory-credentials'
)
rtGradleDeployer(
id: 'deployer',
serverId: 'R3-Artifactory',
repo: 'corda-releases'
)
rtGradleRun(
usesPlugin: true,
useWrapper: true,
switches: '-s --info',
tasks: 'artifactoryPublish',
deployerId: 'deployer',
buildName: env.ARTIFACTORY_BUILD_NAME
)
rtPublishBuildInfo(
serverId: 'R3-Artifactory',
buildName: env.ARTIFACTORY_BUILD_NAME
)
}
}
}
}
post {
cleanup {
deleteDir() /* clean up our workspace */
}
}
}

View File

@ -1,58 +0,0 @@
#!groovy
/**
* Jenkins pipeline to build Corda Opensource Pull Requests with JDK11.
*/
@Library('corda-shared-build-pipeline-steps')
import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
pipeline {
agent {
dockerfile {
label 'standard'
additionalBuildArgs '--build-arg USER="${USER}"' // DON'T change quotation - USER variable is substituted by SHELL!!!!
filename '.ci/dev/compatibility/DockerfileJDK11'
}
}
options {
timestamps()
timeout(time: 3, unit: 'HOURS')
buildDiscarder(logRotator(daysToKeepStr: '14', artifactDaysToKeepStr: '14'))
}
environment {
ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials')
BUILD_CACHE_CREDENTIALS = credentials('gradle-ent-cache-credentials')
BUILD_CACHE_PASSWORD = "${env.BUILD_CACHE_CREDENTIALS_PSW}"
BUILD_CACHE_USERNAME = "${env.BUILD_CACHE_CREDENTIALS_USR}"
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
CORDA_GRADLE_SCAN_KEY = credentials('gradle-build-scans-key')
CORDA_USE_CACHE = "corda-remotes"
}
stages {
stage('JDK 11 Compile') {
steps {
authenticateGradleWrapper()
sh "./gradlew --no-daemon --parallel --build-cache -Pcompilation.allWarningsAsErrors=true -Ptests.failFast=false " +
"-Ptests.ignoreFailures=true clean compileAll --stacktrace"
}
}
stage('Deploy nodes') {
steps {
sh "./gradlew --no-daemon --build-cache deployNodes"
}
}
}
post {
always {
findBuildScans()
}
cleanup {
deleteDir() /* clean up our workspace */
}
}
}

View File

@ -13,13 +13,13 @@
* the branch name of origin branch, it should match the current branch * the branch name of origin branch, it should match the current branch
* and it acts as a fail-safe inside {@code forwardMerger} pipeline * and it acts as a fail-safe inside {@code forwardMerger} pipeline
*/ */
String originBranch = 'release/os/4.11' String originBranch = 'release/os/4.12'
/** /**
* the branch name of target branch, it should be the branch with the next version * the branch name of target branch, it should be the branch with the next version
* after the one in current branch. * after the one in current branch.
*/ */
String targetBranch = 'release/os/4.12' String targetBranch = 'release/os/4.13'
/** /**
* Forward merge any changes between #originBranch and #targetBranch * Forward merge any changes between #originBranch and #targetBranch

View File

@ -45,6 +45,8 @@ pipeline {
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}" CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}" CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
CORDA_USE_CACHE = "corda-remotes" CORDA_USE_CACHE = "corda-remotes"
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
JAVA_8_HOME = "/usr/lib/jvm/java-1.8.0-amazon-corretto"
} }
stages { stages {

View File

@ -15,48 +15,32 @@ pipeline {
* List environment variables in alphabetical order * List environment variables in alphabetical order
*/ */
environment { environment {
SNYK_API_TOKEN = credentials('c4-os-snyk-api-token-secret')
C4_OS_SNYK_ORG_ID = credentials('c4-os-snyk-org-id')
ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials') ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials')
BUILD_CACHE_CREDENTIALS = credentials('gradle-ent-cache-credentials')
BUILD_CACHE_PASSWORD = "${env.BUILD_CACHE_CREDENTIALS_PSW}"
BUILD_CACHE_USERNAME = "${env.BUILD_CACHE_CREDENTIALS_USR}"
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}" CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}" CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
CORDA_GRADLE_SCAN_KEY = credentials('gradle-build-scans-key')
CORDA_USE_CACHE = "corda-remotes" CORDA_USE_CACHE = "corda-remotes"
C4_OS_SNYK_ORG_ID = credentials('c4-os-snyk-org-id') JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
SNYK_API_TOKEN = credentials('c4-os-snyk-api-token-secret')
} }
stages { stages {
stage('Detekt check') { stage('Detekt check') {
steps { steps {
authenticateGradleWrapper() authenticateGradleWrapper()
sh "./gradlew --no-daemon --parallel --build-cache clean detekt" sh "./gradlew --no-daemon clean detekt"
} }
} }
stage('Compilation warnings check') { stage('Compilation warnings check') {
steps { steps {
sh "./gradlew --no-daemon --parallel --build-cache -Pcompilation.warningsAsErrors=true compileAll" sh "./gradlew --no-daemon -Pcompilation.warningsAsErrors=true compileAll"
} }
} }
stage('Snyk Delta') { stage('Snyk Delta') {
agent { agent { label 'standard' }
docker {
image 'build-zulu-openjdk:8'
reuseNode true
registryUrl 'https://engineering-docker.software.r3.com/'
registryCredentialsId 'artifactory-credentials'
args '-v /tmp:/host_tmp'
}
}
environment {
GRADLE_USER_HOME = "/host_tmp/gradle"
}
steps { steps {
authenticateGradleWrapper()
sh 'mkdir -p ${GRADLE_USER_HOME}'
authenticateGradleWrapper() authenticateGradleWrapper()
snykDeltaScan(env.SNYK_API_TOKEN, env.C4_OS_SNYK_ORG_ID) snykDeltaScan(env.SNYK_API_TOKEN, env.C4_OS_SNYK_ORG_ID)
} }
@ -64,21 +48,19 @@ pipeline {
stage('No API change check') { stage('No API change check') {
steps { steps {
sh "./gradlew --no-daemon --parallel --build-cache generateApi" sh "./gradlew --no-daemon generateApi"
sh ".ci/check-api-changes.sh" sh ".ci/check-api-changes.sh"
} }
} }
stage('Deploy Nodes') { stage('Deploy Nodes') {
steps { steps {
sh "./gradlew --no-daemon --build-cache jar deployNodes" sh "./gradlew --no-daemon jar deployNodes"
} }
} }
} }
post { post {
always {
findBuildScans()
}
cleanup { cleanup {
deleteDir() /* clean up our workspace */ deleteDir() /* clean up our workspace */
} }

View File

@ -27,6 +27,7 @@ pipeline {
ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials') ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials')
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}" CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}" CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
} }
stages { stages {

View File

@ -39,6 +39,7 @@ pipeline {
BUILD_CACHE_USERNAME = "${env.BUILD_CACHE_CREDENTIALS_USR}" BUILD_CACHE_USERNAME = "${env.BUILD_CACHE_CREDENTIALS_USR}"
USE_CACHE = 'corda-remotes' USE_CACHE = 'corda-remotes'
DOCKER_URL = "https://index.docker.io/v1/" DOCKER_URL = "https://index.docker.io/v1/"
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
} }
stages { stages {

View File

@ -24,6 +24,7 @@ pipeline {
// in the name // in the name
ARTIFACTORY_BUILD_NAME = "Corda / Publish / Publish Preview to Artifactory" ARTIFACTORY_BUILD_NAME = "Corda / Publish / Publish Preview to Artifactory"
.replaceAll("/", " :: ") .replaceAll("/", " :: ")
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
} }
stages { stages {

View File

@ -70,6 +70,8 @@ pipeline {
SNYK_API_KEY = "c4-os-snyk" //Jenkins credential type: Snyk Api token SNYK_API_KEY = "c4-os-snyk" //Jenkins credential type: Snyk Api token
SNYK_TOKEN = credentials('c4-os-snyk-api-token-secret') //Jenkins credential type: Secret text SNYK_TOKEN = credentials('c4-os-snyk-api-token-secret') //Jenkins credential type: Secret text
C4_OS_SNYK_ORG_ID = credentials('corda4-os-snyk-org-id') C4_OS_SNYK_ORG_ID = credentials('corda4-os-snyk-org-id')
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
JAVA_8_HOME = "/usr/lib/jvm/java-1.8.0-amazon-corretto"
} }
stages { stages {
@ -327,7 +329,7 @@ pipeline {
'./gradlew', './gradlew',
COMMON_GRADLE_PARAMS, COMMON_GRADLE_PARAMS,
'docker:pushDockerImage', 'docker:pushDockerImage',
'-Pdocker.image.repository=corda/community', '-Pdocker.image.repository=corda/open-source',
'--image OFFICIAL' '--image OFFICIAL'
].join(' ') ].join(' ')
} }

View File

@ -3,9 +3,9 @@
# PR Checklist: # PR Checklist:
- [ ] Have you run the unit, integration and smoke tests as described [here](https://docs.r3.com/en/platform/corda/4.8/open-source/testing.html)? - [ ] Have you run the unit, integration and smoke tests as described [here](https://docs.r3.com/testing.html)?
- [ ] If you added public APIs, did you write the JavaDocs/kdocs? - [ ] If you added public APIs, did you write the JavaDocs/kdocs?
- [ ] If the changes are of interest to application developers, have you added them to the changelog, and potentially the [release notes](https://docs.corda.net/head/release-notes.html) (`https://docs.r3.com/en/platform/corda/4.8/open-source/release-notes.html`)? - [ ] If the changes are of interest to application developers, have you added them to the changelog, and potentially the [release notes](https://docs.r3.com/release-notes.html) (`https://docs.r3.com/release-notes.html`)?
- [ ] If you are contributing for the first time, please read the [contributor agreement](https://docs.r3.com/en/platform/corda/4.8/open-source/contributing.html) now and add a comment to this pull request stating that your PR is in accordance with the [Developer's Certificate of Origin](https://docs.r3.com/en/platform/corda/4.8/open-source/contributing.html#merging-the-changes-back-into-corda). - [ ] If you are contributing for the first time, please read the [contributor agreement](https://docs.r3.com/contributing.html) now and add a comment to this pull request stating that your PR is in accordance with the [Developer's Certificate of Origin](https://docs.r3.com/contributing.html).
Thanks for your code, it's appreciated! :) Thanks for your code, it's appreciated! :)

View File

@ -9,6 +9,6 @@ jobs:
steps: steps:
- uses: morrisoncole/pr-lint-action@v1.7.1 - uses: morrisoncole/pr-lint-action@v1.7.1
with: with:
title-regex: '^((CORDA|AG|EG|ENT|INFRA|ES)-\d+)(.*)' title-regex: '^((CORDA|AG|EG|ENT|INFRA|ES|DOC)-\d+)(.*)'
on-failed-regex-comment: "PR title failed to match regex -> `%regex%`" on-failed-regex-comment: "PR title failed to match regex -> `%regex%`"
repo-token: "${{ secrets.GITHUB_TOKEN }}" repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@ -2,4 +2,4 @@
Corda is an open-source project and contributions are welcome! Corda is an open-source project and contributions are welcome!
To find out how to contribute, please see our [contributing docs](https://docs.r3.com/en/platform/corda/4.8/open-source/contributing.html). To find out how to contribute, please see our [contributing docs](https://docs.r3.com/contributing.html).

20
Jenkinsfile vendored
View File

@ -53,6 +53,8 @@ pipeline {
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}" CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
CORDA_GRADLE_SCAN_KEY = credentials('gradle-build-scans-key') CORDA_GRADLE_SCAN_KEY = credentials('gradle-build-scans-key')
CORDA_USE_CACHE = "corda-remotes" CORDA_USE_CACHE = "corda-remotes"
JAVA_HOME="/usr/lib/jvm/java-17-amazon-corretto"
JAVA_8_HOME = "/usr/lib/jvm/java-1.8.0-amazon-corretto"
} }
stages { stages {
@ -119,6 +121,24 @@ pipeline {
].join(' ') ].join(' ')
} }
} }
stage('Smoke Test') {
steps {
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
'smokeTest'
].join(' ')
}
}
stage('Slow Integration Test') {
steps {
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
'slowIntegrationTest'
].join(' ')
}
}
} }
} }
stage('Same agent') { stage('Same agent') {

View File

@ -1,5 +1,5 @@
<p align="center"> <p align="center">
<img src="https://www.corda.net/wp-content/themes/corda/assets/images/crda-logo-big.svg" alt="Corda" width="500"> <img src="https://corda.net/wp-content/uploads/2021/11/corda-logo.svg" alt="Corda" width="500">
</p> </p>
<a href="https://ci-master.corda.r3cev.com/viewType.html?buildTypeId=Corda_Build_ActiveReleaseBranches_BuildOsRelease45&tab=buildTypeStatusDiv&guest=1"><img src="https://ci.corda.r3cev.com/app/rest/builds/buildType:Corda_Build_ActiveReleaseBranches_BuildOsRelease45/statusIcon"/></a> [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) <a href="https://ci-master.corda.r3cev.com/viewType.html?buildTypeId=Corda_Build_ActiveReleaseBranches_BuildOsRelease45&tab=buildTypeStatusDiv&guest=1"><img src="https://ci.corda.r3cev.com/app/rest/builds/buildType:Corda_Build_ActiveReleaseBranches_BuildOsRelease45/statusIcon"/></a> [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
@ -25,27 +25,26 @@ However, like all things, Corda must evolve to serve the more stringent needs of
## Getting started ## Getting started
1. Read the [Getting Started](https://docs.corda.net/getting-set-up.html) documentation 1. Read the [Getting Started](https://docs.r3.com/getting-set-up.html) documentation
2. Run the [Example CorDapp](https://docs.corda.net/tutorial-cordapp.html) 2. Run the [Example CorDapp](https://docs.r3.com/tutorial-cordapp.html)
3. Read about Corda's [Key Concepts](https://docs.corda.net/key-concepts.html) 3. Read about Corda's [Key Concepts](https://docs.r3.com/key-concepts.html)
4. Follow the [Hello, World! tutorial](https://docs.corda.net/hello-world-introduction.html) 4. Follow the [Hello, World! tutorial](https://docs.r3.com/hello-world-introduction.html)
## Useful links ## Useful links
* [Project Website](https://corda.net) * [Project Website](https://corda.net)
* [Mailing List](https://groups.io/g/corda-dev/) * [Mailing List](https://groups.io/g/corda-dev/)
* [Documentation](https://docs.corda.net) * [Documentation](https://docs.r3.com)
* [Stack Overflow Tag](https://stackoverflow.com/questions/tagged/corda) * [Stack Overflow Tag](https://stackoverflow.com/questions/tagged/corda)
* [Slack Channel](https://slack.corda.net/) * [Slack Channel](https://slack.corda.net/)
* [Twitter](https://twitter.com/Cordablockchain) * [Twitter](https://twitter.com/inside_r3)
* [Meetups](https://www.meetup.com/pro/corda/) * [Training Courses](https://r3certification.com/)
* [Training Courses](https://www.corda.net/corda-training/)
## Contributing ## Contributing
Corda is an open-source project and contributions are welcome! Corda is an open-source project and contributions are welcome!
To find out how to contribute, please see our [contributing docs](https://docs.r3.com/en/platform/corda/4.8/open-source/contributing.html). To find out how to contribute, please see our [contributing docs](https://docs.r3.com/contributing.html).
## License ## License

View File

@ -1,8 +1,11 @@
import com.r3.testing.DistributeTestsBy import com.r3.testing.DistributeTestsBy
import com.r3.testing.PodLogLevel import com.r3.testing.PodLogLevel
import net.corda.plugins.apiscanner.GenerateApi
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import static org.gradle.api.JavaVersion.VERSION_11 import static org.gradle.api.JavaVersion.VERSION_17
import static org.gradle.api.JavaVersion.VERSION_1_8 import static org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
import static org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
buildscript { buildscript {
// For sharing constants between builds // For sharing constants between builds
@ -15,26 +18,18 @@ buildscript {
ext.corda_build_edition = System.getenv("CORDA_BUILD_EDITION")?.trim() ?: "Corda Open Source" ext.corda_build_edition = System.getenv("CORDA_BUILD_EDITION")?.trim() ?: "Corda Open Source"
ext.corda_platform_version = constants.getProperty("platformVersion") ext.corda_platform_version = constants.getProperty("platformVersion")
ext.corda_shell_version = constants.getProperty("cordaShellVersion")
ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion") ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion")
// Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things. // Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things.
// //
// TODO: Sort this alphabetically. // TODO: Sort this alphabetically.
ext.kotlin_version = constants.getProperty("kotlinVersion")
ext.warnings_as_errors = project.hasProperty("compilation.warningsAsErrors") ? project.property("compilation.warningsAsErrors").toBoolean() : false ext.warnings_as_errors = project.hasProperty("compilation.warningsAsErrors") ? project.property("compilation.warningsAsErrors").toBoolean() : false
ext.quasar_group = 'co.paralleluniverse' ext.quasar_group = 'co.paralleluniverse'
// Set version of Quasar according to version of Java used: // Set version of Quasar according to version of Java used:
if (JavaVersion.current().isJava8()) { ext.quasar_version = constants.getProperty("quasarVersion")
ext.quasar_version = constants.getProperty("quasarVersion") ext.quasar_classifier = constants.getProperty("quasarClassifier")
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 = [ ext.quasar_exclusions = [
'co.paralleluniverse**', 'co.paralleluniverse**',
'groovy**', 'groovy**',
@ -49,7 +44,7 @@ buildscript {
'org.junit**', 'org.junit**',
'org.slf4j**', 'org.slf4j**',
'worker.org.gradle.**', 'worker.org.gradle.**',
'com.nhaarman.mockito_kotlin**', 'org.mockito.kotlin**',
'org.assertj**', 'org.assertj**',
'org.hamcrest**', 'org.hamcrest**',
'org.mockito**', 'org.mockito**',
@ -95,7 +90,6 @@ buildscript {
ext.h2_version = constants.getProperty("h2Version") ext.h2_version = constants.getProperty("h2Version")
ext.rxjava_version = constants.getProperty("rxjavaVersion") ext.rxjava_version = constants.getProperty("rxjavaVersion")
ext.dokka_version = constants.getProperty("dokkaVersion") ext.dokka_version = constants.getProperty("dokkaVersion")
ext.eddsa_version = constants.getProperty("eddsaVersion")
ext.dependency_checker_version = constants.getProperty("dependencyCheckerVersion") ext.dependency_checker_version = constants.getProperty("dependencyCheckerVersion")
ext.commons_collections_version = constants.getProperty("commonsCollectionsVersion") ext.commons_collections_version = constants.getProperty("commonsCollectionsVersion")
ext.beanutils_version = constants.getProperty("beanutilsVersion") ext.beanutils_version = constants.getProperty("beanutilsVersion")
@ -116,7 +110,6 @@ buildscript {
ext.class_graph_version = constants.getProperty('classgraphVersion') ext.class_graph_version = constants.getProperty('classgraphVersion')
ext.jcabi_manifests_version = constants.getProperty("jcabiManifestsVersion") ext.jcabi_manifests_version = constants.getProperty("jcabiManifestsVersion")
ext.picocli_version = constants.getProperty("picocliVersion") ext.picocli_version = constants.getProperty("picocliVersion")
ext.commons_lang_version = constants.getProperty("commonsLangVersion")
ext.commons_io_version = constants.getProperty("commonsIoVersion") ext.commons_io_version = constants.getProperty("commonsIoVersion")
ext.controlsfx_version = constants.getProperty("controlsfxVersion") ext.controlsfx_version = constants.getProperty("controlsfxVersion")
ext.detekt_version = constants.getProperty('detektVersion') ext.detekt_version = constants.getProperty('detektVersion')
@ -124,20 +117,10 @@ buildscript {
ext.commons_configuration2_version = constants.getProperty("commonsConfiguration2Version") ext.commons_configuration2_version = constants.getProperty("commonsConfiguration2Version")
ext.commons_text_version = constants.getProperty("commonsTextVersion") ext.commons_text_version = constants.getProperty("commonsTextVersion")
ext.snake_yaml_version = constants.getProperty("snakeYamlVersion") ext.snake_yaml_version = constants.getProperty("snakeYamlVersion")
ext.fontawesomefx_commons_version = constants.getProperty("fontawesomefxCommonsVersion")
ext.fontawesomefx_fontawesome_version = constants.getProperty("fontawesomefxFontawesomeVersion")
ext.javaassist_version = constants.getProperty("javaassistVersion") ext.javaassist_version = constants.getProperty("javaassistVersion")
if (JavaVersion.current().isJava8()) {
ext.fontawesomefx_commons_version = constants.getProperty("fontawesomefxCommonsJava8Version")
ext.fontawesomefx_fontawesome_version = constants.getProperty("fontawesomefxFontawesomeJava8Version")
} else {
ext.fontawesomefx_commons_version = constants.getProperty("fontawesomefxCommonsVersion")
ext.fontawesomefx_fontawesome_version = constants.getProperty("fontawesomefxFontawesomeVersion")
}
// Update 121 is required for ObjectInputFilter.
// Updates [131, 161] also have zip compression bugs on MacOS (High Sierra).
// when the java version in NodeStartup.hasMinimumJavaVersion() changes, so must this check
ext.java8_minUpdateVersion = constants.getProperty('java8MinUpdateVersion')
ext.corda_revision = { ext.corda_revision = {
try { try {
"git rev-parse HEAD".execute().text.trim() "git rev-parse HEAD".execute().text.trim()
@ -171,6 +154,7 @@ buildscript {
content { content {
includeGroupByRegex 'net\\.corda(\\..*)?' includeGroupByRegex 'net\\.corda(\\..*)?'
includeGroupByRegex 'com\\.r3(\\..*)?' includeGroupByRegex 'com\\.r3(\\..*)?'
includeGroup 'co.paralleluniverse'
} }
} }
maven { maven {
@ -180,7 +164,6 @@ buildscript {
includeGroupByRegex 'com\\.r3(\\..*)?' includeGroupByRegex 'com\\.r3(\\..*)?'
} }
} }
gradlePluginPortal()
mavenCentral() mavenCentral()
maven { maven {
url "${publicArtifactURL}/jcenter-backup" url "${publicArtifactURL}/jcenter-backup"
@ -188,28 +171,21 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
classpath "net.corda.plugins:publish-utils:$gradle_plugins_version"
classpath "net.corda.plugins:quasar-utils:$gradle_plugins_version" classpath "net.corda.plugins:quasar-utils:$gradle_plugins_version"
classpath "net.corda.plugins:cordformation:$gradle_plugins_version" classpath "net.corda.plugins:cordformation:$gradle_plugins_version"
classpath "net.corda.plugins:cordapp:$gradle_plugins_version" classpath "net.corda.plugins:cordapp:$gradle_plugins_version"
classpath "net.corda.plugins:api-scanner:$gradle_plugins_version" classpath "net.corda.plugins:api-scanner:$gradle_plugins_version"
classpath "net.corda.plugins:jar-filter:$gradle_plugins_version" classpath "net.corda.plugins:jar-filter:$gradle_plugins_version"
classpath "net.sf.proguard:proguard-gradle:$proguard_version" classpath "com.guardsquare:proguard-gradle:$proguard_version"
classpath 'com.github.ben-manes:gradle-versions-plugin:0.15.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.15.0'
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version" classpath "org.jetbrains.dokka:dokka-base:$dokka_version"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" classpath "org.owasp:dependency-check-gradle:$dependency_checker_version"
classpath "net.i2p.crypto:eddsa:$eddsa_version" // Needed for ServiceIdentityGenerator in the build environment.
classpath "org.owasp:dependency-check-gradle:${dependency_checker_version}"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$artifactory_plugin_version" classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$artifactory_plugin_version"
// Capsule gradle plugin forked and maintained locally to support Gradle 5.x // Capsule gradle plugin forked and maintained locally to support Gradle 5.x
// See https://github.com/corda/gradle-capsule-plugin // See https://github.com/corda/gradle-capsule-plugin
classpath "us.kirchmeier:gradle-capsule-plugin:1.0.4_r3" classpath "us.kirchmeier:gradle-capsule-plugin:1.0.5_r3"
classpath group: "com.r3.testing", name: "gradle-distributed-testing-plugin", version: '1.3.0' classpath group: "com.r3.testing", name: "gradle-distributed-testing-plugin", version: '1.3.0'
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8"
classpath "com.gradle:gradle-enterprise-gradle-plugin:$gradleEnterprisePlugin"
classpath "com.gradle:common-custom-user-data-gradle-plugin:$customUserDataGradlePlugin"
} }
configurations.classpath { configurations.classpath {
@ -219,34 +195,20 @@ buildscript {
} }
plugins { plugins {
// Add the shadow plugin to the plugins classpath for the entire project. id 'org.jetbrains.kotlin.jvm' apply false
id 'com.github.johnrengelman.shadow' version '2.0.4' apply false id 'org.jetbrains.kotlin.plugin.allopen' apply false
id 'org.jetbrains.kotlin.plugin.jpa' apply false
id 'com.github.johnrengelman.shadow' version '7.1.2' apply false
id "org.ajoberstar.grgit" version "4.0.0" id "org.ajoberstar.grgit" version "4.0.0"
id 'corda.root-publish'
id "org.jetbrains.dokka" version "1.8.20"
} }
apply plugin: 'project-report' apply plugin: 'project-report'
apply plugin: 'com.github.ben-manes.versions' apply plugin: 'com.github.ben-manes.versions'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'com.r3.testing.distributed-testing' apply plugin: 'com.r3.testing.distributed-testing'
apply plugin: "com.gradle.build-scan"
apply plugin: "com.gradle.common-custom-user-data-gradle-plugin"
buildScan { // If the command line project option -PversionFromGit is added to the gradle invocation, we'll resolve
server = gradleEnterpriseUrl
allowUntrustedServer = false
def apiKey = project.findProperty('CORDA_GRADLE_SCAN_KEY') ?: System.getenv('CORDA_GRADLE_SCAN_KEY')
if (apiKey?.trim()) {
publishAlways()
capture {
taskInputFiles = true
}
uploadInBackground = false
accessKey = apiKey
}
}
// If the command line project option -PversionFromGit is added to the gradle invocation, we'll resolve
// the latest git commit hash and timestamp and create a version postfix from that // the latest git commit hash and timestamp and create a version postfix from that
if (project.hasProperty("versionFromGit")){ if (project.hasProperty("versionFromGit")){
ext.versionSuffix = "${grgit.head().dateTime.format("yyyyMMdd_HHmmss")}-${grgit.head().abbreviatedId}" ext.versionSuffix = "${grgit.head().dateTime.format("yyyyMMdd_HHmmss")}-${grgit.head().abbreviatedId}"
@ -259,26 +221,17 @@ if (ext.versionSuffix != ""){
ext.corda_release_version = "${ext.baseVersion}".toString() ext.corda_release_version = "${ext.baseVersion}".toString()
} }
// We need the following three lines even though they're inside an allprojects {} block below because otherwise logger.lifecycle("JDK: {}", System.getProperty("java.home"))
// IntelliJ gets confused when importing the project and ends up erasing and recreating the .idea directory, along
// with the run configurations. It also doesn't realise that the project is a Java 8 project and misconfigures
// the resulting import. This fixes it.
apply plugin: 'java'
logger.lifecycle("Java version: {}", JavaVersion.current())
sourceCompatibility = VERSION_1_8
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) logger.lifecycle("Quasar version: {}", quasar_version)
logger.lifecycle("Quasar classifier: {}", quasar_classifier.toString()) logger.lifecycle("Quasar classifier: {}", quasar_classifier.toString())
logger.lifecycle("Building Corda version: {}", corda_release_version) logger.lifecycle("Building Corda version: {}", corda_release_version)
logger.lifecycle("User home: {}", System.getProperty('user.home'))
allprojects { allprojects {
apply plugin: 'kotlin' apply plugin: 'java'
apply plugin: 'kotlin-allopen'
apply plugin: 'jacoco' apply plugin: 'jacoco'
apply plugin: 'org.owasp.dependencycheck' apply plugin: 'org.owasp.dependencycheck'
apply plugin: 'kotlin-allopen'
apply plugin: 'org.sonarqube' apply plugin: 'org.sonarqube'
allOpen { allOpen {
@ -289,19 +242,6 @@ allprojects {
) )
} }
// we do this to allow for Gradle task caching.
// below block tells Gradle to ignore specifically the dymaically generated files in the manifest when checking if a file is up to date
// this has no impact on publishing or production of jar, This only reates to Grades mechamish for verifying the Cache key
normalization {
runtimeClasspath {
ignore("**/*.EC") //signing related
ignore("**/*.SF") //signing related
ignore("**/*.MF")
ignore("**/*.kotlin_module")
ignore("**/Cordapp-Dependencies")
}
}
dependencyCheck { dependencyCheck {
suppressionFile = '.ci/dependency-checker/suppressedLibraries.xml' suppressionFile = '.ci/dependency-checker/suppressedLibraries.xml'
cveValidForHours = 1 cveValidForHours = 1
@ -316,12 +256,17 @@ allprojects {
nugetconfEnabled = false nugetconfEnabled = false
} }
} }
sourceCompatibility = VERSION_1_8
targetCompatibility = JavaVersion.current().isJava8() ? VERSION_1_8 : VERSION_11 sourceCompatibility = VERSION_17
targetCompatibility = VERSION_17
jacoco { jacoco {
// JDK11 official support (https://github.com/jacoco/jacoco/releases/tag/v0.8.3) toolVersion = "0.8.7"
toolVersion = "0.8.3" }
java {
withSourcesJar()
withJavadocJar()
} }
tasks.withType(JavaCompile).configureEach { tasks.withType(JavaCompile).configureEach {
@ -336,13 +281,13 @@ allprojects {
options.encoding = 'UTF-8' options.encoding = 'UTF-8'
} }
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { tasks.withType(KotlinCompile).configureEach {
kotlinOptions { compilerOptions {
languageVersion = "1.2" languageVersion = KOTLIN_1_9
apiVersion = "1.2" apiVersion = KOTLIN_1_9
jvmTarget = VERSION_1_8 jvmTarget = JVM_17
javaParameters = true // Useful for reflection. javaParameters = true // Useful for reflection.
freeCompilerArgs = ['-Xjvm-default=compatibility'] freeCompilerArgs = ['-Xjvm-default=all-compatibility']
allWarningsAsErrors = warnings_as_errors allWarningsAsErrors = warnings_as_errors
} }
} }
@ -362,15 +307,14 @@ allprojects {
attributes('Corda-Docs-Link': corda_docs_link) attributes('Corda-Docs-Link': corda_docs_link)
} }
} }
tasks.withType(Test).configureEach { tasks.withType(Test).configureEach {
jvmArgs += project(":node:capsule").file("src/main/resources/node-jvm-args.txt").readLines()
jvmArgs += "--add-modules=jdk.incubator.foreign" // For the SharedMemoryIncremental
forkEvery = 20 forkEvery = 20
ignoreFailures = project.hasProperty('tests.ignoreFailures') ? project.property('tests.ignoreFailures').toBoolean() : false ignoreFailures = project.hasProperty('tests.ignoreFailures') ? project.property('tests.ignoreFailures').toBoolean() : false
failFast = project.hasProperty('tests.failFast') ? project.property('tests.failFast').toBoolean() : false failFast = project.hasProperty('tests.failFast') ? project.property('tests.failFast').toBoolean() : false
// Prevent the project from creating temporary files outside of the build directory.
systemProperty 'java.io.tmpdir', buildDir.absolutePath
maxHeapSize = "1g" maxHeapSize = "1g"
if (project.path.startsWith(':experimental') && System.getProperty("experimental.test.enable") == null) { if (project.path.startsWith(':experimental') && System.getProperty("experimental.test.enable") == null) {
@ -380,25 +324,18 @@ allprojects {
// Required to use Gradle build cache (until Gradle 5.0 is released with default value of "append" set to false) // Required to use Gradle build cache (until Gradle 5.0 is released with default value of "append" set to false)
// See https://github.com/gradle/gradle/issues/5269 and https://github.com/gradle/gradle/pull/6419 // See https://github.com/gradle/gradle/issues/5269 and https://github.com/gradle/gradle/pull/6419
extensions.configure(TypeOf.typeOf(JacocoTaskExtension)) { ex -> extensions.configure(TypeOf.typeOf(JacocoTaskExtension)) { ex ->
ex.append = false // ex.append = false
} }
maxParallelForks = (System.env.CORDA_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_TESTING_FORKS".toInteger()
systemProperty 'java.security.egd', 'file:/dev/./urandom'
}
tasks.withType(Test).configureEach {
if (name.contains("integrationTest")) { if (name.contains("integrationTest")) {
maxParallelForks = (System.env.CORDA_INT_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_INT_TESTING_FORKS".toInteger() maxParallelForks = (System.env.CORDA_INT_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_INT_TESTING_FORKS".toInteger()
} else {
maxParallelForks = (System.env.CORDA_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_TESTING_FORKS".toInteger()
} }
}
if (jdkClassifier) { // Prevent the project from creating temporary files outside of the build directory.
jar { systemProperty 'java.io.tmpdir', buildDir.absolutePath
// JDK11 built and published artifacts to include classifier systemProperty 'java.security.egd', 'file:/dev/./urandom'
archiveClassifier = jdkClassifier
}
} }
group 'net.corda' group 'net.corda'
@ -439,6 +376,16 @@ allprojects {
includeGroup 'com.github.bft-smart' includeGroup 'com.github.bft-smart'
includeGroup 'com.github.detro' includeGroup 'com.github.detro'
} }
metadataSources {
mavenPom()
artifact()
}
}
maven {
url "${publicArtifactURL}/corda-dependencies-dev"
content {
includeGroup 'co.paralleluniverse'
}
} }
maven { maven {
url "${publicArtifactURL}/corda-dev" url "${publicArtifactURL}/corda-dev"
@ -468,12 +415,12 @@ allprojects {
} }
configurations { configurations {
all { configureEach {
resolutionStrategy { resolutionStrategy {
// Force dependencies to use the same version of Kotlin as Corda. if (pluginManager.hasPlugin("org.jetbrains.kotlin.jvm")) {
force "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // Force dependencies to use the same version of Kotlin as Corda.
force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" }
// Force dependencies to use the same version of Guava as Corda. // Force dependencies to use the same version of Guava as Corda.
force "com.google.guava:guava:$guava_version" force "com.google.guava:guava:$guava_version"
@ -508,7 +455,7 @@ allprojects {
substitute module('commons-logging:commons-logging') with module("org.slf4j:jcl-over-slf4j:$slf4j_version") substitute module('commons-logging:commons-logging') with module("org.slf4j:jcl-over-slf4j:$slf4j_version")
// Remove any transitive dependency on Logback (e.g. Liquibase 3.6 introduces this dependency) // Remove any transitive dependency on Logback (e.g. Liquibase 3.6 introduces this dependency)
substitute module('ch.qos.logback:logback-classic') with module("org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version") substitute module('ch.qos.logback:logback-classic') with module("org.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version")
// Netty-All is an uber-jar which contains every Netty module. // Netty-All is an uber-jar which contains every Netty module.
// Exclude it to force us to use the individual Netty modules instead. // Exclude it to force us to use the individual Netty modules instead.
@ -519,27 +466,32 @@ allprojects {
// Effectively delete this unused and unwanted transitive dependency of Artemis. // Effectively delete this unused and unwanted transitive dependency of Artemis.
substitute module('org.jgroups:jgroups') with module("org.apache.activemq:artemis-commons:$artemis_version") substitute module('org.jgroups:jgroups') with module("org.apache.activemq:artemis-commons:$artemis_version")
}
}
}
// Select all of the compileClasspath and runtimeClasspath etc configurations, // Force use of LTS version of BC everywhere
// but NOT the "classpath" configuration, as that is used by the Gradle plugins. substitute module('org.bouncycastle:bcutil-jdk18on') with module("org.bouncycastle:bcutil-lts8on:$bouncycastle_version")
matching { it.name.endsWith("Classpath") }.configureEach { cfg -> substitute module('org.bouncycastle:bcprov-jdk18on') with module("org.bouncycastle:bcprov-lts8on:$bouncycastle_version")
cfg.resolutionStrategy { substitute module('org.bouncycastle:bcpkix-jdk18on') with module("org.bouncycastle:bcpkix-lts8on:$bouncycastle_version")
dependencySubstitution {
// Force dependencies to use the same version of Kotlin as Corda.
substitute module('org.jetbrains.kotlin:kotlin-stdlib-jdk8') with module("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-stdlib-jdk7') with module("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-stdlib-common') with module("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-stdlib') with module("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-reflect') with module("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
} }
// FORCE Gradle to use latest SNAPSHOT dependencies. // FORCE Gradle to use latest SNAPSHOT dependencies.
cacheChangingModulesFor 0, 'seconds' cacheChangingModulesFor 0, 'seconds'
} }
} }
if (pluginManager.hasPlugin("org.jetbrains.kotlin.jvm")) {
// Select all of the compileClasspath and runtimeClasspath etc configurations,
// but NOT the "classpath" configuration, as that is used by the Gradle plugins.
matching { it.name.endsWith("Classpath") }.configureEach { cfg ->
cfg.resolutionStrategy {
dependencySubstitution {
// Force dependencies to use the same version of Kotlin as Corda.
substitute module('org.jetbrains.kotlin:kotlin-stdlib-common') with module("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-stdlib') with module("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
substitute module('org.jetbrains.kotlin:kotlin-reflect') with module("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
}
}
}
}
} }
} }
@ -554,37 +506,29 @@ sonarqube {
} }
} }
// Check that we are running on a Java 8 JDK. The source/targetCompatibility values above aren't sufficient to
// guarantee this because those are properties checked by the Java plugin, but we're using Kotlin.
//
// We recommend a specific minor version (unfortunately, not checkable directly) because JavaFX adds APIs in
// minor releases, so we can't work with just any Java 8, it has to be a recent one.
if (!JavaVersion.current().java8Compatible)
throw new GradleException("Corda requires Java 8, please upgrade to at least 1.8.0_$java8_minUpdateVersion")
configurations { configurations {
detekt detekt
} }
// Required for building out the fat JAR. // Required for building out the fat JAR.
dependencies { dependencies {
compile project(':node') implementation project(':node')
compile "com.google.guava:guava:$guava_version" implementation "com.google.guava:guava:$guava_version"
// Set to corda compile to ensure it exists now deploy nodes no longer relies on build // Set to corda implementation to ensure it exists now deploy nodes no longer relies on build
compile project(path: ":node:capsule", configuration: 'runtimeArtifacts') implementation project(path: ":node:capsule", configuration: 'runtimeArtifacts')
compile project(path: ":testing:testserver:testcapsule:", configuration: 'runtimeArtifacts') implementation project(path: ":testing:testserver:testcapsule:", configuration: 'runtimeArtifacts')
// For the buildCordappDependenciesJar task // For the buildCordappDependenciesJar task
runtime project(':client:jfx') runtimeOnly project(':client:jfx')
runtime project(':client:mock') runtimeOnly project(':client:mock')
runtime project(':client:rpc') runtimeOnly project(':client:rpc')
runtime project(':core') runtimeOnly project(':core')
runtime project(':confidential-identities') runtimeOnly project(':confidential-identities')
runtime project(':finance:workflows') runtimeOnly project(':finance:workflows')
runtime project(':finance:contracts') runtimeOnly project(':finance:contracts')
runtime project(':testing:testserver') runtimeOnly project(':testing:testserver')
testCompile project(':test-utils') testImplementation project(':test-utils')
detekt 'io.gitlab.arturbosch.detekt:detekt-cli:1.0.1' detekt 'io.gitlab.arturbosch.detekt:detekt-cli:1.0.1'
} }
@ -593,16 +537,16 @@ jar {
enabled = false enabled = false
} }
task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { tasks.register('jacocoRootReport', JacocoReport) {
dependsOn = subprojects.test dependsOn = subprojects.test
additionalSourceDirs = files(subprojects.sourceSets.main.allSource.srcDirs) // additionalSourceDirs = files(subprojects.sourceSets.main.allSource.srcDirs)
sourceDirectories = files(subprojects.sourceSets.main.allSource.srcDirs) // sourceDirectories = files(subprojects.sourceSets.main.allSource.srcDirs)
classDirectories = files(subprojects.sourceSets.main.output) // classDirectories = files(subprojects.sourceSets.main.output)
executionData = files(subprojects.jacocoTestReport.executionData) // executionData = files(subprojects.jacocoTestReport.executionData)
reports { reports {
html.enabled = true html.required = true
xml.enabled = true xml.required = true
csv.enabled = false csv.required = false
} }
onlyIf = { onlyIf = {
true true
@ -622,13 +566,13 @@ tasks.register('detekt', JavaExec) {
def plugins = detektPluginsJar.outputs.files.singleFile def plugins = detektPluginsJar.outputs.files.singleFile
def params = ['-i', input, '-c', config, '-b', baseline, '--plugins', plugins] def params = ['-i', input, '-c', config, '-b', baseline, '--plugins', plugins]
inputs.files(detektPluginsJar, config, baseline) inputs.files(detektPluginsJar, config, baseline)
main = "io.gitlab.arturbosch.detekt.cli.Main" mainClass = "io.gitlab.arturbosch.detekt.cli.Main"
classpath = configurations.detekt classpath = configurations.detekt
args(params) args(params)
} }
tasks.register('detektBaseline', JavaExec) { tasks.register('detektBaseline', JavaExec) {
main = "io.gitlab.arturbosch.detekt.cli.Main" mainClass = "io.gitlab.arturbosch.detekt.cli.Main"
classpath = configurations.detekt classpath = configurations.detekt
def input = "$projectDir" def input = "$projectDir"
def config = "$projectDir/detekt-config.yml, $projectDir/detekt-baseline-config.yml" def config = "$projectDir/detekt-config.yml, $projectDir/detekt-baseline-config.yml"
@ -638,103 +582,28 @@ tasks.register('detektBaseline', JavaExec) {
} }
tasks.withType(Test).configureEach { tasks.withType(Test).configureEach {
reports.html.destination = file("${reporting.baseDir}/${name}") reports.html.outputLocation.set(file("${reporting.baseDir}/${name}"))
} }
task testReport(type: TestReport) { tasks.register('testReport', TestReport) {
destinationDir = file("$buildDir/reports/allTests") destinationDir = file("$buildDir/reports/allTests")
// Include the results from the `test` task in all subprojects // Include the results from the `test` task in all subprojects
reportOn subprojects*.test reportOn subprojects*.test
} }
bintrayConfig {
user = System.getenv('CORDA_BINTRAY_USER')
key = System.getenv('CORDA_BINTRAY_KEY')
repo = 'corda'
org = 'r3'
licenses = ['Apache-2.0']
vcsUrl = 'https://github.com/corda/corda'
projectUrl = 'https://github.com/corda/corda'
gpgSign = true
gpgPassphrase = System.getenv('CORDA_BINTRAY_GPG_PASSPHRASE')
publications = [
'corda-opentelemetry',
'corda-opentelemetry-driver',
'corda-jfx',
'corda-mock',
'corda-rpc',
'corda-core',
'corda',
'corda-finance-workflows',
'corda-finance-contracts',
'corda-node',
'corda-node-api',
'corda-test-common',
'corda-core-test-utils',
'corda-test-utils',
'corda-test-db',
'corda-jackson',
'corda-testserver-impl',
'corda-testserver',
'corda-node-driver',
'corda-confidential-identities',
'corda-shell',
'corda-tools-shell-cli',
'corda-serialization',
'corda-tools-blob-inspector',
'corda-tools-explorer',
'corda-tools-network-bootstrapper',
'corda-tools-cliutils',
'corda-common-configuration-parsing',
'corda-common-validation',
'corda-common-logging',
'corda-tools-network-builder',
'corda-tools-checkpoint-agent'
]
license {
name = 'Apache-2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0'
distribution = 'repo'
}
developer {
id = 'R3'
name = 'R3'
email = 'dev@corda.net'
}
}
// Build a ZIP of all JARs required to compile the Cordapp template
// Note: corda.jar is used at runtime so no runtime ZIP is necessary. // Note: corda.jar is used at runtime so no runtime ZIP is necessary.
// Resulting ZIP can be found in "build/distributions" // Resulting ZIP can be found in "build/distributions"
task buildCordappDependenciesZip(type: Zip) { tasks.register('buildCordappDependenciesZip', Zip) {
baseName 'corda-deps' baseName 'corda-deps'
from configurations.runtime from configurations.runtimeOnly
from configurations.compile from configurations.implementation
from configurations.testCompile from configurations.testImplementation
from buildscript.configurations.classpath from buildscript.configurations.classpath
from 'node/capsule/NOTICE' // CDDL notice from 'node/capsule/NOTICE' // CDDL notice
duplicatesStrategy = DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
} }
artifactory { tasks.register('generateApi', GenerateApi) {
publish {
contextUrl = artifactory_contextUrl
repository {
repoKey = 'corda-dev'
username = System.getenv('CORDA_ARTIFACTORY_USERNAME')
password = System.getenv('CORDA_ARTIFACTORY_PASSWORD')
}
defaults {
// Root project applies the plugin (for this block) but does not need to be published
if (project != rootProject) {
publications(project.extensions.publish.name())
}
}
}
}
tasks.register('generateApi', net.corda.plugins.apiscanner.GenerateApi) {
baseName = "api-corda" baseName = "api-corda"
} }
@ -770,7 +639,7 @@ if (file('corda-docs-only-build').exists() || (System.getenv('CORDA_DOCS_ONLY_BU
} }
wrapper { wrapper {
gradleVersion = '5.6.4' gradleVersion = '7.6.4'
distributionType = Wrapper.DistributionType.ALL distributionType = Wrapper.DistributionType.ALL
} }

16
buildCacheSettings.gradle Normal file
View File

@ -0,0 +1,16 @@
// Gradle Build Cache configuration recommendation: https://docs.gradle.org/current/userguide/build_cache.html
ext {
isCiServer = System.getenv().containsKey("CORDA_CI")
gradleBuildCacheURL = System.getenv().containsKey("GRADLE_BUILD_CACHE_URL") ? System.getenv().get("GRADLE_BUILD_CACHE_URL") : 'http://localhost:5071/cache/'
}
buildCache {
local {
enabled = !isCiServer
}
remote(HttpBuildCache) {
enabled = isCiServer
url = gradleBuildCacheURL
push = isCiServer
}
}

View File

@ -1,11 +1,56 @@
plugins {
id 'groovy-gradle-plugin'
}
Properties constants = new Properties() Properties constants = new Properties()
file("$rootDir/../constants.properties").withInputStream { constants.load(it) } file("$rootDir/../constants.properties").withInputStream { constants.load(it) }
def internalPublishVersion = constants.getProperty('internalPublishVersion')
def artifactoryContextUrl = constants.getProperty('artifactoryContextUrl')
repositories { repositories {
mavenCentral() def cordaUseCache = System.getenv("CORDA_USE_CACHE")
if (cordaUseCache != null) {
maven {
url = "${artifactoryContextUrl}/${cordaUseCache}"
name = "R3 Maven remote repositories"
authentication {
basic(BasicAuthentication)
}
credentials {
username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME')
password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD')
}
metadataSources {
mavenPom()
artifact()
ignoreGradleMetadataRedirection()
}
}
} else {
maven {
url "${artifactoryContextUrl}/engineering-tools-maven"
authentication {
basic(BasicAuthentication)
}
credentials {
username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME')
password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD')
}
content {
includeGroupByRegex 'com\\.r3\\.internal(\\..*)?'
}
}
gradlePluginPortal()
}
} }
dependencies { dependencies {
compile group: 'com.github.docker-java', name: 'docker-java', version: constants.dockerJavaVersion implementation "com.github.docker-java:docker-java:$constants.dockerJavaVersion"
compile group: 'com.github.docker-java', name: 'docker-java-transport-httpclient5', version: constants.dockerJavaVersion implementation "com.github.docker-java:docker-java-transport-httpclient5:$constants.dockerJavaVersion"
implementation "org.jooq:joor:$constants.joorVersion"
if (System.getenv('CORDA_ARTIFACTORY_USERNAME') != null || project.hasProperty('cordaArtifactoryUsername')) {
implementation "com.r3.internal.gradle.plugins:publish:$internalPublishVersion"
}
} }

View File

@ -0,0 +1,92 @@
import groovy.transform.CompileStatic
// plugin to cater for R3 vs Non R3 users building code base. R3 employees will leverage internal plugins non
// R3 users will use standard Maven publishing conventions as provided by the Maven-publish gradle plugin
if (System.getenv('CORDA_ARTIFACTORY_USERNAME') != null || project.hasProperty('cordaArtifactoryUsername')) {
logger.info("Internal R3 user - resolving publication build dependencies from internal plugins")
pluginManager.apply('com.r3.internal.gradle.plugins.r3Publish')
afterEvaluate {
publishing {
publications {
configureEach {
def repo = "https://github.com/corda/corda"
pom {
description = project.description
name = project.name
url = repo
scm {
url = repo
}
licenses {
license {
name = 'Apache-2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0'
distribution = 'repo'
}
}
developers {
developer {
id = 'R3'
name = 'R3'
email = 'dev@corda.net'
}
}
}
}
}
}
}
} else {
logger.info("External user - using standard maven publishing")
pluginManager.apply('maven-publish')
pluginManager.withPlugin('java') {
afterEvaluate {
publishing {
if (publications.isEmpty()) {
// If we haven't already created a MavenPublication then create one now.
publications {
maven(MavenPublication) {
artifactId = tasks.named('jar', Jar).flatMap { it.archiveBaseName }.get()
groupId group.toString()
from findSoftwareComponent(components).get()
if (artifacts.matching { it.classifier == 'sources' }.isEmpty()) {
try {
artifact tasks.named('sourcesJar', Jar)
} catch (UnknownTaskException ignored) {
}
}
try {
artifact tasks.named('javadocJar', Jar)
} catch (UnknownTaskException ignored) {
}
}
}
}
}
}
}
tasks.withType(GenerateModuleMetadata).configureEach {
enabled = false
}
tasks.register('install') {
dependsOn 'publishToMavenLocal'
}
}
@CompileStatic
private static Provider<SoftwareComponent> findSoftwareComponent(SoftwareComponentContainer components) {
try {
return components.named('cordapp')
} catch (UnknownDomainObjectException ignored) {
try {
return components.named('kotlin')
} catch (UnknownDomainObjectException ignored2) {
return components.named('java')
}
}
}

View File

@ -0,0 +1,91 @@
import org.gradle.api.internal.file.DefaultSourceDirectorySet
import static org.joor.Reflect.onClass
pluginManager.apply(Kotlin12Plugin.class)
// We cannot use the 1.2 Kotlin plugin as it only works with a very old version of Gradle, which itself will only work on Java 8. So we need
// our own plugin which calls the 1.2 compiler directly.
class Kotlin12Plugin implements Plugin<Project> {
private static final KOTLIN_VERSION = "1.2.71"
@Override
void apply(Project project) {
project.pluginManager.apply(JavaPlugin.class)
project.extensions.add("kotlin_1_2_version", KOTLIN_VERSION)
project.dependencies.add("implementation", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION")
def kotlinCompilerConfiguration = project.configurations.create("kotlinCompiler")
project.dependencies.add("kotlinCompiler", "org.jetbrains.kotlin:kotlin-compiler:$KOTLIN_VERSION")
project.extensions.getByType(JavaPluginExtension.class).sourceSets.configureEach { sourceSet ->
// Create the "src/*/kotlin" SourceDirectorySet, alongside the "java" one
def kotlinSourceDirectorySet = new DefaultSourceDirectorySet(project.objects.sourceDirectorySet("kotlin", "${sourceSet.displayName} Kotlin source"))
sourceSet.extensions.add(SourceDirectorySet.class, "kotlin", kotlinSourceDirectorySet)
kotlinSourceDirectorySet.filter.include("**/*.java", "**/*.kt")
kotlinSourceDirectorySet.srcDir(project.file("src/${sourceSet.name}/kotlin"))
def allKotlin = project.objects.sourceDirectorySet("allkotlin", "${sourceSet.displayName} Kotlin source")
allKotlin.filter.include("**/*.kt")
allKotlin.source(kotlinSourceDirectorySet)
sourceSet.allJava.source(kotlinSourceDirectorySet)
sourceSet.allSource.source(kotlinSourceDirectorySet)
def kotlinBuildDir = project.layout.buildDirectory.dir("classes/kotlin/${sourceSet.name}")
sourceSet.output.dir(kotlinBuildDir)
def taskSourceSetName = isMain(sourceSet) ? "" : sourceSet.name.capitalize()
def compileKotlin = project.tasks.register("compile${taskSourceSetName}Kotlin", KotlinCompile.class) { task ->
// The 1.2 compiler needs to be laoded in a separate class loader, as the build classpath already contains its own version
// of Kotlin.
task.compilerClasspath.from(kotlinCompilerConfiguration)
task.source(allKotlin)
// Paradoxically, the Java sources are also required by the Kotlin compiler. This is actually so that it can correctly
// resolve any references the Kotlin code makes to Java code.
task.source(sourceSet.allJava)
task.classpath = sourceSet.compileClasspath
task.destinationDirectory = kotlinBuildDir
}
// Compiling the Java code needs the compiled Kotlin code first
project.tasks.named("compile${taskSourceSetName}Java", JavaCompile.class) { task ->
task.classpath += project.files(compileKotlin.map { it.destinationDirectory })
}
}
}
}
abstract class KotlinCompile extends AbstractCompile {
@Classpath
abstract ConfigurableFileCollection getCompilerClasspath()
@TaskAction
void compile() {
def args = [
"-jvm-target", "1.8",
"-language-version", "1.2",
"-api-version", "1.2",
"-java-parameters",
"-Xjvm-default=compatibility",
"-no-stdlib",
"-Xallow-kotlin-package", // We may have copies of stdlib APIs (see `core-1.2`)
"-cp", classpath.asPath,
"-d", destinationDirectory.get().asFile.absolutePath
]
args.addAll(source.collect { it.absolutePath })
logger.info("args: {}", args)
def compilerClassLoader = new URLClassLoader(compilerClasspath.collect { it.toURI().toURL() } as URL[])
def exitCode = onClass("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler", compilerClassLoader)
.create()
.call("exec", System.err, args as String[])
.get()
if (exitCode.toString() != "OK") {
throw new GradleException("Compilation error. See log for more details")
}
}
}

View File

@ -0,0 +1,6 @@
// Apply artifactory r3ArtifactoryPublish plugin
if (System.getenv('CORDA_ARTIFACTORY_USERNAME') != null || project.hasProperty('cordaArtifactoryUsername')) {
project.pluginManager.apply('com.r3.internal.gradle.plugins.r3ArtifactoryPublish')
}
project.pluginManager.apply('maven-publish')

View File

@ -1,25 +1,36 @@
apply plugin: 'java' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'net.corda.plugins.api-scanner'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'corda.common-publishing'
description 'Corda Jackson module'
dependencies { dependencies {
compile project(':serialization') api project(':core')
implementation project(':serialization')
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
// Jackson and its plugins: parsing to/from JSON and other textual formats. // Jackson and its plugins: parsing to/from JSON and other textual formats.
compile("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_kotlin_version") { implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_kotlin_version") {
exclude module: "jackson-databind" exclude module: "jackson-databind"
} }
// Yaml is useful for parsing strings to method calls. // Yaml is useful for parsing strings to method calls.
compile "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version" implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version"
// This adds support for java.time types. // This adds support for java.time types.
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version" implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
compile "com.google.guava:guava:$guava_version" implementation "com.google.guava:guava:$guava_version"
testCompile project(':test-utils') // Bouncy castle support needed for X509 certificate manipulation
testCompile project(path: ':core', configuration: 'testArtifacts') implementation "org.bouncycastle:bcprov-lts8on:${bouncycastle_version}"
implementation "org.bouncycastle:bcpkix-lts8on:${bouncycastle_version}"
implementation "org.slf4j:slf4j-api:$slf4j_version"
testImplementation project(':finance:workflows')
testImplementation project(':node-api')
testImplementation project(':test-common')
testImplementation project(':core-test-utils')
testImplementation project(':test-utils')
testImplementation project(":node-driver")
testImplementation project(path: ':core', configuration: 'testArtifacts')
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "junit:junit:$junit_version" testImplementation "junit:junit:$junit_version"
@ -28,7 +39,7 @@ dependencies {
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
} }
@ -39,6 +50,11 @@ jar {
} }
} }
publish { publishing {
name jar.baseName publications {
} maven(MavenPublication) {
artifactId jar.baseName
from components.java
}
}
}

View File

@ -24,7 +24,7 @@ import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers import com.fasterxml.jackson.databind.deser.std.NumberDeserializers
import com.fasterxml.jackson.databind.node.ObjectNode import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.kotlinModule
import net.corda.client.jackson.internal.CordaModule import net.corda.client.jackson.internal.CordaModule
import net.corda.client.jackson.internal.ToStringSerialize import net.corda.client.jackson.internal.ToStringSerialize
import net.corda.client.jackson.internal.jsonObject import net.corda.client.jackson.internal.jsonObject
@ -197,7 +197,7 @@ object JacksonSupport {
addSerializer(Date::class.java, DateSerializer) addSerializer(Date::class.java, DateSerializer)
}) })
registerModule(CordaModule()) registerModule(CordaModule())
registerModule(KotlinModule().apply { registerModule(kotlinModule().apply {
setDeserializerModifier(KotlinObjectDeserializerModifier) setDeserializerModifier(KotlinObjectDeserializerModifier)
}) })

View File

@ -120,7 +120,7 @@ open class StringToMethodCallParser<in T : Any> @JvmOverloads constructor(
} }
/** /**
* Uses either Kotlin or Java 8 reflection to learn the names of the parameters to a method. * Uses either Kotlin or Java reflection to learn the names of the parameters to a method.
*/ */
open fun paramNamesFromMethod(method: Method): List<String> { open fun paramNamesFromMethod(method: Method): List<String> {
val kf: KFunction<*>? = method.kotlinFunction val kf: KFunction<*>? = method.kotlinFunction
@ -135,7 +135,7 @@ open class StringToMethodCallParser<in T : Any> @JvmOverloads constructor(
} }
/** /**
* Uses either Kotlin or Java 8 reflection to learn the names of the parameters to a constructor. * Uses either Kotlin or Java reflection to learn the names of the parameters to a constructor.
*/ */
open fun paramNamesFromConstructor(ctor: Constructor<*>): List<String> { open fun paramNamesFromConstructor(ctor: Constructor<*>): List<String> {
val kf: KFunction<*>? = ctor.kotlinFunction val kf: KFunction<*>? = ctor.kotlinFunction

View File

@ -95,7 +95,8 @@ import java.math.BigDecimal
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.time.Instant import java.time.Instant
import java.util.* import java.util.Currency
import java.util.UUID
class CordaModule : SimpleModule("corda-core") { class CordaModule : SimpleModule("corda-core") {
override fun setupModule(context: SetupContext) { override fun setupModule(context: SetupContext) {
@ -256,6 +257,7 @@ private data class StxJson(
private interface WireTransactionMixin private interface WireTransactionMixin
private class WireTransactionSerializer : JsonSerializer<WireTransaction>() { private class WireTransactionSerializer : JsonSerializer<WireTransaction>() {
@Suppress("INVISIBLE_MEMBER")
override fun serialize(value: WireTransaction, gen: JsonGenerator, serializers: SerializerProvider) { override fun serialize(value: WireTransaction, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeObject(WireTransactionJson( gen.writeObject(WireTransactionJson(
value.digestService, value.digestService,
@ -265,7 +267,7 @@ private class WireTransactionSerializer : JsonSerializer<WireTransaction>() {
value.outputs, value.outputs,
value.commands, value.commands,
value.timeWindow, value.timeWindow,
value.attachments, value.legacyAttachments.map { "$it-legacy" } + value.nonLegacyAttachments.map { it.toString() },
value.references, value.references,
value.privacySalt, value.privacySalt,
value.networkParametersHash value.networkParametersHash
@ -276,15 +278,18 @@ private class WireTransactionSerializer : JsonSerializer<WireTransaction>() {
private class WireTransactionDeserializer : JsonDeserializer<WireTransaction>() { private class WireTransactionDeserializer : JsonDeserializer<WireTransaction>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): WireTransaction { override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): WireTransaction {
val wrapper = parser.readValueAs<WireTransactionJson>() val wrapper = parser.readValueAs<WireTransactionJson>()
// We're not concerned with backwards compatibility for any JSON string that was created with 4.11 and being materialised in 4.12.
val (legacyAttachments, newerAttachments) = wrapper.attachments.partition { it.endsWith("-legacy") }
val componentGroups = createComponentGroups( val componentGroups = createComponentGroups(
wrapper.inputs, wrapper.inputs,
wrapper.outputs, wrapper.outputs,
wrapper.commands, wrapper.commands,
wrapper.attachments, newerAttachments.map(SecureHash::parse),
wrapper.notary, wrapper.notary,
wrapper.timeWindow, wrapper.timeWindow,
wrapper.references, wrapper.references,
wrapper.networkParametersHash wrapper.networkParametersHash,
legacyAttachments.map { SecureHash.parse(it.removeSuffix("-legacy")) }
) )
return WireTransaction(componentGroups, wrapper.privacySalt, wrapper.digestService ?: DigestService.sha2_256) return WireTransaction(componentGroups, wrapper.privacySalt, wrapper.digestService ?: DigestService.sha2_256)
} }
@ -297,10 +302,11 @@ private class WireTransactionJson(@get:JsonInclude(Include.NON_NULL) val digestS
val outputs: List<TransactionState<*>>, val outputs: List<TransactionState<*>>,
val commands: List<Command<*>>, val commands: List<Command<*>>,
val timeWindow: TimeWindow?, val timeWindow: TimeWindow?,
val attachments: List<SecureHash>, val attachments: List<String>,
val references: List<StateRef>, val references: List<StateRef>,
val privacySalt: PrivacySalt, val privacySalt: PrivacySalt,
val networkParametersHash: SecureHash?) val networkParametersHash: SecureHash?
)
private interface TransactionStateMixin { private interface TransactionStateMixin {
@get:JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @get:JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)

View File

@ -8,27 +8,36 @@ import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.databind.node.TextNode import com.fasterxml.jackson.databind.node.TextNode
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.kotlin.convertValue import com.fasterxml.jackson.module.kotlin.convertValue
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 com.nhaarman.mockito_kotlin.spy
import net.corda.client.jackson.internal.childrenAs import net.corda.client.jackson.internal.childrenAs
import net.corda.client.jackson.internal.valueAs import net.corda.client.jackson.internal.valueAs
import net.corda.core.contracts.* import net.corda.core.contracts.Amount
import net.corda.core.contracts.Command
import net.corda.core.contracts.LinearState
import net.corda.core.contracts.PrivacySalt
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow
import net.corda.core.contracts.TransactionState
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.cordapp.CordappProvider import net.corda.core.cordapp.CordappProvider
import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.PartialMerkleTree.PartialTree import net.corda.core.crypto.PartialMerkleTree.PartialTree
import net.corda.core.identity.* import net.corda.core.crypto.SecureHash
import net.corda.core.internal.AbstractAttachment import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.secureRandomBytes
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.DigitalSignatureWithCert import net.corda.core.internal.DigitalSignatureWithCert
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.NetworkParametersService
import net.corda.core.node.services.TransactionStorage
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
@ -37,14 +46,27 @@ import net.corda.core.transactions.CoreTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.* import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.days
import net.corda.core.utilities.hours
import net.corda.core.utilities.toBase58String
import net.corda.core.utilities.toBase64
import net.corda.core.utilities.toHexString
import net.corda.coretesting.internal.createNodeInfoAndSigned
import net.corda.coretesting.internal.rigorousMock
import net.corda.finance.USD import net.corda.finance.USD
import net.corda.nodeapi.internal.crypto.x509Certificates import net.corda.nodeapi.internal.crypto.x509Certificates
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.* import net.corda.testing.core.ALICE_NAME
import net.corda.coretesting.internal.createNodeInfoAndSigned import net.corda.testing.core.BOB_NAME
import net.corda.coretesting.internal.rigorousMock import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.DummyCommandData
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity
import net.corda.testing.node.MockServices
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before import org.junit.Before
@ -54,15 +76,22 @@ import org.junit.jupiter.api.TestFactory
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters import org.junit.runners.Parameterized.Parameters
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
import java.math.BigInteger import java.math.BigInteger
import java.nio.charset.StandardCharsets.UTF_8 import java.nio.charset.StandardCharsets.UTF_8
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.time.Instant import java.time.Instant
import java.util.* import java.util.Currency
import java.util.Date
import java.util.UUID
import javax.security.auth.x500.X500Principal import javax.security.auth.x500.X500Principal
import kotlin.collections.ArrayList import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.component3
import kotlin.collections.component4
@RunWith(Parameterized::class) @RunWith(Parameterized::class)
class JacksonSupportTest(@Suppress("unused") private val name: String, factory: JsonFactory) { class JacksonSupportTest(@Suppress("unused") private val name: String, factory: JsonFactory) {
@ -90,23 +119,12 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
@Before @Before
fun setup() { fun setup() {
val unsignedAttachment = object : AbstractAttachment({ byteArrayOf() }, "test") { services = MockServices(
override val id: SecureHash get() = throw UnsupportedOperationException() listOf("net.corda.testing.contracts"),
} MINI_CORP,
testNetworkParameters(minimumPlatformVersion = 4)
val attachments = rigorousMock<AttachmentStorage>().also { )
doReturn(unsignedAttachment).whenever(it).openAttachment(any())
}
services = rigorousMock()
cordappProvider = rigorousMock() cordappProvider = rigorousMock()
val networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
val networkParametersService = rigorousMock<NetworkParametersService>().also {
doReturn(networkParameters.serialize().hash).whenever(it).currentHash
}
doReturn(networkParametersService).whenever(services).networkParametersService
doReturn(cordappProvider).whenever(services).cordappProvider
doReturn(networkParameters).whenever(services).networkParameters
doReturn(attachments).whenever(services).attachments
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -263,17 +281,6 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
@Test(timeout=300_000) @Test(timeout=300_000)
fun `SignedTransaction (WireTransaction)`() { fun `SignedTransaction (WireTransaction)`() {
val attachmentId = SecureHash.randomSHA256() val attachmentId = SecureHash.randomSHA256()
doReturn(attachmentId).whenever(cordappProvider).getContractAttachmentID(DummyContract.PROGRAM_ID)
val attachmentStorage = rigorousMock<AttachmentStorage>()
doReturn(attachmentStorage).whenever(services).attachments
doReturn(mock<TransactionStorage>()).whenever(services).validatedTransactions
doReturn(mock<IdentityService>()).whenever(services).identityService
val attachment = rigorousMock<ContractAttachment>()
doReturn(attachment).whenever(attachmentStorage).openAttachment(attachmentId)
doReturn(attachmentId).whenever(attachment).id
doReturn(emptyList<Party>()).whenever(attachment).signerKeys
doReturn(setOf(DummyContract.PROGRAM_ID)).whenever(attachment).allContracts
doReturn("app").whenever(attachment).uploader
val wtx = TransactionBuilder( val wtx = TransactionBuilder(
notary = DUMMY_NOTARY, notary = DUMMY_NOTARY,

View File

@ -26,7 +26,7 @@ class StringToMethodCallParserTest {
"simple" to "simple", "simple" to "simple",
"string noteTextWord: A test of barewords" to "A test of barewords", "string noteTextWord: A test of barewords" to "A test of barewords",
"twoStrings a: Some words, b: ' and some words, like, Kirk, would, speak'" to "Some words and some words, like, Kirk, would, speak", "twoStrings a: Some words, b: ' and some words, like, Kirk, would, speak'" to "Some words and some words, like, Kirk, would, speak",
"simpleObject hash: $randomHash" to randomHash.toUpperCase(), "simpleObject hash: $randomHash" to randomHash.uppercase(Locale.getDefault()),
"complexObject pair: { first: 12, second: Word up brother }" to Pair(12, "Word up brother"), "complexObject pair: { first: 12, second: Word up brother }" to Pair(12, "Word up brother"),
"overload a: A" to "A", "overload a: A" to "A",
"overload a: A, b: B" to "AB" "overload a: A, b: B" to "AB"
@ -51,7 +51,6 @@ class StringToMethodCallParserTest {
val result = parser.parse(Target(), "complexNestedObject pairs: { first: 101, second: [ A, B, C ] }").invoke() val result = parser.parse(Target(), "complexNestedObject pairs: { first: 101, second: [ A, B, C ] }").invoke()
assertTrue(result is Pair<*,*>) assertTrue(result is Pair<*,*>)
result as Pair<*,*>
assertEquals(101, result.first) assertEquals(101, result.first)

View File

@ -1,28 +1,27 @@
// JDK 11 JavaFX // JDK 11 JavaFX
plugins { plugins {
id 'org.openjfx.javafxplugin' version '0.0.7' apply false id 'org.openjfx.javafxplugin' version '0.0.7' apply false
id 'corda.common-publishing'
} }
if (JavaVersion.current().isJava9Compatible()) { apply plugin: 'org.openjfx.javafxplugin'
apply plugin: 'org.openjfx.javafxplugin' javafx {
javafx { version = "11.0.2"
version = "11.0.2" modules = [
modules = ['javafx.controls', 'javafx.controls',
'javafx.fxml' 'javafx.fxml'
] ]
}
} }
apply plugin: 'kotlin'
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory'
description 'Corda client JavaFX modules' description 'Corda client JavaFX modules'
//noinspection GroovyAssignabilityCheck //noinspection GroovyAssignabilityCheck
configurations { configurations {
integrationTestCompile.extendsFrom testCompile integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntime.extendsFrom testRuntime integrationTestRuntime.extendsFrom testRuntimeOnly
} }
sourceSets { sourceSets {
@ -39,23 +38,26 @@ sourceSets {
// build/reports/project/dependencies/index.html for green highlighted parts of the tree. // build/reports/project/dependencies/index.html for green highlighted parts of the tree.
dependencies { dependencies {
compile project(':core') implementation project(':core')
compile project(':finance:contracts') implementation project(':finance:contracts')
compile project(':finance:workflows') implementation project(':finance:workflows')
compile project(':client:rpc') implementation project(':client:rpc')
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "com.google.guava:guava:$guava_version"
compile "com.google.guava:guava:$guava_version" implementation "io.reactivex:rxjava:$rxjava_version"
// For caches rather than guava
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
// ReactFX: Functional reactive UI programming. // ReactFX: Functional reactive UI programming.
compile 'org.reactfx:reactfx:2.0-M5' implementation 'org.reactfx:reactfx:2.0-M5'
compile 'org.fxmisc.easybind:easybind:1.0.3' implementation 'org.fxmisc.easybind:easybind:1.0.3'
// Artemis Client: ability to connect to an Artemis broker and control it. // Artemis Client: ability to connect to an Artemis broker and control it.
// TODO: remove the forced update of commons-collections and beanutils when artemis updates them // TODO: remove the forced update of commons-collections and beanutils when artemis updates them
compile "org.apache.commons:commons-collections4:${commons_collections_version}" implementation "org.apache.commons:commons-collections4:${commons_collections_version}"
compile "commons-beanutils:commons-beanutils:${beanutils_version}" implementation "commons-beanutils:commons-beanutils:${beanutils_version}"
compile("org.apache.activemq:artemis-core-client:${artemis_version}") { implementation("org.apache.activemq:artemis-core-client:${artemis_version}") {
exclude group: 'org.jgroups', module: 'jgroups' exclude group: 'org.jgroups', module: 'jgroups'
} }
@ -67,13 +69,14 @@ dependencies {
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
testCompile "org.assertj:assertj-core:${assertj_version}" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testImplementation "org.assertj:assertj-core:${assertj_version}"
testCompile project(':test-utils') testImplementation project(':test-utils')
// Integration test helpers // Integration test helpers
integrationTestCompile "junit:junit:$junit_version" integrationTestImplementation "junit:junit:$junit_version"
integrationTestCompile project(':node-driver') integrationTestImplementation project(':node-driver')
} }
task integrationTest(type: Test) { task integrationTest(type: Test) {
@ -88,6 +91,11 @@ jar {
} }
} }
publish { publishing {
name jar.baseName publications {
maven(MavenPublication) {
artifactId jar.baseName
from components.java
}
}
} }

View File

@ -40,4 +40,4 @@ inline fun <reified M : Any, T> observableList(noinline observableListProperty:
TrackedDelegate.ObservableListDelegate(M::class, observableListProperty) TrackedDelegate.ObservableListDelegate(M::class, observableListProperty)
inline fun <reified M : Any, T> observableListReadOnly(noinline observableListProperty: (M) -> ObservableList<out T>) = inline fun <reified M : Any, T> observableListReadOnly(noinline observableListProperty: (M) -> ObservableList<out T>) =
TrackedDelegate.ObservableListReadOnlyDelegate(M::class, observableListProperty) TrackedDelegate.ObservableListReadOnlyDelegate(M::class, observableListProperty)

View File

@ -14,13 +14,14 @@ import java.util.stream.Collectors
* Utility bindings for the [Amount] type, similar in spirit to [Bindings] * Utility bindings for the [Amount] type, similar in spirit to [Bindings]
*/ */
object AmountBindings { object AmountBindings {
@Suppress("SpreadOperator")
fun <T : Any> sum(amounts: ObservableList<Amount<T>>, token: T): MonadicBinding<Amount<T>> = EasyBind.map( fun <T : Any> sum(amounts: ObservableList<Amount<T>>, token: T): MonadicBinding<Amount<T>> = EasyBind.map(
Bindings.createLongBinding({ Bindings.createLongBinding({
amounts.stream().collect(Collectors.summingLong { amounts.stream().collect(Collectors.summingLong {
require(it.token == token) require(it.token == token)
it.quantity it.quantity
}) })
}, arrayOf(amounts)) }, *arrayOf(amounts))
) { sum -> Amount(sum.toLong(), token) } ) { sum -> Amount(sum.toLong(), token) }
fun exchange( fun exchange(
@ -35,6 +36,7 @@ object AmountBindings {
} }
} }
@Suppress("SpreadOperator")
fun sumAmountExchange( fun sumAmountExchange(
amounts: ObservableList<Amount<Currency>>, amounts: ObservableList<Amount<Currency>>,
currency: ObservableValue<Currency>, currency: ObservableValue<Currency>,
@ -45,7 +47,7 @@ object AmountBindings {
EasyBind.map( EasyBind.map(
Bindings.createLongBinding({ Bindings.createLongBinding({
amounts.stream().collect(Collectors.summingLong { exchange(it) }) amounts.stream().collect(Collectors.summingLong { exchange(it) })
}, arrayOf(amounts)) }, *arrayOf(amounts))
) { Amount(it.toLong(), currencyValue) } ) { Amount(it.toLong(), currencyValue) }
} }
} }

View File

@ -120,7 +120,7 @@ fun <A> ObservableList<out A>.filter(predicate: ObservableValue<(A) -> Boolean>)
*/ */
fun <A> ObservableList<out A?>.filterNotNull(): ObservableList<A> { fun <A> ObservableList<out A?>.filterNotNull(): ObservableList<A> {
//TODO This is a tactical work round for an issue with SAM conversion (https://youtrack.jetbrains.com/issue/ALL-1552) so that the M10 explorer works. //TODO This is a tactical work round for an issue with SAM conversion (https://youtrack.jetbrains.com/issue/ALL-1552) so that the M10 explorer works.
return uncheckedCast(uncheckedCast<Any, ObservableList<A?>>(this).filtered { t -> t != null }) return uncheckedCast(uncheckedCast<Any, ObservableList<A>>(this).filtered { t -> t != null })
} }
/** /**
@ -128,6 +128,7 @@ fun <A> ObservableList<out A?>.filterNotNull(): ObservableList<A> {
* val concatenatedNames = people.foldObservable("", { names, person -> names + person.name }) * val concatenatedNames = people.foldObservable("", { names, person -> names + person.name })
* val concatenatedNames2 = people.map(Person::name).fold("", String::plus) * val concatenatedNames2 = people.map(Person::name).fold("", String::plus)
*/ */
@Suppress("SpreadOperator")
fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B, A) -> B): ObservableValue<B> { fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B, A) -> B): ObservableValue<B> {
return Bindings.createObjectBinding({ return Bindings.createObjectBinding({
var current = initial var current = initial
@ -135,7 +136,7 @@ fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B,
current = folderFunction(current, it) current = folderFunction(current, it)
} }
current current
}, arrayOf(this)) }, *arrayOf(this))
} }
/** /**
@ -285,6 +286,7 @@ fun <A> ObservableList<A>.first(): ObservableValue<A?> {
return getValueAt(0) return getValueAt(0)
} }
@Suppress("SpreadOperator")
fun <A> ObservableList<A>.last(): ObservableValue<A?> { fun <A> ObservableList<A>.last(): ObservableValue<A?> {
return Bindings.createObjectBinding({ return Bindings.createObjectBinding({
if (size > 0) { if (size > 0) {
@ -292,7 +294,7 @@ fun <A> ObservableList<A>.last(): ObservableValue<A?> {
} else { } else {
null null
} }
}, arrayOf(this)) }, *arrayOf(this))
} }
fun <T : Any> ObservableList<T>.unique(): ObservableList<T> { fun <T : Any> ObservableList<T>.unique(): ObservableList<T> {
@ -303,24 +305,27 @@ fun <T : Any, K : Any> ObservableList<T>.distinctBy(toKey: (T) -> K): Observable
return AggregatedList(this, toKey, { _, entryList -> entryList[0] }) return AggregatedList(this, toKey, { _, entryList -> entryList[0] })
} }
@Suppress("SpreadOperator")
fun ObservableValue<*>.isNotNull(): BooleanBinding { fun ObservableValue<*>.isNotNull(): BooleanBinding {
return Bindings.createBooleanBinding({ this.value != null }, arrayOf(this)) return Bindings.createBooleanBinding({ this.value != null }, *arrayOf(this))
} }
/** /**
* Return first element of the observable list as observable value. * Return first element of the observable list as observable value.
* Return provided default value if the list is empty. * Return provided default value if the list is empty.
*/ */
@Suppress("SpreadOperator")
fun <A> ObservableList<A>.firstOrDefault(default: ObservableValue<A?>, predicate: (A) -> Boolean): ObservableValue<A?> { fun <A> ObservableList<A>.firstOrDefault(default: ObservableValue<A?>, predicate: (A) -> Boolean): ObservableValue<A?> {
return Bindings.createObjectBinding({ this.firstOrNull(predicate) ?: default.value }, arrayOf(this, default)) return Bindings.createObjectBinding({ this.firstOrNull(predicate) ?: default.value }, *arrayOf(this, default))
} }
/** /**
* Return first element of the observable list as observable value. * Return first element of the observable list as observable value.
* Return ObservableValue(null) if the list is empty. * Return ObservableValue(null) if the list is empty.
*/ */
@Suppress("SpreadOperator")
fun <A> ObservableList<A>.firstOrNullObservable(predicate: (A) -> Boolean): ObservableValue<A?> { fun <A> ObservableList<A>.firstOrNullObservable(predicate: (A) -> Boolean): ObservableValue<A?> {
return Bindings.createObjectBinding({ this.firstOrNull(predicate) }, arrayOf(this)) return Bindings.createObjectBinding({ this.firstOrNull(predicate) }, *arrayOf(this))
} }
/** /**

View File

@ -1,7 +1,6 @@
apply plugin: 'kotlin' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'corda.common-publishing'
apply plugin: 'com.jfrog.artifactory'
description 'Corda client mock modules' description 'Corda client mock modules'
@ -9,9 +8,9 @@ description 'Corda client mock modules'
// build/reports/project/dependencies/index.html for green highlighted parts of the tree. // build/reports/project/dependencies/index.html for green highlighted parts of the tree.
dependencies { dependencies {
compile project(":core") implementation project(":core")
compile project(':finance:workflows') implementation project(':finance:workflows')
compile project(':finance:contracts') implementation project(':finance:contracts')
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "junit:junit:$junit_version" testImplementation "junit:junit:$junit_version"
@ -21,9 +20,9 @@ dependencies {
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
// Unit testing helpers. // Unit testing helpers.
testCompile "org.assertj:assertj-core:${assertj_version}" testImplementation "org.assertj:assertj-core:${assertj_version}"
testCompile project(':test-utils') testImplementation project(':test-utils')
} }
jar { jar {
@ -39,6 +38,11 @@ jar {
} }
} }
publish { publishing {
name jar.baseName publications {
maven(MavenPublication) {
artifactId jar.baseName
from components.java
}
}
} }

View File

@ -1,28 +1,20 @@
apply plugin: 'kotlin' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'net.corda.plugins.api-scanner' apply plugin: 'net.corda.plugins.api-scanner'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'corda.common-publishing'
apply plugin: 'us.kirchmeier.capsule'
description 'Corda client RPC modules' description 'Corda client RPC modules'
//noinspection GroovyAssignabilityCheck //noinspection GroovyAssignabilityCheck
configurations { configurations {
integrationTestCompile.extendsFrom testCompile integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
smokeTestCompile.extendsFrom compile smokeTestImplementation.extendsFrom compile
smokeTestRuntimeOnly.extendsFrom runtimeOnly smokeTestRuntimeOnly.extendsFrom runtimeOnly
} }
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
sourceSets { sourceSets {
integrationTest { integrationTest {
kotlin { kotlin {
@ -55,8 +47,58 @@ sourceSets {
} }
} }
dependencies {
implementation project(':core')
implementation project(':node-api')
implementation project(':serialization')
// For caches rather than guava
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "io.reactivex:rxjava:$rxjava_version"
implementation("org.apache.activemq:artemis-core-client:${artemis_version}") {
exclude group: 'org.jgroups', module: 'jgroups'
}
implementation "com.google.guava:guava-testlib:$guava_version"
testImplementation project(':node')
testImplementation project(':node-driver')
testImplementation project(':client:mock')
testImplementation project(':core-test-utils')
testImplementation "junit:junit:$junit_version"
// Unit testing helpers.
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testImplementation "org.assertj:assertj-core:${assertj_version}"
testImplementation "io.dropwizard.metrics:metrics-core:$metrics_version"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
integrationTestImplementation project(path: ':node-api', configuration: 'testArtifacts')
integrationTestImplementation project(':common-configuration-parsing')
integrationTestImplementation project(':finance:contracts')
integrationTestImplementation project(':finance:workflows')
integrationTestImplementation project(':test-utils')
integrationTestImplementation "co.paralleluniverse:quasar-core:$quasar_version"
integrationTestImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
smokeTestImplementation project(':core')
smokeTestImplementation project(':node-api')
smokeTestImplementation project(':finance:contracts')
smokeTestImplementation project(':finance:workflows')
smokeTestImplementation project(':smoke-test-utils')
smokeTestImplementation project(':testing:cordapps:sleeping')
smokeTestImplementation "junit:junit:$junit_version"
smokeTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
smokeTestImplementation "com.google.guava:guava:$guava_version"
smokeTestImplementation "org.hamcrest:hamcrest-library:2.1"
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
}
processSmokeTestResources { processSmokeTestResources {
from(project(':node:capsule').tasks['buildCordaJAR']) { // Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
from(project(":node:capsule").tasks['buildCordaJAR']) {
rename 'corda-(.*)', 'corda.jar' rename 'corda-(.*)', 'corda.jar'
} }
from(project(':finance:workflows').tasks['jar']) { from(project(':finance:workflows').tasks['jar']) {
@ -70,52 +112,12 @@ processSmokeTestResources {
} }
} }
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in tasks.register('integrationTest', Test) {
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
dependencies {
compile project(':core')
compile project(':node-api')
// For caches rather than guava
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_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}"
// Unit testing helpers.
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testCompile "org.assertj:assertj-core:${assertj_version}"
testCompile project(':node-driver')
testCompile project(':client:mock')
integrationTestCompile project(path: ':node-api', configuration: 'testArtifacts')
// Smoke tests do NOT have any Node code on the classpath!
smokeTestCompile project(':smoke-test-utils')
smokeTestCompile project(':finance:contracts')
smokeTestCompile project(':finance:workflows')
smokeTestCompile project(':testing:cordapps:sleeping')
smokeTestCompile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
smokeTestCompile "org.apache.logging.log4j:log4j-core:$log4j_version"
smokeTestCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
smokeTestImplementation "junit:junit:$junit_version"
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
// JDK11: required by Quasar at run-time
smokeTestRuntimeOnly "com.esotericsoftware:kryo:$kryo_version"
}
task integrationTest(type: Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath classpath = sourceSets.integrationTest.runtimeClasspath
} }
task smokeTest(type: Test) { tasks.register('smokeTest', Test) {
testClassesDirs = sourceSets.smokeTest.output.classesDirs testClassesDirs = sourceSets.smokeTest.output.classesDirs
classpath = sourceSets.smokeTest.runtimeClasspath classpath = sourceSets.smokeTest.runtimeClasspath
} }
@ -127,6 +129,11 @@ jar {
} }
} }
publish { publishing {
name jar.baseName publications {
maven(MavenPublication) {
artifactId jar.baseName
from components.java
}
}
} }

View File

@ -54,7 +54,7 @@ import org.junit.Test
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.net.URLClassLoader import java.net.URLClassLoader
import java.nio.file.Paths import java.nio.file.Paths
import java.util.* import java.util.Currency
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.ScheduledExecutorService

View File

@ -1,12 +1,12 @@
package net.corda.client.rpc package net.corda.client.rpc
import com.nhaarman.mockito_kotlin.any import org.mockito.kotlin.any
import com.nhaarman.mockito_kotlin.atLeastOnce import org.mockito.kotlin.atLeastOnce
import com.nhaarman.mockito_kotlin.mock import org.mockito.kotlin.mock
import com.nhaarman.mockito_kotlin.never import org.mockito.kotlin.never
import com.nhaarman.mockito_kotlin.times import org.mockito.kotlin.times
import com.nhaarman.mockito_kotlin.verify import org.mockito.kotlin.verify
import com.nhaarman.mockito_kotlin.whenever import org.mockito.kotlin.whenever
import net.corda.client.rpc.internal.RPCClient import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.ext.RPCConnectionListener import net.corda.client.rpc.ext.RPCConnectionListener
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
@ -338,4 +338,4 @@ class RPCConnectionListenerTest(@Suppress("unused") private val iteration: Int)
} }
} }
} }
} }

View File

@ -1,6 +1,6 @@
package net.corda.client.rpc package net.corda.client.rpc
import com.nhaarman.mockito_kotlin.mock import org.mockito.kotlin.mock
import net.corda.client.rpc.RPCMultipleInterfacesTests.StringRPCOpsImpl.testPhrase import net.corda.client.rpc.RPCMultipleInterfacesTests.StringRPCOpsImpl.testPhrase
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps

View File

@ -544,7 +544,7 @@ class RPCStabilityTests {
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@Ignore // TODO: This is ignored because Artemis slow consumers are broken. I'm not deleting it in case we can get the feature fixed. @Ignore // TODO: This is ignored because Artemis slow consumers are broken. I'm not deleting it in case we can get the feature fixed.
fun `slow consumers are kicked`() { fun `slow consumers are kicked`() {
rpcDriver { rpcDriver {
val server = startRpcServer(maxBufferedBytesPerClient = 10 * 1024 * 1024, ops = SlowConsumerRPCOpsImpl()).get() val server = startRpcServer(maxBufferedBytesPerClient = 10 * 1024 * 1024, ops = SlowConsumerRPCOpsImpl()).get()
@ -552,7 +552,7 @@ class RPCStabilityTests {
// Construct an RPC session manually so that we can hang in the message handler // Construct an RPC session manually so that we can hang in the message handler
val myQueue = "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.test.${random63BitValue()}" val myQueue = "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.test.${random63BitValue()}"
val session = startArtemisSession(server.broker.hostAndPort!!) val session = startArtemisSession(server.broker.hostAndPort!!)
session.createQueue(QueueConfiguration(myQueue) session.createQueue(QueueConfiguration.of(myQueue)
.setRoutingType(ActiveMQDefaultConfiguration.getDefaultRoutingType()) .setRoutingType(ActiveMQDefaultConfiguration.getDefaultRoutingType())
.setAddress(myQueue) .setAddress(myQueue)
.setTemporary(true) .setTemporary(true)
@ -569,7 +569,7 @@ class RPCStabilityTests {
val message = session.createMessage(false) val message = session.createMessage(false)
val request = RPCApi.ClientToServer.RpcRequest( val request = RPCApi.ClientToServer.RpcRequest(
clientAddress = SimpleString(myQueue), clientAddress = SimpleString.of(myQueue),
methodName = SlowConsumerRPCOps::streamAtInterval.name, methodName = SlowConsumerRPCOps::streamAtInterval.name,
serialisedArguments = listOf(100.millis, 1234).serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT), serialisedArguments = listOf(100.millis, 1234).serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT),
replyId = Trace.InvocationId.newInstance(), replyId = Trace.InvocationId.newInstance(),
@ -593,7 +593,7 @@ class RPCStabilityTests {
// Construct an RPC client session manually // Construct an RPC client session manually
val myQueue = "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.test.${random63BitValue()}" val myQueue = "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.test.${random63BitValue()}"
val session = startArtemisSession(server.broker.hostAndPort!!) val session = startArtemisSession(server.broker.hostAndPort!!)
session.createQueue(QueueConfiguration(myQueue) session.createQueue(QueueConfiguration.of(myQueue)
.setRoutingType(ActiveMQDefaultConfiguration.getDefaultRoutingType()) .setRoutingType(ActiveMQDefaultConfiguration.getDefaultRoutingType())
.setAddress(myQueue) .setAddress(myQueue)
.setTemporary(true) .setTemporary(true)
@ -612,7 +612,7 @@ class RPCStabilityTests {
val message = session.createMessage(false) val message = session.createMessage(false)
val request = RPCApi.ClientToServer.RpcRequest( val request = RPCApi.ClientToServer.RpcRequest(
clientAddress = SimpleString(myQueue), clientAddress = SimpleString.of(myQueue),
methodName = DummyOps::protocolVersion.name, methodName = DummyOps::protocolVersion.name,
serialisedArguments = emptyList<Any>().serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT), serialisedArguments = emptyList<Any>().serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT),
replyId = Trace.InvocationId.newInstance(), replyId = Trace.InvocationId.newInstance(),
@ -669,4 +669,4 @@ fun RPCDriverDSL.pollUntilClientNumber(server: RpcServerHandle, expected: Int) {
val clientAddresses = server.broker.serverControl.addressNames.filter { it.startsWith(RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX) } val clientAddresses = server.broker.serverControl.addressNames.filter { it.startsWith(RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX) }
clientAddresses.size == expected clientAddresses.size == expected
}.get() }.get()
} }

View File

@ -110,11 +110,11 @@ class CordaRPCClientReconnectionTest {
assertThatThrownBy { assertThatThrownBy {
val node = startNode () val node = startNode ()
val client = CordaRPCClient(node.rpcAddress, config.copy(minimumServerProtocolVersion = 100, maxReconnectAttempts = 1)) val client = CordaRPCClient(node.rpcAddress, config.copy(minimumServerProtocolVersion = 999, maxReconnectAttempts = 1))
client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect) client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)
} }
.isInstanceOf(UnrecoverableRPCException::class.java) .isInstanceOf(UnrecoverableRPCException::class.java)
.hasMessageStartingWith("Requested minimum protocol version (100) is higher than the server's supported protocol version ") .hasMessageStartingWith("Requested minimum protocol version (999) is higher than the server's supported protocol version ")
} }
} }
@ -638,4 +638,4 @@ class CordaRPCClientReconnectionTest {
throw IllegalStateException("bye") throw IllegalStateException("bye")
} }
} }
} }

View File

@ -7,12 +7,12 @@ import com.github.benmanes.caffeine.cache.LoadingCache
import net.corda.core.internal.NamedCacheFactory import net.corda.core.internal.NamedCacheFactory
class ClientCacheFactory : NamedCacheFactory { class ClientCacheFactory : NamedCacheFactory {
override fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String): Cache<K, V> { override fun <K : Any, V : Any> buildNamed(caffeine: Caffeine<in K, in V>, name: String): Cache<K, V> {
checkCacheName(name) checkCacheName(name)
return caffeine.build<K, V>() return caffeine.build<K, V>()
} }
override fun <K, V> buildNamed(caffeine: Caffeine<in K, in V>, name: String, loader: CacheLoader<K, V>): LoadingCache<K, V> { override fun <K : Any, V : Any> buildNamed(caffeine: Caffeine<in K, in V>, name: String, loader: CacheLoader<K, V>): LoadingCache<K, V> {
checkCacheName(name) checkCacheName(name)
return caffeine.build<K, V>(loader) return caffeine.build<K, V>(loader)
} }

View File

@ -150,7 +150,6 @@ internal class RPCClientProxyHandler(
} }
} }
@Suppress("TooGenericExceptionCaught")
private fun closeObservable(observable: UnicastSubject<Notification<*>>) { private fun closeObservable(observable: UnicastSubject<Notification<*>>) {
// Notify listeners of the observables that the connection is being terminated. // Notify listeners of the observables that the connection is being terminated.
try { try {
@ -300,7 +299,7 @@ internal class RPCClientProxyHandler(
class FailoverHandler(private val detected: () -> Unit = {}, class FailoverHandler(private val detected: () -> Unit = {},
private val completed: () -> Unit = {}, private val completed: () -> Unit = {},
private val failed: () -> Unit = {}): FailoverEventListener { private val failed: () -> Unit = {}): FailoverEventListener {
override fun failoverEvent(eventType: FailoverEventType?) { override fun failoverEvent(eventType: FailoverEventType) {
when (eventType) { when (eventType) {
FailoverEventType.FAILURE_DETECTED -> { detected() } FailoverEventType.FAILURE_DETECTED -> { detected() }
FailoverEventType.FAILOVER_COMPLETED -> { completed() } FailoverEventType.FAILOVER_COMPLETED -> { completed() }
@ -346,7 +345,7 @@ internal class RPCClientProxyHandler(
impersonatedActor, impersonatedActor,
rpcClientTelemetry.telemetryService.getCurrentTelemetryData() rpcClientTelemetry.telemetryService.getCurrentTelemetryData()
) )
val replyFuture = SettableFuture.create<Any>() val replyFuture = SettableFuture.create<Any?>()
require(rpcReplyMap.put(replyId, replyFuture) == null) { require(rpcReplyMap.put(replyId, replyFuture) == null) {
"Generated several RPC requests with same ID $replyId" "Generated several RPC requests with same ID $replyId"
} }
@ -589,7 +588,6 @@ internal class RPCClientProxyHandler(
} }
if (observableIds != null) { if (observableIds != null) {
log.debug { "Reaping ${observableIds.size} observables" } log.debug { "Reaping ${observableIds.size} observables" }
@Suppress("TooGenericExceptionCaught")
try { try {
sendMessage(RPCApi.ClientToServer.ObservablesClosed(observableIds)) sendMessage(RPCApi.ClientToServer.ObservablesClosed(observableIds))
} catch(ex: Exception) { } catch(ex: Exception) {
@ -654,9 +652,9 @@ internal class RPCClientProxyHandler(
producerSession = sessionFactory!!.createSession(rpcUsername, rpcPassword, false, true, true, false, DEFAULT_ACK_BATCH_SIZE) producerSession = sessionFactory!!.createSession(rpcUsername, rpcPassword, false, true, true, false, DEFAULT_ACK_BATCH_SIZE)
rpcProducer = producerSession!!.createProducer(RPCApi.RPC_SERVER_QUEUE_NAME) rpcProducer = producerSession!!.createProducer(RPCApi.RPC_SERVER_QUEUE_NAME)
consumerSession = sessionFactory!!.createSession(rpcUsername, rpcPassword, false, true, true, false, 16384) consumerSession = sessionFactory!!.createSession(rpcUsername, rpcPassword, false, true, true, false, 16384)
clientAddress = SimpleString("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$rpcUsername.${random63BitValue()}") clientAddress = SimpleString.of("${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.$rpcUsername.${random63BitValue()}")
log.debug { "Client address: $clientAddress" } log.debug { "Client address: $clientAddress" }
consumerSession!!.createQueue(QueueConfiguration(clientAddress).setAddress(clientAddress).setRoutingType(RoutingType.ANYCAST) consumerSession!!.createQueue(QueueConfiguration.of(clientAddress).setAddress(clientAddress).setRoutingType(RoutingType.ANYCAST)
.setTemporary(true).setDurable(false)) .setTemporary(true).setDurable(false))
rpcConsumer = consumerSession!!.createConsumer(clientAddress) rpcConsumer = consumerSession!!.createConsumer(clientAddress)
rpcConsumer!!.setMessageHandler(this::artemisMessageHandler) rpcConsumer!!.setMessageHandler(this::artemisMessageHandler)

View File

@ -1,14 +1,15 @@
package net.corda.client.rpc.internal package net.corda.client.rpc.internal
import net.corda.core.internal.telemetry.OpenTelemetryComponent
import net.corda.core.internal.telemetry.SimpleLogTelemetryComponent import net.corda.core.internal.telemetry.SimpleLogTelemetryComponent
import net.corda.core.internal.telemetry.TelemetryServiceImpl import net.corda.core.internal.telemetry.TelemetryServiceImpl
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.telemetry.OpenTelemetryComponent
class RPCClientTelemetry(val serviceName: String, val openTelemetryEnabled: Boolean, class RPCClientTelemetry(serviceName: String,
val simpleLogTelemetryEnabled: Boolean, val spanStartEndEventsEnabled: Boolean, val openTelemetryEnabled: Boolean,
val simpleLogTelemetryEnabled: Boolean,
val spanStartEndEventsEnabled: Boolean,
val copyBaggageToTags: Boolean) { val copyBaggageToTags: Boolean) {
companion object { companion object {
private val log = contextLogger() private val log = contextLogger()
} }
@ -39,4 +40,4 @@ class RPCClientTelemetry(val serviceName: String, val openTelemetryEnabled: Bool
fun <T> getTelemetryHandle(telemetryClass: Class<T>): T? { fun <T> getTelemetryHandle(telemetryClass: Class<T>): T? {
return telemetryService.getTelemetryHandle(telemetryClass) return telemetryService.getTelemetryHandle(telemetryClass)
} }
} }

View File

@ -1,6 +1,5 @@
package net.corda.java.rpc; package net.corda.java.rpc;
import net.corda.client.rpc.CordaRPCConnection;
import net.corda.core.contracts.Amount; import net.corda.core.contracts.Amount;
import net.corda.core.identity.CordaX500Name; import net.corda.core.identity.CordaX500Name;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
@ -10,88 +9,48 @@ import net.corda.core.utilities.OpaqueBytes;
import net.corda.finance.flows.AbstractCashFlow; import net.corda.finance.flows.AbstractCashFlow;
import net.corda.finance.flows.CashIssueFlow; import net.corda.finance.flows.CashIssueFlow;
import net.corda.nodeapi.internal.config.User; import net.corda.nodeapi.internal.config.User;
import net.corda.smoketesting.NodeConfig; import net.corda.smoketesting.NodeParams;
import net.corda.smoketesting.NodeProcess; import net.corda.smoketesting.NodeProcess;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.io.IOException; import java.util.Currency;
import java.nio.file.Files; import java.util.HashSet;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static kotlin.test.AssertionsKt.assertEquals; import static kotlin.test.AssertionsKt.assertEquals;
import static kotlin.test.AssertionsKt.fail;
import static net.corda.finance.workflows.GetBalances.getCashBalance; import static net.corda.finance.workflows.GetBalances.getCashBalance;
import static net.corda.kotlin.rpc.StandaloneCordaRPClientTest.gatherCordapps;
public class StandaloneCordaRPCJavaClientTest { public class StandaloneCordaRPCJavaClientTest {
private final User superUser = new User("superUser", "test", new HashSet<>(singletonList("ALL")));
public static void copyCordapps(NodeProcess.Factory factory, NodeConfig notaryConfig) { private final AtomicInteger port = new AtomicInteger(15000);
Path cordappsDir = (factory.baseDirectory(notaryConfig).resolve(NodeProcess.CORDAPPS_DIR_NAME)); private final NodeProcess.Factory factory = new NodeProcess.Factory();
try {
Files.createDirectories(cordappsDir);
} catch (IOException ex) {
fail("Failed to create directories");
}
try (Stream<Path> paths = Files.walk(Paths.get("build", "resources", "smokeTest"))) {
paths.filter(path -> path.toFile().getName().startsWith("cordapp")).forEach(file -> {
try {
Files.copy(file, cordappsDir.resolve(file.getFileName()));
} catch (IOException ex) {
fail("Failed to copy cordapp jar");
}
});
} catch (IOException e) {
fail("Failed to walk files");
}
}
private List<String> perms = singletonList("ALL");
private Set<String> permSet = new HashSet<>(perms);
private User superUser = new User("superUser", "test", permSet);
private AtomicInteger port = new AtomicInteger(15000);
private NodeProcess notary;
private CordaRPCOps rpcProxy; private CordaRPCOps rpcProxy;
private CordaRPCConnection connection;
private Party notaryNodeIdentity; private Party notaryNodeIdentity;
private NodeConfig notaryConfig = new NodeConfig(
new CordaX500Name("Notary Service", "Zurich", "CH"),
port.getAndIncrement(),
port.getAndIncrement(),
port.getAndIncrement(),
true,
singletonList(superUser),
true
);
@Before @Before
public void setUp() { public void setUp() {
NodeProcess.Factory factory = new NodeProcess.Factory(); NodeProcess notary = factory.createNotaries(new NodeParams(
copyCordapps(factory, notaryConfig); new CordaX500Name("Notary Service", "Zurich", "CH"),
notary = factory.create(notaryConfig); port.getAndIncrement(),
connection = notary.connect(superUser); port.getAndIncrement(),
rpcProxy = connection.getProxy(); port.getAndIncrement(),
singletonList(superUser),
gatherCordapps()
)).get(0);
rpcProxy = notary.connect(superUser).getProxy();
notaryNodeIdentity = rpcProxy.nodeInfo().getLegalIdentities().get(0); notaryNodeIdentity = rpcProxy.nodeInfo().getLegalIdentities().get(0);
} }
@After @After
public void done() { public void done() {
try { factory.close();
connection.close();
} finally {
if (notary != null) {
notary.close();
}
}
} }
@Test @Test

View File

@ -27,6 +27,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.minutes import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.GBP
import net.corda.finance.POUNDS import net.corda.finance.POUNDS
import net.corda.finance.SWISS_FRANCS import net.corda.finance.SWISS_FRANCS
import net.corda.finance.USD import net.corda.finance.USD
@ -35,89 +36,103 @@ import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow import net.corda.finance.flows.CashPaymentFlow
import net.corda.finance.workflows.getCashBalance import net.corda.finance.workflows.getCashBalance
import net.corda.finance.workflows.getCashBalances import net.corda.finance.workflows.getCashBalances
import net.corda.java.rpc.StandaloneCordaRPCJavaClientTest
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.sleeping.SleepingFlow import net.corda.sleeping.SleepingFlow
import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeParams
import net.corda.smoketesting.NodeProcess import net.corda.smoketesting.NodeProcess
import org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM
import org.hamcrest.text.MatchesPattern import org.hamcrest.text.MatchesPattern
import org.junit.After import org.junit.After
import org.junit.AfterClass
import org.junit.Before import org.junit.Before
import org.junit.BeforeClass
import org.junit.Ignore import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.ExpectedException import org.junit.rules.ExpectedException
import java.io.FilterInputStream import java.io.FilterInputStream
import java.io.InputStream import java.io.InputStream
import java.util.Currency import java.io.OutputStream.nullOutputStream
import java.nio.file.Path
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.io.path.Path
import kotlin.io.path.listDirectoryEntries
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertNotEquals import kotlin.test.assertNotEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
class StandaloneCordaRPClientTest { class StandaloneCordaRPClientTest {
private companion object { companion object {
private val log = contextLogger() private val log = contextLogger()
val superUser = User("superUser", "test", permissions = setOf("ALL")) val superUser = User("superUser", "test", permissions = setOf("ALL"))
val nonUser = User("nonUser", "test", permissions = emptySet()) val nonUser = User("nonUser", "test", permissions = emptySet())
val rpcUser = User("rpcUser", "test", permissions = setOf("InvokeRpc.startFlow", "InvokeRpc.killFlow")) val rpcUser = User("rpcUser", "test", permissions = setOf("InvokeRpc.startFlow", "InvokeRpc.killFlow"))
val flowUser = User("flowUser", "test", permissions = setOf("StartFlow.net.corda.finance.flows.CashIssueFlow")) val flowUser = User("flowUser", "test", permissions = setOf("StartFlow.net.corda.finance.flows.CashIssueFlow"))
val port = AtomicInteger(15200) val port = AtomicInteger(15200)
const val attachmentSize = 2116 const val ATTACHMENT_SIZE = 2116
val timeout = 60.seconds val timeout = 60.seconds
private val factory = NodeProcess.Factory()
private lateinit var notary: NodeProcess
private val notaryConfig = NodeParams(
legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
users = listOf(superUser, nonUser, rpcUser, flowUser),
cordappJars = gatherCordapps()
)
@BeforeClass
@JvmStatic
fun startNotary() {
notary = factory.createNotaries(notaryConfig)[0]
}
@AfterClass
@JvmStatic
fun close() {
factory.close()
}
@JvmStatic
fun gatherCordapps(): List<Path> {
return Path("build", "resources", "smokeTest").listDirectoryEntries("cordapp*.jar")
}
} }
private lateinit var factory: NodeProcess.Factory
private lateinit var notary: NodeProcess
private lateinit var rpcProxy: CordaRPCOps
private lateinit var connection: CordaRPCConnection private lateinit var connection: CordaRPCConnection
private lateinit var notaryNode: NodeInfo private lateinit var rpcProxy: CordaRPCOps
private lateinit var notaryNodeIdentity: Party private lateinit var notaryNodeIdentity: Party
private val notaryConfig = NodeConfig(
legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
isNotary = true,
users = listOf(superUser, nonUser, rpcUser, flowUser)
)
@get:Rule @get:Rule
val exception: ExpectedException = ExpectedException.none() val exception: ExpectedException = ExpectedException.none()
@Before @Before
fun setUp() { fun setUp() {
factory = NodeProcess.Factory()
StandaloneCordaRPCJavaClientTest.copyCordapps(factory, notaryConfig)
notary = factory.create(notaryConfig)
connection = notary.connect(superUser) connection = notary.connect(superUser)
rpcProxy = connection.proxy rpcProxy = connection.proxy
notaryNode = fetchNotaryIdentity()
notaryNodeIdentity = rpcProxy.nodeInfo().legalIdentitiesAndCerts.first().party notaryNodeIdentity = rpcProxy.nodeInfo().legalIdentitiesAndCerts.first().party
} }
@After @After
fun done() { fun closeConnection() {
connection.use { connection.close()
notary.close()
}
} }
@Test(timeout=300_000) @Test(timeout=300_000)
fun `test attachments`() { fun `test attachments`() {
val attachment = InputStreamAndHash.createInMemoryTestZip(attachmentSize, 1) val attachment = InputStreamAndHash.createInMemoryTestZip(ATTACHMENT_SIZE, 1)
assertFalse(rpcProxy.attachmentExists(attachment.sha256)) assertFalse(rpcProxy.attachmentExists(attachment.sha256))
val id = attachment.inputStream.use { rpcProxy.uploadAttachment(it) } val id = attachment.inputStream.use { rpcProxy.uploadAttachment(it) }
assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash") assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash")
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use { it -> val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use {
it.copyTo(NULL_OUTPUT_STREAM) it.copyTo(nullOutputStream())
SecureHash.createSHA256(it.hash().asBytes()) SecureHash.createSHA256(it.hash().asBytes())
} }
assertEquals(attachment.sha256, hash) assertEquals(attachment.sha256, hash)
@ -126,13 +141,13 @@ class StandaloneCordaRPClientTest {
@Ignore("CORDA-1520 - After switching from Kryo to AMQP this test won't work") @Ignore("CORDA-1520 - After switching from Kryo to AMQP this test won't work")
@Test(timeout=300_000) @Test(timeout=300_000)
fun `test wrapped attachments`() { fun `test wrapped attachments`() {
val attachment = InputStreamAndHash.createInMemoryTestZip(attachmentSize, 1) val attachment = InputStreamAndHash.createInMemoryTestZip(ATTACHMENT_SIZE, 1)
assertFalse(rpcProxy.attachmentExists(attachment.sha256)) assertFalse(rpcProxy.attachmentExists(attachment.sha256))
val id = WrapperStream(attachment.inputStream).use { rpcProxy.uploadAttachment(it) } val id = WrapperStream(attachment.inputStream).use { rpcProxy.uploadAttachment(it) }
assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash") assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash")
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use { it -> val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use {
it.copyTo(NULL_OUTPUT_STREAM) it.copyTo(nullOutputStream())
SecureHash.createSHA256(it.hash().asBytes()) SecureHash.createSHA256(it.hash().asBytes())
} }
assertEquals(attachment.sha256, hash) assertEquals(attachment.sha256, hash)
@ -168,8 +183,7 @@ class StandaloneCordaRPClientTest {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `test state machines`() { fun `test state machines`() {
val (stateMachines, updates) = rpcProxy.stateMachinesFeed() val (_, updates) = rpcProxy.stateMachinesFeed()
assertEquals(0, stateMachines.size)
val updateLatch = CountDownLatch(1) val updateLatch = CountDownLatch(1)
val updateCount = AtomicInteger(0) val updateCount = AtomicInteger(0)
@ -190,8 +204,9 @@ class StandaloneCordaRPClientTest {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `test vault track by`() { fun `test vault track by`() {
val (vault, vaultUpdates) = rpcProxy.vaultTrackBy<Cash.State>(paging = PageSpecification(DEFAULT_PAGE_NUM)) val initialGbpBalance = rpcProxy.getCashBalance(GBP)
assertEquals(0, vault.totalStatesAvailable)
val (_, vaultUpdates) = rpcProxy.vaultTrackBy<Cash.State>(paging = PageSpecification(DEFAULT_PAGE_NUM))
val updateLatch = CountDownLatch(1) val updateLatch = CountDownLatch(1)
vaultUpdates.subscribe { update -> vaultUpdates.subscribe { update ->
@ -207,34 +222,35 @@ class StandaloneCordaRPClientTest {
// Check that this cash exists in the vault // Check that this cash exists in the vault
val cashBalance = rpcProxy.getCashBalances() val cashBalance = rpcProxy.getCashBalances()
log.info("Cash Balances: $cashBalance") log.info("Cash Balances: $cashBalance")
assertEquals(1, cashBalance.size) assertEquals(629.POUNDS, cashBalance[GBP]!! - initialGbpBalance)
assertEquals(629.POUNDS, cashBalance[Currency.getInstance("GBP")])
} }
@Test(timeout=300_000) @Test(timeout=300_000)
fun `test vault query by`() { fun `test vault query by`() {
// Now issue some cash
rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNodeIdentity)
.returnValue.getOrThrow(timeout)
val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL) val criteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL)
val paging = PageSpecification(DEFAULT_PAGE_NUM, 10) val paging = PageSpecification(DEFAULT_PAGE_NUM, 10)
val sorting = Sort(setOf(Sort.SortColumn(SortAttribute.Standard(Sort.VaultStateAttribute.RECORDED_TIME), Sort.Direction.DESC))) val sorting = Sort(setOf(Sort.SortColumn(SortAttribute.Standard(Sort.VaultStateAttribute.RECORDED_TIME), Sort.Direction.DESC)))
val initialStateCount = rpcProxy.vaultQueryBy<Cash.State>(criteria, paging, sorting).totalStatesAvailable
val initialGbpBalance = rpcProxy.getCashBalance(GBP)
// Now issue some cash
rpcProxy.startFlow(::CashIssueFlow, 629.POUNDS, OpaqueBytes.of(0), notaryNodeIdentity)
.returnValue.getOrThrow(timeout)
val queryResults = rpcProxy.vaultQueryBy<Cash.State>(criteria, paging, sorting) val queryResults = rpcProxy.vaultQueryBy<Cash.State>(criteria, paging, sorting)
assertEquals(1, queryResults.totalStatesAvailable) assertEquals(1, queryResults.totalStatesAvailable - initialStateCount)
assertEquals(queryResults.states.first().state.data.amount.quantity, 629.POUNDS.quantity) assertEquals(queryResults.states.first().state.data.amount.quantity, 629.POUNDS.quantity)
rpcProxy.startFlow(::CashPaymentFlow, 100.POUNDS, notaryNodeIdentity, true, notaryNodeIdentity).returnValue.getOrThrow() rpcProxy.startFlow(::CashPaymentFlow, 100.POUNDS, notaryNodeIdentity, true, notaryNodeIdentity).returnValue.getOrThrow()
val moreResults = rpcProxy.vaultQueryBy<Cash.State>(criteria, paging, sorting) val moreResults = rpcProxy.vaultQueryBy<Cash.State>(criteria, paging, sorting)
assertEquals(3, moreResults.totalStatesAvailable) // 629 - 100 + 100 assertEquals(3, moreResults.totalStatesAvailable - initialStateCount) // 629 - 100 + 100
// Check that this cash exists in the vault // Check that this cash exists in the vault
val cashBalances = rpcProxy.getCashBalances() val cashBalances = rpcProxy.getCashBalances()
log.info("Cash Balances: $cashBalances") log.info("Cash Balances: $cashBalances")
assertEquals(1, cashBalances.size) assertEquals(629.POUNDS, cashBalances[GBP]!! - initialGbpBalance)
assertEquals(629.POUNDS, cashBalances[Currency.getInstance("GBP")])
} }
@Test(timeout=300_000) @Test(timeout=300_000)

View File

@ -2,6 +2,7 @@ package net.corda.client.rpc
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import kotlin.reflect.KCallable import kotlin.reflect.KCallable
import kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
import kotlin.reflect.jvm.reflect import kotlin.reflect.jvm.reflect
/** /**
@ -10,15 +11,19 @@ import kotlin.reflect.jvm.reflect
* different combinations of parameters. * different combinations of parameters.
*/ */
@OptIn(ExperimentalReflectionOnLambdas::class)
fun <A : Any, R> measure(a: Iterable<A>, f: (A) -> R) = fun <A : Any, R> measure(a: Iterable<A>, f: (A) -> R) =
measure(listOf(a), f.reflect()!!) { f(uncheckedCast(it[0])) } measure(listOf(a), f.reflect()!!) { f(uncheckedCast(it[0])) }
@OptIn(ExperimentalReflectionOnLambdas::class)
fun <A : Any, B : Any, R> measure(a: Iterable<A>, b: Iterable<B>, f: (A, B) -> R) = fun <A : Any, B : Any, R> measure(a: Iterable<A>, b: Iterable<B>, f: (A, B) -> R) =
measure(listOf(a, b), f.reflect()!!) { f(uncheckedCast(it[0]), uncheckedCast(it[1])) } measure(listOf(a, b), f.reflect()!!) { f(uncheckedCast(it[0]), uncheckedCast(it[1])) }
@OptIn(ExperimentalReflectionOnLambdas::class)
fun <A : Any, B : Any, C : Any, R> measure(a: Iterable<A>, b: Iterable<B>, c: Iterable<C>, f: (A, B, C) -> R) = fun <A : Any, B : Any, C : Any, R> measure(a: Iterable<A>, b: Iterable<B>, c: Iterable<C>, f: (A, B, C) -> R) =
measure(listOf(a, b, c), f.reflect()!!) { f(uncheckedCast(it[0]), uncheckedCast(it[1]), uncheckedCast(it[2])) } measure(listOf(a, b, c), f.reflect()!!) { f(uncheckedCast(it[0]), uncheckedCast(it[1]), uncheckedCast(it[2])) }
@OptIn(ExperimentalReflectionOnLambdas::class)
fun <A : Any, B : Any, C : Any, D : Any, R> measure(a: Iterable<A>, b: Iterable<B>, c: Iterable<C>, d: Iterable<D>, f: (A, B, C, D) -> R) = fun <A : Any, B : Any, C : Any, D : Any, R> measure(a: Iterable<A>, b: Iterable<B>, c: Iterable<C>, d: Iterable<D>, f: (A, B, C, D) -> R) =
measure(listOf(a, b, c, d), f.reflect()!!) { f(uncheckedCast(it[0]), uncheckedCast(it[1]), uncheckedCast(it[2]), uncheckedCast(it[3])) } measure(listOf(a, b, c, d), f.reflect()!!) { f(uncheckedCast(it[0]), uncheckedCast(it[1]), uncheckedCast(it[2]), uncheckedCast(it[3])) }

View File

@ -49,14 +49,14 @@ class RPCFailureTests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `kotlin NPE`() = rpc { fun `kotlin NPE`() = rpc {
assertThatThrownBy { it.kotlinNPE() }.isInstanceOf(CordaRuntimeException::class.java) assertThatThrownBy { it.kotlinNPE() }.isInstanceOf(CordaRuntimeException::class.java)
.hasMessageContaining("kotlin.KotlinNullPointerException") .hasMessageContaining("java.lang.NullPointerException")
} }
@Test(timeout=300_000) @Test(timeout=300_000)
fun `kotlin NPE async`() = rpc { fun `kotlin NPE async`() = rpc {
val future = it.kotlinNPEAsync() val future = it.kotlinNPEAsync()
assertThatThrownBy { future.getOrThrow() }.isInstanceOf(CordaRuntimeException::class.java) assertThatThrownBy { future.getOrThrow() }.isInstanceOf(CordaRuntimeException::class.java)
.hasMessageContaining("kotlin.KotlinNullPointerException") .hasMessageContaining("java.lang.NullPointerException")
} }
@Test(timeout=300_000) @Test(timeout=300_000)

View File

@ -1,15 +1,14 @@
apply plugin: 'kotlin' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'corda.common-publishing'
apply plugin: 'net.corda.plugins.publish-utils' description 'Corda common-configuration-parsing module'
apply plugin: 'com.jfrog.artifactory'
dependencies { dependencies {
compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: kotlin_version implementation group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
compile group: "com.typesafe", name: "config", version: typesafe_config_version implementation group: "com.typesafe", name: "config", version: typesafe_config_version
compile project(":common-validation") implementation project(":common-validation")
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "junit:junit:$junit_version" testImplementation "junit:junit:$junit_version"
@ -18,14 +17,20 @@ dependencies {
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
testCompile group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version testImplementation group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
testCompile group: "org.assertj", name: "assertj-core", version: assertj_version testImplementation group: "org.assertj", name: "assertj-core", version: assertj_version
} }
jar { jar {
baseName 'corda-common-configuration-parsing' baseName 'corda-common-configuration-parsing'
} }
publish { publishing {
name jar.baseName publications {
} maven(MavenPublication) {
artifactId jar.baseName
from components.java
}
}
}

View File

@ -5,6 +5,7 @@ import net.corda.common.configuration.parsing.internal.versioned.VersionExtracto
import net.corda.common.validation.internal.Validated import net.corda.common.validation.internal.Validated
import net.corda.common.validation.internal.Validated.Companion.invalid import net.corda.common.validation.internal.Validated.Companion.invalid
import java.time.Duration import java.time.Duration
import java.util.Locale
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -468,7 +469,7 @@ object Configuration {
fun of(message: String, keyName: String? = null, typeName: String = UNKNOWN, containingPath: List<String> = emptyList()): WrongType = contextualize(keyName ?: UNKNOWN, containingPath).let { (key, path) -> WrongType(key, typeName, message, path) } fun of(message: String, keyName: String? = null, typeName: String = UNKNOWN, containingPath: List<String> = emptyList()): WrongType = contextualize(keyName ?: UNKNOWN, containingPath).let { (key, path) -> WrongType(key, typeName, message, path) }
fun forKey(keyName: String, expectedTypeName: String, actualTypeName: String): WrongType = of("$keyName has type ${actualTypeName.toUpperCase()} rather than ${expectedTypeName.toUpperCase()}") fun forKey(keyName: String, expectedTypeName: String, actualTypeName: String): WrongType = of("$keyName has type ${actualTypeName.uppercase(Locale.getDefault())} rather than ${expectedTypeName.uppercase(Locale.getDefault())}")
} }
override fun withContainingPath(vararg containingPath: String) = WrongType(keyName, typeName, message, containingPath.toList()) override fun withContainingPath(vararg containingPath: String) = WrongType(keyName, typeName, message, containingPath.toList())

View File

@ -61,7 +61,7 @@ class SpecificationTest {
override fun parseValid(configuration: Config, options: Configuration.Options): Valid<AtomicLong> { override fun parseValid(configuration: Config, options: Configuration.Options): Valid<AtomicLong> {
val config = configuration.withOptions(options) val config = configuration.withOptions(options)
return valid(AtomicLong(config[maxElement]!!)) return valid(AtomicLong(config[maxElement]))
} }
} }
@ -103,7 +103,7 @@ class SpecificationTest {
if (elements.any { element -> element <= 1 }) { if (elements.any { element -> element <= 1 }) {
return invalid(Configuration.Validation.Error.BadValue.of("elements cannot be less than or equal to 1")) return invalid(Configuration.Validation.Error.BadValue.of("elements cannot be less than or equal to 1"))
} }
return valid(elements.max()!!) return valid(elements.max())
} }
val spec = object : Configuration.Specification<AtomicLong>("AtomicLong") { val spec = object : Configuration.Specification<AtomicLong>("AtomicLong") {

View File

@ -1,28 +1,28 @@
import org.apache.tools.ant.filters.ReplaceTokens import org.apache.tools.ant.filters.ReplaceTokens
apply plugin: 'kotlin' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'corda.common-publishing'
apply plugin: 'com.jfrog.artifactory'
description 'Corda common-logging module'
dependencies { dependencies {
compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: kotlin_version implementation group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
compile group: "com.typesafe", name: "config", version: typesafe_config_version implementation group: "com.typesafe", name: "config", version: typesafe_config_version
// Log4J: logging framework // Log4J: logging framework
compile "org.apache.logging.log4j:log4j-core:$log4j_version" implementation "org.apache.logging.log4j:log4j-core:$log4j_version"
compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version" implementation "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
// Need to depend on one other Corda project in order to get hold of a valid manifest for the tests // Need to depend on one other Corda project in order to get hold of a valid manifest for the tests
testCompile project(":common-validation") testImplementation project(":common-validation")
// test dependencies // test dependencies
testImplementation "junit:junit:$junit_version" testImplementation "junit:junit:$junit_version"
testCompile group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version testImplementation group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
testCompile "org.mockito:mockito-core:$mockito_version" testImplementation "org.mockito:mockito-core:$mockito_version"
testCompile "com.natpryce:hamkrest:$hamkrest_version" testImplementation "com.natpryce:hamkrest:$hamkrest_version"
} }
@ -33,11 +33,19 @@ task generateSource(type: Copy) {
into 'src/main' into 'src/main'
} }
compileKotlin.dependsOn generateSource compileKotlin.dependsOn generateSource
processResources.dependsOn generateSource
sourcesJar.dependsOn generateSource
jar { jar {
baseName 'corda-common-logging' baseName 'corda-common-logging'
} }
publish { publishing {
name jar.baseName publications {
} maven(MavenPublication) {
artifactId jar.baseName
from components.java
}
}
}

View File

@ -9,4 +9,4 @@ package net.corda.common.logging
* (originally added to source control for ease of use) * (originally added to source control for ease of use)
*/ */
internal const val CURRENT_MAJOR_RELEASE = "4.11-SNAPSHOT" internal const val CURRENT_MAJOR_RELEASE = "4.12-SNAPSHOT"

View File

@ -1,6 +1,7 @@
package net.corda.common.logging.errorReporting package net.corda.common.logging.errorReporting
import org.slf4j.Logger import org.slf4j.Logger
import java.util.Locale
/** /**
* Report errors that have occurred. * Report errors that have occurred.
@ -12,7 +13,7 @@ import org.slf4j.Logger
fun Logger.report(error: ErrorCode<*>) = ErrorReporting().getReporter().report(error, this) fun Logger.report(error: ErrorCode<*>) = ErrorReporting().getReporter().report(error, this)
internal fun ErrorCode<*>.formatCode() : String { internal fun ErrorCode<*>.formatCode() : String {
val namespaceString = this.code.namespace.toLowerCase().replace("_", "-") val namespaceString = this.code.namespace.lowercase(Locale.getDefault()).replace("_", "-")
val codeString = this.code.toString().toLowerCase().replace("_", "-") val codeString = this.code.toString().lowercase(Locale.getDefault()).replace("_", "-")
return "$namespaceString-$codeString" return "$namespaceString-$codeString"
} }

View File

@ -1,17 +1,21 @@
apply plugin: 'kotlin' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'corda.common-publishing'
apply plugin: 'net.corda.plugins.publish-utils' description 'Corda common-validation module'
apply plugin: 'com.jfrog.artifactory'
dependencies { dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
} }
jar { jar {
baseName 'corda-common-validation' baseName 'corda-common-validation'
} }
publish { publishing {
name jar.baseName publications {
} maven(MavenPublication) {
artifactId jar.baseName
from components.java
}
}
}

View File

@ -1,18 +1,24 @@
// This contains the SwapIdentitiesFlow which can be used for exchanging confidential identities as part of a flow. // This contains the SwapIdentitiesFlow which can be used for exchanging confidential identities as part of a flow.
// TODO: Merge this into core: the original plan was to develop it independently but in practice it's too widely used to break compatibility now, as finance uses it. // TODO: Merge this into core: the original plan was to develop it independently but in practice it's too widely used to break compatibility now, as finance uses it.
apply plugin: 'kotlin' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.cordapp' apply plugin: 'net.corda.plugins.cordapp'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'corda.common-publishing'
description 'Corda Experimental Confidential Identities' description 'Corda Experimental Confidential Identities'
dependencies { cordapp {
cordaCompile project(':core') targetPlatformVersion corda_platform_version.toInteger()
}
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" dependencies {
cordaProvided project(':core')
api "org.slf4j:slf4j-api:$slf4j_version"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "co.paralleluniverse:quasar-core:$quasar_version"
testImplementation "junit:junit:$junit_version" testImplementation "junit:junit:$junit_version"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}" testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
@ -20,19 +26,28 @@ dependencies {
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
// Guava: Google test library (collections test suite) // Guava: Google test library (collections test suite)
testCompile "com.google.guava:guava-testlib:$guava_version" testImplementation "com.google.guava:guava-testlib:$guava_version"
// Bring in the MockNode infrastructure for writing protocol unit tests. // Bring in the MockNode infrastructure for writing protocol unit tests.
testCompile project(":node-driver") testImplementation project(":node")
testImplementation project(":node-api")
testImplementation project(":node-driver")
testImplementation project(":core-test-utils")
testImplementation project(':finance:contracts')
testImplementation project(':finance:workflows')
// AssertJ: for fluent assertions for testing // AssertJ: for fluent assertions for testing
testCompile "org.assertj:assertj-core:$assertj_version" testImplementation "org.assertj:assertj-core:$assertj_version"
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
testImplementation "io.reactivex:rxjava:$rxjava_version"
} }
jar { publishing {
baseName 'corda-confidential-identities' publications {
} maven(MavenPublication) {
artifactId 'corda-confidential-identities'
publish { from components.cordapp
name jar.baseName artifact javadocJar
}
}
} }

View File

@ -129,4 +129,4 @@ class IdentitySyncFlowTests {
otherSideSession.send(true) otherSideSession.send(true)
} }
} }
} }

View File

@ -2,64 +2,26 @@
<Configuration status="info" shutdownHook="disable"> <Configuration status="info" shutdownHook="disable">
<Properties> <Properties>
<Property name="log-path">${sys:log-path:-logs}</Property> <Property name="log_path">${sys:log-path:-logs}</Property>
<Property name="log-name">node-${hostName}</Property> <Property name="log_name">node-${hostName}</Property>
<Property name="diagnostic-log-name">diagnostic-${hostName}</Property> <Property name="diagnostic_log_name">diagnostic-${hostName}</Property>
<Property name="archive">${log-path}/archive</Property> <Property name="archive">${log_path}/archive</Property>
<Property name="defaultLogLevel">${sys:defaultLogLevel:-info}</Property> <Property name="default_log_level">${sys:defaultLogLevel:-info}</Property>
<Property name="consoleLogLevel">${sys:consoleLogLevel:-error}</Property> <Property name="console_log_level">${sys:consoleLogLevel:-error}</Property>
</Properties> </Properties>
<Appenders> <Appenders>
<ScriptAppenderSelector name="Console-Selector"> <!-- The default console appender - prints no exception information -->
<Script language="nashorn"><![CDATA[ <Console name="Console-Appender" target="SYSTEM_OUT">
var System = Java.type('java.lang.System'); <PatternLayout
var level = System.getProperty("consoleLogLevel"); pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{0}}{INFO=white,WARN=red,FATAL=bright red}"/>
var enabled = System.getProperty("consoleLoggingEnabled"); </Console>
enabled == "true" && (level == "debug" || level == "trace") ? "Console-Debug-Appender" : "Console-Appender";
]]></Script>
<AppenderSet>
<!-- The default console appender - prints no exception information --> <!-- The console appender when debug or trace level logging is specified. Prints full stack trace -->
<Console name="Console-Appender" target="SYSTEM_OUT"> <Console name="Console-Debug-Appender" target="SYSTEM_OUT">
<PatternLayout> <PatternLayout
<ScriptPatternSelector pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{}}{INFO=white,WARN=red,FATAL=bright red}"/>
defaultPattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{0}}{INFO=white,WARN=red,FATAL=bright red}"> </Console>
<Script name="MDCSelector" language="javascript"><![CDATA[
result = null;
if (!logEvent.getContextData().size() == 0) {
result = "WithMDC";
} else {
result = null;
}
result;
]]>
</Script>
<PatternMatch key="WithMDC" pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg %X%n%throwable{0}}{INFO=white,WARN=red,FATAL=bright red}"/>
</ScriptPatternSelector>
</PatternLayout>
</Console>
<!-- The console appender when debug or trace level logging is specified. Prints full stack trace -->
<Console name="Console-Debug-Appender" target="SYSTEM_OUT">
<PatternLayout>
<ScriptPatternSelector defaultPattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{}}{INFO=white,WARN=red,FATAL=bright red}">
<Script name="MDCSelector" language="javascript"><![CDATA[
result = null;
if (!logEvent.getContextData().size() == 0) {
result = "WithMDC";
} else {
result = null;
}
result;
]]>
</Script>
<PatternMatch key="WithMDC" pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg %X%n%throwable{}}{INFO=white,WARN=red,FATAL=bright red}"/>
</ScriptPatternSelector>
</PatternLayout>
</Console>
</AppenderSet>
</ScriptAppenderSelector>
<!-- Required for printBasicInfo --> <!-- Required for printBasicInfo -->
<Console name="Console-Appender-Println" target="SYSTEM_OUT"> <Console name="Console-Appender-Println" target="SYSTEM_OUT">
@ -69,24 +31,10 @@
<!-- Will generate up to 500 log files for a given day. Adjust this number according to the available storage. <!-- Will generate up to 500 log files for a given day. Adjust this number according to the available storage.
During every rollover it will delete those that are older than 60 days, but keep the most recent 10 GB --> During every rollover it will delete those that are older than 60 days, but keep the most recent 10 GB -->
<RollingRandomAccessFile name="RollingFile-Appender" <RollingRandomAccessFile name="RollingFile-Appender"
fileName="${log-path}/${log-name}.log" fileName="${log_path}/${log_name}.log"
filePattern="${archive}/${log-name}.%date{yyyy-MM-dd}-%i.log.gz"> filePattern="${archive}/${log_name}.%date{yyyy-MM-dd}-%i.log.gz">
<PatternLayout> <PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
<ScriptPatternSelector defaultPattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n">
<Script name="MDCSelector" language="javascript"><![CDATA[
result = null;
if (!logEvent.getContextData().size() == 0) {
result = "WithMDC";
} else {
result = null;
}
result;
]]>
</Script>
<PatternMatch key="WithMDC" pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg %X%n"/>
</ScriptPatternSelector>
</PatternLayout>
<Policies> <Policies>
<TimeBasedTriggeringPolicy/> <TimeBasedTriggeringPolicy/>
@ -95,7 +43,7 @@
<DefaultRolloverStrategy min="1" max="500"> <DefaultRolloverStrategy min="1" max="500">
<Delete basePath="${archive}" maxDepth="1"> <Delete basePath="${archive}" maxDepth="1">
<IfFileName glob="${log-name}*.log.gz"/> <IfFileName glob="${log_name}*.log.gz"/>
<IfLastModified age="60d"> <IfLastModified age="60d">
<IfAny> <IfAny>
<IfAccumulatedFileSize exceeds="10 GB"/> <IfAccumulatedFileSize exceeds="10 GB"/>
@ -109,24 +57,10 @@
<!-- Will generate up to 100 log files for a given day. During every rollover it will delete <!-- Will generate up to 100 log files for a given day. During every rollover it will delete
those that are older than 60 days, but keep the most recent 10 GB --> those that are older than 60 days, but keep the most recent 10 GB -->
<RollingRandomAccessFile name="Diagnostic-RollingFile-Appender" <RollingRandomAccessFile name="Diagnostic-RollingFile-Appender"
fileName="${log-path}/${diagnostic-log-name}.log" fileName="${log_path}/${diagnostic_log_name}.log"
filePattern="${archive}/${diagnostic-log-name}.%date{yyyy-MM-dd}-%i.log.gz"> filePattern="${archive}/${diagnostic_log_name}.%date{yyyy-MM-dd}-%i.log.gz">
<PatternLayout> <PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
<ScriptPatternSelector defaultPattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n">
<Script name="MDCSelector" language="javascript"><![CDATA[
result = null;
if (!logEvent.getContextData().size() == 0) {
result = "WithMDC";
} else {
result = null;
}
result;
]]>
</Script>
<PatternMatch key="WithMDC" pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg %X%n"/>
</ScriptPatternSelector>
</PatternLayout>
<Policies> <Policies>
<TimeBasedTriggeringPolicy/> <TimeBasedTriggeringPolicy/>
@ -135,7 +69,7 @@
<DefaultRolloverStrategy min="1" max="100"> <DefaultRolloverStrategy min="1" max="100">
<Delete basePath="${archive}" maxDepth="1"> <Delete basePath="${archive}" maxDepth="1">
<IfFileName glob="${diagnostic-log-name}*.log.gz"/> <IfFileName glob="${diagnostic_log_name}*.log.gz"/>
<IfLastModified age="60d"> <IfLastModified age="60d">
<IfAny> <IfAny>
<IfAccumulatedFileSize exceeds="10 GB"/> <IfAccumulatedFileSize exceeds="10 GB"/>
@ -147,7 +81,7 @@
</RollingRandomAccessFile> </RollingRandomAccessFile>
<RollingFile name="Checkpoint-Agent-RollingFile-Appender" <RollingFile name="Checkpoint-Agent-RollingFile-Appender"
fileName="${log-path}/checkpoints_agent-${date:yyyyMMdd-HHmmss}.log" fileName="${log_path}/checkpoints_agent-${date:yyyyMMdd-HHmmss}.log"
filePattern="${archive}/checkpoints_agent.%date{yyyy-MM-dd}-%i.log.gz"> filePattern="${archive}/checkpoints_agent.%date{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/> <PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n"/>
@ -171,7 +105,7 @@
</RollingFile> </RollingFile>
<Rewrite name="Console-ErrorCode-Selector"> <Rewrite name="Console-ErrorCode-Selector">
<AppenderRef ref="Console-Selector"/> <AppenderRef ref="Console-Appender"/>
</Rewrite> </Rewrite>
<Rewrite name="Console-ErrorCode-Appender-Println"> <Rewrite name="Console-ErrorCode-Appender-Println">
@ -187,8 +121,8 @@
</Appenders> </Appenders>
<Loggers> <Loggers>
<Root level="${defaultLogLevel}"> <Root level="${default_log_level}">
<AppenderRef ref="Console-ErrorCode-Selector" level="${consoleLogLevel}"/> <AppenderRef ref="Console-ErrorCode-Selector" level="${console_log_level}"/>
<AppenderRef ref="RollingFile-ErrorCode-Appender"/> <AppenderRef ref="RollingFile-ErrorCode-Appender"/>
</Root> </Root>
<Logger name="BasicInfo" additivity="false"> <Logger name="BasicInfo" additivity="false">

View File

@ -3,35 +3,32 @@
# their own projects. So don't get fancy with syntax! # their own projects. So don't get fancy with syntax!
# Fancy syntax - multi pass ${whatever} replacement # Fancy syntax - multi pass ${whatever} replacement
cordaVersion=4.11 cordaVersion=4.12
versionSuffix=SNAPSHOT versionSuffix=SNAPSHOT
gradlePluginsVersion=5.0.12 cordaShellVersion=4.12-SNAPSHOT
kotlinVersion=1.2.71 gradlePluginsVersion=5.1.1
java8MinUpdateVersion=171 artifactoryContextUrl=https://software.r3.com/artifactory
internalPublishVersion=1.+
# ***************************************************************# # ***************************************************************#
# When incrementing platformVersion make sure to update # # When incrementing platformVersion make sure to update #
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. # # net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
# ***************************************************************# # ***************************************************************#
platformVersion=13 platformVersion=140
openTelemetryVersion=1.20.1 openTelemetryVersion=1.20.1
openTelemetrySemConvVersion=1.20.1-alpha openTelemetrySemConvVersion=1.20.1-alpha
guavaVersion=28.0-jre guavaVersion=33.1.0-jre
# Quasar version to use with Java 8: quasarVersion=0.9.0_r3
quasarVersion=0.7.16_r3
# Quasar version to use with Java 11:
quasarVersion11=0.8.1_r3
jdkClassifier11=jdk11
dockerJavaVersion=3.2.5 dockerJavaVersion=3.2.5
proguardVersion=6.1.1 proguardVersion=7.3.1
// bouncy castle version must not be changed on a patch release. Needs a full release test cycle to flush out any issues. # Switch to release version when out
bouncycastleVersion=1.78.1 bouncycastleVersion=2.73.6
classgraphVersion=4.8.135 classgraphVersion=4.8.135
disruptorVersion=3.4.2 disruptorVersion=3.4.2
typesafeConfigVersion=1.3.4 typesafeConfigVersion=1.3.4
jsr305Version=3.0.2 jsr305Version=3.0.2
artifactoryPluginVersion=4.16.1 artifactoryPluginVersion=4.16.1
snakeYamlVersion=1.33 snakeYamlVersion=2.2
caffeineVersion=2.9.3 caffeineVersion=3.1.8
metricsVersion=4.1.0 metricsVersion=4.1.0
metricsNewRelicVersion=1.1.1 metricsNewRelicVersion=1.1.1
openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4 openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4
@ -46,22 +43,22 @@ commonsTextVersion=1.10.0
# gradle-capsule-plugin:1.0.2 contains capsule:1.0.1 by default. # gradle-capsule-plugin:1.0.2 contains capsule:1.0.1 by default.
# We must configure it manually to use the latest capsule version. # We must configure it manually to use the latest capsule version.
capsuleVersion=1.0.3 capsuleVersion=1.0.4_r3
asmVersion=7.1 asmVersion=9.5
artemisVersion=2.19.1 artemisVersion=2.36.0
# TODO Upgrade Jackson only when corda is using kotlin 1.3.10 # TODO Upgrade Jackson only when corda is using kotlin 1.3.10
jacksonVersion=2.17.2 jacksonVersion=2.17.2
jacksonKotlinVersion=2.9.7 jacksonKotlinVersion=2.17.0
jettyVersion=9.4.53.v20231009 jettyVersion=12.0.7
jerseyVersion=2.25 jerseyVersion=3.1.6
servletVersion=4.0.1 servletVersion=4.0.1
assertjVersion=3.12.2 assertjVersion=3.12.2
slf4JVersion=1.7.30 slf4JVersion=2.0.12
log4JVersion=2.17.1 log4JVersion=2.23.1
okhttpVersion=3.14.9 okhttpVersion=4.12.0
nettyVersion=4.1.77.Final nettyVersion=4.1.109.Final
fileuploadVersion=1.4 fileuploadVersion=2.0.0-M1
kryoVersion=4.0.2 kryoVersion=5.5.0
kryoSerializerVersion=0.43 kryoSerializerVersion=0.43
# Legacy JUnit 4 version # Legacy JUnit 4 version
junitVersion=4.12 junitVersion=4.12
@ -71,8 +68,8 @@ junitVersion=4.12
junitVintageVersion=5.5.0-RC1 junitVintageVersion=5.5.0-RC1
junitJupiterVersion=5.5.0-RC1 junitJupiterVersion=5.5.0-RC1
junitPlatformVersion=1.5.0-RC1 junitPlatformVersion=1.5.0-RC1
mockitoVersion=2.28.2 mockitoVersion=5.5.0
mockitoKotlinVersion=1.6.0 mockitoKotlinVersion=5.2.1
hamkrestVersion=1.7.0.0 hamkrestVersion=1.7.0.0
joptSimpleVersion=5.0.2 joptSimpleVersion=5.0.2
jansiVersion=1.18 jansiVersion=1.18
@ -80,13 +77,12 @@ hibernateVersion=5.6.14.Final
# h2Version - Update docs if renamed or removed. # h2Version - Update docs if renamed or removed.
h2Version=2.2.224 h2Version=2.2.224
rxjavaVersion=1.3.8 rxjavaVersion=1.3.8
dokkaVersion=0.10.1 dokkaVersion=1.8.20
eddsaVersion=0.3.0
dependencyCheckerVersion=5.2.0 dependencyCheckerVersion=5.2.0
commonsCollectionsVersion=4.3 commonsCollectionsVersion=4.3
beanutilsVersion=1.9.4 beanutilsVersion=1.9.4
shiroVersion=1.10.0 shiroVersion=1.10.0
hikariVersion=3.3.1 hikariVersion=5.1.0
liquibaseVersion=4.20.0 liquibaseVersion=4.20.0
dockerComposeRuleVersion=1.5.0 dockerComposeRuleVersion=1.5.0
seleniumVersion=3.141.59 seleniumVersion=3.141.59
@ -97,13 +93,9 @@ protonjVersion=0.33.0
snappyVersion=0.5 snappyVersion=0.5
jcabiManifestsVersion=1.1 jcabiManifestsVersion=1.1
picocliVersion=3.9.6 picocliVersion=3.9.6
commonsLangVersion=3.9 commonsIoVersion=2.7
commonsIoVersion=2.6
controlsfxVersion=8.40.15 controlsfxVersion=8.40.15
# FontAwesomeFX for Java8
fontawesomefxCommonsJava8Version=8.15
fontawesomefxFontawesomeJava8Version=4.7.0-5
# FontAwesomeFX for a more recent version of the Java Runtime (class file version 55.0)
fontawesomefxCommonsVersion=11.0 fontawesomefxCommonsVersion=11.0
fontawesomefxFontawesomeVersion=4.7.0-11 fontawesomefxFontawesomeVersion=4.7.0-11
javaassistVersion=3.27.0-GA javaassistVersion=3.29.2-GA
joorVersion=0.9.15

4
core-1.2/README.md Normal file
View File

@ -0,0 +1,4 @@
This is a Kotlin 1.2 version of the `core` module, which is consumed by the `verifier` module, for verifying contracts written in Kotlin
1.2. This is just a "shell" module which uses the existing the code in `core` and compiles it with the 1.2 compiler.
To allow `core` to benefit from new APIs introduced since 1.2, those APIs much be copied into this module with the same `kotlin` package.

34
core-1.2/build.gradle Normal file
View File

@ -0,0 +1,34 @@
apply plugin: "corda.kotlin-1.2"
apply plugin: "corda.common-publishing"
description 'Corda core built with Kotlin 1.2'
sourceSets {
main {
java.srcDir("../core/src/main/java")
kotlin.srcDir("../core/src/main/kotlin")
}
}
dependencies {
// Use the same dependencies as core (minus Kotlin)
implementation(project(path: ":core", configuration: "resolvableImplementation")) {
exclude(group: "org.jetbrains.kotlin")
}
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_1_2_version"
implementation "co.paralleluniverse:quasar-core:$quasar_version"
}
jar {
archiveBaseName = 'corda-core-1.2'
}
// TODO Don't publish publicly as it's only needed by the `verifier` module which consumes this into a fat jar.
publishing {
publications {
maven(MavenPublication) {
artifactId 'corda-core-1.2'
from components.java
}
}
}

View File

@ -0,0 +1,5 @@
// Implement the new post-1.2 APIs which are used by core and serialization
@file:Suppress("unused")
package kotlin
inline val Char.code: Int get() = this.toInt()

View File

@ -0,0 +1,37 @@
// Implement the new post-1.2 APIs which are used by core and serialization
@file:Suppress("unused", "MagicNumber", "INVISIBLE_MEMBER")
package kotlin.collections
inline fun <K, V> Iterable<K>.associateWith(valueSelector: (K) -> V): Map<K, V> {
val result = LinkedHashMap<K, V>(mapCapacity(if (this is Collection<*>) size else 10).coerceAtLeast(16))
return associateWithTo(result, valueSelector)
}
inline fun <K, V, M : MutableMap<in K, in V>> Iterable<K>.associateWithTo(destination: M, valueSelector: (K) -> V): M {
for (element in this) {
destination.put(element, valueSelector(element))
}
return destination
}
inline fun <T> Iterable<T>.sumOf(selector: (T) -> Int): Int {
var sum = 0
for (element in this) {
sum += selector(element)
}
return sum
}
inline fun <T, R : Comparable<R>> Iterable<T>.maxOf(selector: (T) -> R): R {
val iterator = iterator()
if (!iterator.hasNext()) throw NoSuchElementException()
var maxValue = selector(iterator.next())
while (iterator.hasNext()) {
val v = selector(iterator.next())
if (maxValue < v) {
maxValue = v
}
}
return maxValue
}

View File

@ -0,0 +1,37 @@
// Implement the new post-1.2 APIs which are used by core and serialization
@file:Suppress("unused", "SpreadOperator", "NOTHING_TO_INLINE")
package kotlin.io.path
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Files
import java.nio.file.LinkOption
import java.nio.file.OpenOption
import java.nio.file.Path
import java.nio.file.attribute.FileAttribute
inline operator fun Path.div(other: String): Path = this.resolve(other)
fun Path.listDirectoryEntries(glob: String = "*"): List<Path> = Files.newDirectoryStream(this, glob).use { it.toList() }
inline fun Path.createDirectories(vararg attributes: FileAttribute<*>): Path = Files.createDirectories(this, *attributes)
inline fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this)
inline fun Path.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options)
inline fun Path.inputStream(vararg options: OpenOption): InputStream = Files.newInputStream(this, *options)
inline fun Path.outputStream(vararg options: OpenOption): OutputStream = Files.newOutputStream(this, *options)
inline fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(this, *options)
inline fun Path.isSymbolicLink(): Boolean = Files.isSymbolicLink(this)
inline fun Path.readSymbolicLink(): Path = Files.readSymbolicLink(this)
val Path.name: String
get() = fileName?.toString().orEmpty()
inline fun Path.readBytes(): ByteArray = Files.readAllBytes(this)

View File

@ -0,0 +1,24 @@
// Implement the new post-1.2 APIs which are used by core and serialization
@file:Suppress("NOTHING_TO_INLINE", "unused")
package kotlin.text
import java.util.Locale
inline fun Char.isLowerCase(): Boolean = Character.isLowerCase(this)
public fun Char.lowercase(locale: Locale): String = toString().lowercase(locale)
inline fun Char.lowercaseChar(): Char = Character.toLowerCase(this)
inline fun Char.uppercase(): String = toString().uppercase()
fun Char.uppercase(locale: Locale): String = toString().uppercase(locale)
inline fun Char.titlecaseChar(): Char = Character.toTitleCase(this)
fun Char.titlecase(locale: Locale): String {
val localizedUppercase = uppercase(locale)
if (localizedUppercase.length > 1) {
return if (this == '\u0149') localizedUppercase else localizedUppercase[0] + localizedUppercase.substring(1).lowercase()
}
if (localizedUppercase != uppercase()) {
return localizedUppercase
}
return titlecaseChar().toString()
}

View File

@ -0,0 +1,12 @@
// Implement the new post-1.2 APIs which are used by core and serialization
@file:Suppress("NOTHING_TO_INLINE", "unused")
package kotlin.text
// StringBuilder
fun StringBuilder.append(vararg value: String?): StringBuilder {
for (item in value)
append(item)
return this
}
inline fun StringBuilder.appendLine(): StringBuilder = append('\n')
inline fun StringBuilder.appendLine(value: String?): StringBuilder = append(value).appendLine()

View File

@ -0,0 +1,8 @@
// Implement the new post-1.2 APIs which are used by core and serialization
@file:Suppress("unused")
package kotlin.text
inline fun String.replaceFirstChar(transform: (Char) -> CharSequence): String {
return if (isNotEmpty()) transform(this[0]).toString() + substring(1) else this
}

View File

@ -0,0 +1,12 @@
// Implement the new post-1.2 APIs which are used by core and serialization
@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE", "unused")
package kotlin.text
import java.util.Locale
// String extensions
inline fun String.lowercase(): String = (this as java.lang.String).toLowerCase(Locale.ROOT)
inline fun String.lowercase(locale: Locale): String = (this as java.lang.String).toLowerCase(locale)
inline fun String.uppercase(): String = (this as java.lang.String).toUpperCase(Locale.ROOT)
inline fun String.uppercase(locale: Locale): String = (this as java.lang.String).toUpperCase(locale)

View File

@ -1,18 +1,21 @@
apply plugin: 'kotlin' apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'kotlin-jpa' apply plugin: 'org.jetbrains.kotlin.plugin.jpa'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'idea'
description 'Corda core tests' description 'Corda core tests'
configurations { configurations {
integrationTestCompile.extendsFrom testCompile integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
smokeTestCompile.extendsFrom compile smokeTestImplementation.extendsFrom implementation
smokeTestRuntimeOnly.extendsFrom runtimeOnly smokeTestRuntimeOnly.extendsFrom runtimeOnly
}
evaluationDependsOn(':node:capsule') testArtifacts.extendsFrom testRuntimeOnlyClasspath
corda4_11
}
sourceSets { sourceSets {
integrationTest { integrationTest {
@ -41,63 +44,98 @@ sourceSets {
processSmokeTestResources { processSmokeTestResources {
// Bring in the fully built corda.jar for use by NodeFactory in the smoke tests // Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
from(project(':node:capsule').tasks['buildCordaJAR']) { from(tasks.getByPath(":node:capsule:buildCordaJAR")) {
rename 'corda-(.*)', 'corda.jar' rename 'corda-(.*)', 'corda.jar'
} }
from(tasks.getByPath(":finance:workflows:jar")) {
rename 'corda-finance-workflows-.*.jar', 'corda-finance-workflows.jar'
}
from(tasks.getByPath(":finance:contracts:jar")) {
rename 'corda-finance-contracts-.*.jar', 'corda-finance-contracts.jar'
}
from(tasks.getByPath(":testing:cordapps:4.11-workflows:jar"))
from(configurations.corda4_11)
}
processIntegrationTestResources {
from(tasks.getByPath(":finance:contracts:jar")) {
rename 'corda-finance-contracts-.*.jar', 'corda-finance-contracts.jar'
}
from(configurations.corda4_11)
}
processTestResources {
from(configurations.corda4_11)
} }
dependencies { dependencies {
testImplementation project(":core")
testImplementation project(":serialization")
testImplementation project(":finance:contracts")
testImplementation project(":finance:workflows")
testImplementation project(":node")
testImplementation project(":node-api")
testImplementation project(":client:rpc")
testImplementation project(":common-configuration-parsing")
testImplementation project(":core-test-utils")
testImplementation project(":test-utils")
testImplementation project(":node-driver")
// used by FinalityFlowTests
testImplementation project(':testing:cordapps:cashobservers')
testImplementation(project(path: ':core', configuration: 'testArtifacts')) {
transitive = false
}
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "junit:junit:$junit_version" testImplementation "junit:junit:$junit_version"
testImplementation "org.apache.commons:commons-fileupload2-jakarta:$fileupload_version"
// Guava: Google test library (collections test suite)
testImplementation "com.google.guava:guava-testlib:$guava_version"
testImplementation "com.google.jimfs:jimfs:1.1"
testImplementation "com.typesafe:config:$typesafe_config_version"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
// Hamkrest, for fluent, composable matchers
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
testImplementation 'org.hamcrest:hamcrest-library:2.1'
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
testImplementation "org.mockito:mockito-core:$mockito_version"
// AssertJ: for fluent assertions for testing
testImplementation "org.assertj:assertj-core:${assertj_version}"
// Guava: Google utilities library.
testImplementation "com.google.guava:guava:$guava_version"
testImplementation "com.esotericsoftware:kryo:$kryo_version"
testImplementation "co.paralleluniverse:quasar-core:$quasar_version"
testImplementation "org.hibernate:hibernate-core:$hibernate_version"
testImplementation "org.bouncycastle:bcprov-lts8on:${bouncycastle_version}"
testImplementation "io.netty:netty-common:$netty_version"
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}" testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
testCompile "commons-fileupload:commons-fileupload:$fileupload_version"
testCompile project(":core")
testCompile project(path: ':core', configuration: 'testArtifacts')
// Guava: Google test library (collections test suite)
testCompile "com.google.guava:guava-testlib:$guava_version"
// Bring in the MockNode infrastructure for writing protocol unit tests.
testCompile project(":node-driver")
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
// Hamkrest, for fluent, composable matchers
testCompile "com.natpryce:hamkrest:$hamkrest_version"
// SLF4J: commons-logging bindings for a SLF4J back end
compile "org.slf4j:jcl-over-slf4j:$slf4j_version"
compile "org.slf4j:slf4j-api:$slf4j_version"
// AssertJ: for fluent assertions for testing
testCompile "org.assertj:assertj-core:${assertj_version}"
// Guava: Google utilities library.
testCompile "com.google.guava:guava:$guava_version"
// Smoke tests do NOT have any Node code on the classpath! // Smoke tests do NOT have any Node code on the classpath!
smokeTestImplementation project(":core")
smokeTestImplementation project(":node-api")
smokeTestImplementation project(":client:rpc")
smokeTestImplementation project(':smoke-test-utils')
smokeTestImplementation project(':core-test-utils')
smokeTestImplementation project(":finance:workflows")
smokeTestImplementation project(":testing:cordapps:4.11-workflows")
smokeTestImplementation project(":finance:contracts")
smokeTestImplementation "org.assertj:assertj-core:${assertj_version}"
smokeTestImplementation "org.bouncycastle:bcprov-lts8on:${bouncycastle_version}"
smokeTestImplementation "co.paralleluniverse:quasar-core:$quasar_version"
smokeTestImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" smokeTestImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
smokeTestImplementation "junit:junit:$junit_version" smokeTestImplementation "junit:junit:$junit_version"
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}" smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
smokeTestRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}" smokeTestRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
smokeTestRuntimeOnly "org.slf4j:slf4j-simple:$slf4j_version"
smokeTestCompile project(':smoke-test-utils') corda4_11 "net.corda:corda-finance-contracts:4.11"
smokeTestCompile "org.assertj:assertj-core:${assertj_version}" corda4_11 "net.corda:corda-finance-workflows:4.11"
corda4_11 "net.corda:corda:4.11"
// used by FinalityFlowTests
testCompile project(':testing:cordapps:cashobservers')
}
configurations {
testArtifacts.extendsFrom testRuntimeClasspath
} }
tasks.withType(Test).configureEach { tasks.withType(Test).configureEach {
@ -105,27 +143,36 @@ tasks.withType(Test).configureEach {
forkEvery = 10 forkEvery = 10
} }
task testJar(type: Jar) { tasks.register('testJar', Jar) {
classifier "tests" classifier "tests"
from sourceSets.test.output from sourceSets.test.output
} }
task integrationTest(type: Test) { tasks.register('integrationTest', Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath classpath = sourceSets.integrationTest.runtimeClasspath
} }
task smokeTestJar(type: Jar) { tasks.register('smokeTestJar', Jar) {
classifier 'smokeTests' classifier 'smokeTests'
from sourceSets.smokeTest.output from(sourceSets.smokeTest.output) {
exclude("*.jar")
}
} }
task smokeTest(type: Test) { tasks.register('smokeTest', Test) {
dependsOn smokeTestJar dependsOn smokeTestJar
testClassesDirs = sourceSets.smokeTest.output.classesDirs testClassesDirs = sourceSets.smokeTest.output.classesDirs
classpath = sourceSets.smokeTest.runtimeClasspath classpath = sourceSets.smokeTest.runtimeClasspath
} }
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}
// quasar exclusions upon agent code instrumentation at run-time // quasar exclusions upon agent code instrumentation at run-time
quasar { quasar {
excludePackages.addAll( excludePackages.addAll(
@ -140,7 +187,6 @@ quasar {
"io.github.classgraph**", "io.github.classgraph**",
"io.netty*", "io.netty*",
"liquibase**", "liquibase**",
"net.i2p.crypto.**",
"nonapi.io.github.classgraph.**", "nonapi.io.github.classgraph.**",
"org.apiguardian.**", "org.apiguardian.**",
"org.bouncycastle**", "org.bouncycastle**",

View File

@ -0,0 +1,198 @@
package net.corda.coretests.transactions
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.TransactionState
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC
import net.corda.core.internal.hash
import net.corda.core.internal.mapToSet
import net.corda.core.internal.toPath
import net.corda.core.messaging.startFlow
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.coretesting.internal.delete
import net.corda.coretesting.internal.modifyJarManifest
import net.corda.coretesting.internal.useZipFile
import net.corda.finance.DOLLARS
import net.corda.finance.contracts.CommercialPaper
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.finance.issuedBy
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.internal.JarSignatureTestUtils.unsignJar
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.NodeParameters
import net.corda.testing.node.TestCordapp
import net.corda.testing.node.internal.DriverDSLImpl
import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
import net.corda.testing.node.internal.TestCordappInternal
import net.corda.testing.node.internal.UriTestCordapp
import net.corda.testing.node.internal.enclosedCordapp
import net.corda.testing.node.internal.internalDriver
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy
import org.junit.Test
import java.nio.file.Files
import java.nio.file.Path
import java.time.Duration
import java.time.Instant
import kotlin.io.path.absolutePathString
import kotlin.io.path.copyTo
import kotlin.io.path.createDirectories
import kotlin.io.path.inputStream
import kotlin.io.path.isRegularFile
import kotlin.io.path.moveTo
class TransactionBuilderDriverTest {
companion object {
val currentFinanceContractsJar = this::class.java.getResource("/corda-finance-contracts.jar")!!.toPath()
val legacyFinanceContractsJar = this::class.java.getResource("/corda-finance-contracts-4.11.jar")!!.toPath()
}
@Test(timeout=300_000)
fun `adds CorDapp dependencies`() {
internalDriver(cordappsForAllNodes = listOf(FINANCE_WORKFLOWS_CORDAPP), startNodesInProcess = false) {
val (cordapp, dependency) = splitFinanceContractCordapp(currentFinanceContractsJar)
cordapp.jarFile.inputStream().use(defaultNotaryNode.getOrThrow().rpc::uploadAttachment)
dependency.jarFile.inputStream().use(defaultNotaryNode.getOrThrow().rpc::uploadAttachment)
// Start the node with the CorDapp but without the dependency
val node = startNode(NodeParameters(ALICE_NAME, additionalCordapps = listOf(cordapp))).getOrThrow()
// First make sure the missing dependency causes an issue
assertThatThrownBy {
createTransaction(node)
}.hasMessageContaining("Transaction being built has a missing attachment for class net/corda/finance/contracts/asset/")
// Upload the missing dependency
dependency.jarFile.inputStream().use(node.rpc::uploadAttachment)
val stx = createTransaction(node)
assertThat(stx.tx.attachments).contains(cordapp.jarFile.hash, dependency.jarFile.hash)
}
}
@Test(timeout=300_000)
fun `adds legacy contracts CorDapp dependencies`() {
internalDriver(
cordappsForAllNodes = listOf(FINANCE_WORKFLOWS_CORDAPP),
startNodesInProcess = false,
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
) {
val (legacyContracts, legacyDependency) = splitFinanceContractCordapp(legacyFinanceContractsJar)
val currentContracts = TestCordapp.of(currentFinanceContractsJar.toUri()).asSigned() as TestCordappInternal
currentContracts.jarFile.inputStream().use(defaultNotaryNode.getOrThrow().rpc::uploadAttachment)
// Start the node with the legacy CorDapp but without the dependency
val node = startNode(NodeParameters(
ALICE_NAME,
additionalCordapps = listOf(currentContracts),
legacyContracts = listOf(legacyContracts)
)).getOrThrow()
// First make sure the missing dependency causes an issue
assertThatThrownBy {
createTransaction(node)
}.hasMessageContaining("Transaction being built has a missing legacy attachment for class net/corda/finance/contracts/asset/")
// Upload the missing dependency
legacyDependency.jarFile.inputStream().use(node.rpc::uploadAttachment)
val stx = createTransaction(node)
assertThat(stx.tx.legacyAttachments).contains(legacyContracts.jarFile.hash, legacyDependency.jarFile.hash)
}
}
@Test(timeout=300_000)
fun `prevents transaction which is multi-contract but not backwards compatible because one of the contracts has missing legacy attachment`() {
internalDriver(
cordappsForAllNodes = listOf(FINANCE_WORKFLOWS_CORDAPP, enclosedCordapp()),
startNodesInProcess = false,
networkParameters = testNetworkParameters(minimumPlatformVersion = 4),
isDebug = true
) {
val (currentCashContract, currentCpContract) = splitJar(currentFinanceContractsJar) { "CommercialPaper" in it.absolutePathString() }
val (legacyCashContract, _) = splitJar(legacyFinanceContractsJar) { "CommercialPaper" in it.absolutePathString() }
currentCashContract.jarFile.inputStream().use(defaultNotaryNode.getOrThrow().rpc::uploadAttachment)
currentCpContract.jarFile.inputStream().use(defaultNotaryNode.getOrThrow().rpc::uploadAttachment)
// The node has the legacy CommericalPaper contract missing
val node = startNode(NodeParameters(
ALICE_NAME,
additionalCordapps = listOf(currentCashContract, currentCpContract),
legacyContracts = listOf(legacyCashContract)
)).getOrThrow()
assertThatThrownBy { node.rpc.startFlow(::TwoContractTransactionFlow).returnValue.getOrThrow() }
.hasMessageContaining("Transaction being built has a missing legacy attachment")
.hasMessageContaining("CommercialPaper")
}
}
/**
* Split the given finance contracts jar into two such that the second jar becomes a dependency to the first.
*/
private fun DriverDSLImpl.splitFinanceContractCordapp(contractsJar: Path): Pair<UriTestCordapp, UriTestCordapp> {
return splitJar(contractsJar) { it.absolutePathString() == "/net/corda/finance/contracts/asset/CashUtilities.class" }
}
private fun DriverDSLImpl.splitJar(path: Path, move: (Path) -> Boolean): Pair<UriTestCordapp, UriTestCordapp> {
val jar1 = Files.createTempFile(driverDirectory, "jar1-", ".jar")
val jar2 = Files.createTempFile(driverDirectory, "jar2-", ".jar")
path.copyTo(jar1, overwrite = true)
jar1.useZipFile { zipFs1 ->
jar2.useZipFile { zipFs2 ->
Files.walk(zipFs1.getPath("/")).filter { it.isRegularFile() && move(it) }.forEach { file ->
val target = zipFs2.getPath(file.absolutePathString())
target.parent?.createDirectories()
file.moveTo(target)
}
}
}
jar1.modifyJarManifest { manifest ->
manifest.mainAttributes.delete("Sealed")
}
jar1.unsignJar()
return Pair(
TestCordapp.of(jar1.toUri()).asSigned() as UriTestCordapp,
TestCordapp.of(jar2.toUri()).asSigned() as UriTestCordapp
)
}
private fun DriverDSLImpl.createTransaction(node: NodeHandle): SignedTransaction {
return node.rpc.startFlow(
::CashIssueAndPaymentFlow,
1.DOLLARS,
OpaqueBytes.of(0x00),
defaultNotaryIdentity,
false,
defaultNotaryIdentity
).returnValue.getOrThrow().stx
}
@StartableByRPC
class TwoContractTransactionFlow : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val builder = TransactionBuilder(notary)
val issuer = ourIdentity.ref(OpaqueBytes.of(0x00))
val amount = 1.DOLLARS.issuedBy(issuer)
val signers = Cash().generateIssue(builder, amount, ourIdentity, notary)
builder.addOutputState(TransactionState(CommercialPaper.State(issuer, ourIdentity, amount, Instant.MAX), notary = notary))
builder.addCommand(CommercialPaper.Commands.Issue(), signers.first())
builder.setTimeWindow(Instant.now(), Duration.ofMinutes(1))
require(builder.outputStates().mapToSet { it.contract }.size > 1)
serviceHub.signInitialTransaction(builder, signers)
}
}
}

View File

@ -4,20 +4,20 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.* import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeParams
import net.corda.smoketesting.NodeProcess import net.corda.smoketesting.NodeProcess
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.nio.file.Paths
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import java.util.jar.JarFile import java.util.jar.JarFile
import kotlin.streams.toList import kotlin.io.path.Path
import kotlin.io.path.listDirectoryEntries
class NodeVersioningTest { class NodeVersioningTest {
private companion object { private companion object {
@ -27,58 +27,39 @@ class NodeVersioningTest {
private val factory = NodeProcess.Factory() private val factory = NodeProcess.Factory()
private val notaryConfig = NodeConfig(
legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
isNotary = true,
users = listOf(superUser)
)
private val aliceConfig = NodeConfig(
legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
isNotary = false,
users = listOf(superUser)
)
private lateinit var notary: NodeProcess private lateinit var notary: NodeProcess
@Before @Before
fun setUp() { fun startNotary() {
notary = factory.create(notaryConfig) notary = factory.createNotaries(NodeParams(
legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
users = listOf(superUser),
// Find the jar file for the smoke tests of this module
cordappJars = Path("build", "libs").listDirectoryEntries("*-smokeTests*")
))[0]
} }
@After @After
fun done() { fun done() {
notary.close() factory.close()
} }
@Test(timeout=300_000) @Test(timeout=300_000)
fun `platform version in manifest file`() { fun `platform version in manifest file`() {
val manifest = JarFile(factory.cordaJar.toFile()).manifest val manifest = JarFile(NodeProcess.Factory.getCordaJar().toFile()).manifest
assertThat(manifest.mainAttributes.getValue("Corda-Platform-Version").toInt()).isEqualTo(PLATFORM_VERSION) assertThat(manifest.mainAttributes.getValue("Corda-Platform-Version").toInt()).isEqualTo(PLATFORM_VERSION)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
fun `platform version from RPC`() { fun `platform version from RPC`() {
val cordappsDir = (factory.baseDirectory(aliceConfig) / NodeProcess.CORDAPPS_DIR_NAME).createDirectories() notary.connect(superUser).use {
// Find the jar file for the smoke tests of this module val rpc = it.proxy
val selfCordapp = Paths.get("build", "libs").list { assertThat(rpc.protocolVersion).isEqualTo(PLATFORM_VERSION)
it.filter { "-smokeTests" in it.toString() }.toList().single() assertThat(rpc.nodeInfo().platformVersion).isEqualTo(PLATFORM_VERSION)
} assertThat(rpc.startFlow(NodeVersioningTest::GetPlatformVersionFlow).returnValue.getOrThrow()).isEqualTo(PLATFORM_VERSION)
selfCordapp.copyToDirectory(cordappsDir)
factory.create(aliceConfig).use { alice ->
alice.connect(superUser).use {
val rpc = it.proxy
assertThat(rpc.protocolVersion).isEqualTo(PLATFORM_VERSION)
assertThat(rpc.nodeInfo().platformVersion).isEqualTo(PLATFORM_VERSION)
assertThat(rpc.startFlow(NodeVersioningTest::GetPlatformVersionFlow).returnValue.getOrThrow()).isEqualTo(PLATFORM_VERSION)
}
} }
} }

View File

@ -3,11 +3,15 @@ package net.corda.coretests.cordapp
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.flows.* import net.corda.core.flows.FlowInfo
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.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.*
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -21,20 +25,23 @@ import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.createDevNodeCa
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.smoketesting.NodeConfig import net.corda.smoketesting.NodeParams
import net.corda.smoketesting.NodeProcess import net.corda.smoketesting.NodeProcess
import net.corda.smoketesting.NodeProcess.Companion.CORDAPPS_DIR_NAME
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.streams.toList import kotlin.io.path.Path
import kotlin.io.path.createDirectories
import kotlin.io.path.div
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.name
import kotlin.io.path.writeBytes
class CordappSmokeTest { class CordappSmokeTest {
private companion object { private companion object {
@ -44,45 +51,35 @@ class CordappSmokeTest {
private val factory = NodeProcess.Factory() private val factory = NodeProcess.Factory()
private val notaryConfig = NodeConfig( private val aliceConfig = NodeParams(
legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
isNotary = true,
users = listOf(superUser)
)
private val aliceConfig = NodeConfig(
legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"), legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"),
p2pPort = port.andIncrement, p2pPort = port.andIncrement,
rpcPort = port.andIncrement, rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement, rpcAdminPort = port.andIncrement,
isNotary = false, users = listOf(superUser),
users = listOf(superUser) // Find the jar file for the smoke tests of this module
cordappJars = Path("build", "libs").listDirectoryEntries("*-smokeTests*")
) )
private lateinit var notary: NodeProcess
@Before @Before
fun setUp() { fun startNotary() {
notary = factory.create(notaryConfig) factory.createNotaries(NodeParams(
legalName = CordaX500Name(organisation = "Notary Service", locality = "Zurich", country = "CH"),
p2pPort = port.andIncrement,
rpcPort = port.andIncrement,
rpcAdminPort = port.andIncrement,
users = listOf(superUser)
))
} }
@After @After
fun done() { fun done() {
notary.close() factory.close()
} }
@Test(timeout=300_000) @Test(timeout=300_000)
fun `FlowContent appName returns the filename of the CorDapp jar`() { fun `FlowContent appName returns the filename of the CorDapp jar`() {
val baseDir = factory.baseDirectory(aliceConfig) val baseDir = factory.baseDirectory(aliceConfig)
val cordappsDir = (baseDir / CORDAPPS_DIR_NAME).createDirectories()
// Find the jar file for the smoke tests of this module
val selfCordapp = Paths.get("build", "libs").list {
it.filter { "-smokeTests" in it.toString() }.toList().single()
}
selfCordapp.copyToDirectory(cordappsDir)
// The `nodeReadyFuture` in the persistent network map cache will not complete unless there is at least one other // The `nodeReadyFuture` in the persistent network map cache will not complete unless there is at least one other
// node in the network. We work around this limitation by putting another node info file in the additional-node-info // node in the network. We work around this limitation by putting another node info file in the additional-node-info
@ -91,24 +88,17 @@ class CordappSmokeTest {
val additionalNodeInfoDir = (baseDir / "additional-node-infos").createDirectories() val additionalNodeInfoDir = (baseDir / "additional-node-infos").createDirectories()
createDummyNodeInfo(additionalNodeInfoDir) createDummyNodeInfo(additionalNodeInfoDir)
factory.create(aliceConfig).use { alice -> val alice = factory.createNode(aliceConfig)
alice.connect(superUser).use { connectionToAlice -> alice.connect(superUser).use { connectionToAlice ->
val aliceIdentity = connectionToAlice.proxy.nodeInfo().legalIdentitiesAndCerts.first().party val aliceIdentity = connectionToAlice.proxy.nodeInfo().legalIdentitiesAndCerts.first().party
val future = connectionToAlice.proxy.startFlow(CordappSmokeTest::GatherContextsFlow, aliceIdentity).returnValue val future = connectionToAlice.proxy.startFlow(CordappSmokeTest::GatherContextsFlow, aliceIdentity).returnValue
val (sessionInitContext, sessionConfirmContext) = future.getOrThrow() val (sessionInitContext, sessionConfirmContext) = future.getOrThrow()
val selfCordappName = selfCordapp.fileName.toString().removeSuffix(".jar") val selfCordappName = aliceConfig.cordappJars[0].name.removeSuffix(".jar")
assertThat(sessionInitContext.appName).isEqualTo(selfCordappName) assertThat(sessionInitContext.appName).isEqualTo(selfCordappName)
assertThat(sessionConfirmContext.appName).isEqualTo(selfCordappName) assertThat(sessionConfirmContext.appName).isEqualTo(selfCordappName)
}
} }
} }
@Test(timeout=300_000)
fun `empty cordapps directory`() {
(factory.baseDirectory(aliceConfig) / CORDAPPS_DIR_NAME).createDirectories()
factory.create(aliceConfig).close()
}
@InitiatingFlow @InitiatingFlow
@StartableByRPC @StartableByRPC
class GatherContextsFlow(private val otherParty: Party) : FlowLogic<Pair<FlowInfo, FlowInfo>>() { class GatherContextsFlow(private val otherParty: Party) : FlowLogic<Pair<FlowInfo, FlowInfo>>() {
@ -139,7 +129,7 @@ class CordappSmokeTest {
val dummyKeyPair = generateKeyPair() val dummyKeyPair = generateKeyPair()
val nodeInfo = createNodeInfoWithSingleIdentity(CordaX500Name(organisation = "Bob Corp", locality = "Madrid", country = "ES"), dummyKeyPair, dummyKeyPair.public) val nodeInfo = createNodeInfoWithSingleIdentity(CordaX500Name(organisation = "Bob Corp", locality = "Madrid", country = "ES"), dummyKeyPair, dummyKeyPair.public)
val signedNodeInfo = signWith(nodeInfo, listOf(dummyKeyPair.private)) val signedNodeInfo = signWith(nodeInfo, listOf(dummyKeyPair.private))
(additionalNodeInfoDir / "nodeInfo-41408E093F95EAD51F6892C34DEB65AE1A3569A4B0E5744769A1B485AF8E04B5").write(signedNodeInfo.serialize().bytes) (additionalNodeInfoDir / "nodeInfo-41408E093F95EAD51F6892C34DEB65AE1A3569A4B0E5744769A1B485AF8E04B5").writeBytes(signedNodeInfo.serialize().bytes)
} }
private fun createNodeInfoWithSingleIdentity(name: CordaX500Name, nodeKeyPair: KeyPair, identityCertPublicKey: PublicKey): NodeInfo { private fun createNodeInfoWithSingleIdentity(name: CordaX500Name, nodeKeyPair: KeyPair, identityCertPublicKey: PublicKey): NodeInfo {

View File

@ -0,0 +1,304 @@
package net.corda.coretests.verification
import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.notUsed
import net.corda.core.contracts.Amount
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.UnexpectedFlowEndException
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
import net.corda.core.internal.PlatformVersionSwitches.MIGRATE_ATTACHMENT_TO_SIGNATURE_CONSTRAINTS
import net.corda.core.internal.toPath
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow
import net.corda.core.node.NodeInfo
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.coretests.verification.VerificationType.BOTH
import net.corda.coretests.verification.VerificationType.EXTERNAL
import net.corda.finance.DOLLARS
import net.corda.finance.USD
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.AbstractCashFlow
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.flows.CashPaymentFlow
import net.corda.finance.workflows.getCashBalance
import net.corda.nodeapi.internal.config.User
import net.corda.smoketesting.NodeParams
import net.corda.smoketesting.NodeProcess
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.cordapps.workflows411.IssueAndChangeNotaryFlow
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.internal.JarSignatureTestUtils.unsignJar
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.Test
import rx.Observable
import java.net.InetAddress
import java.nio.file.Path
import java.util.Currency
import java.util.concurrent.CompletableFuture
import java.util.concurrent.atomic.AtomicInteger
import kotlin.io.path.Path
import kotlin.io.path.copyTo
import kotlin.io.path.div
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.readText
import kotlin.io.path.useLines
class ExternalVerificationSignedCordappsTest {
private companion object {
private val factory = NodeProcess.Factory(testNetworkParameters(minimumPlatformVersion = MIGRATE_ATTACHMENT_TO_SIGNATURE_CONSTRAINTS))
private lateinit var notaries: List<NodeProcess>
private lateinit var oldNode: NodeProcess
private lateinit var currentNode: NodeProcess
@BeforeClass
@JvmStatic
fun startNodes() {
val (legacyContractsCordapp, legacyWorkflowsCordapp) = listOf("contracts", "workflows").map { smokeTestResource("corda-finance-$it-4.11.jar") }
// The current version finance CorDapp jars
val currentCordapps = listOf("contracts", "workflows").map { smokeTestResource("corda-finance-$it.jar") }
notaries = factory.createNotaries(
nodeParams(DUMMY_NOTARY_NAME, cordappJars = currentCordapps, legacyContractJars = listOf(legacyContractsCordapp)),
nodeParams(CordaX500Name("Notary Service 2", "Zurich", "CH"), currentCordapps)
)
oldNode = factory.createNode(nodeParams(
CordaX500Name("Old", "Delhi", "IN"),
listOf(legacyContractsCordapp, legacyWorkflowsCordapp, smokeTestResource("4.11-workflows-cordapp.jar")),
clientRpcConfig = CordaRPCClientConfiguration(minimumServerProtocolVersion = 13),
version = "4.11"
))
currentNode = factory.createNode(nodeParams(
CordaX500Name("New", "York", "US"),
currentCordapps,
listOf(legacyContractsCordapp)
))
}
@AfterClass
@JvmStatic
fun close() {
factory.close()
// Make sure all UNIX domain files are deleted
(notaries + currentNode).forEach { node ->
node.logFile("node")!!.useLines { lines ->
for (line in lines) {
if ("ExternalVerifierHandleImpl" in line && "Binding to UNIX domain file " in line) {
assertThat(Path(line.substringAfterLast("Binding to UNIX domain file "))).doesNotExist()
}
}
}
}
}
}
@Test(timeout=300_000)
fun `transaction containing 4_11 contract attachment only sent to current node`() {
val (issuanceTx, paymentTx) = cashIssuanceAndPayment(issuer = oldNode, recipient = currentNode)
notaries[0].assertTransactionsWereVerified(EXTERNAL, paymentTx.id)
currentNode.assertTransactionsWereVerified(EXTERNAL, issuanceTx.id, paymentTx.id)
}
@Test(timeout=300_000)
fun `transaction containing 4_11 and 4_12 contract attachments sent to old node`() {
val (issuanceTx, paymentTx) = cashIssuanceAndPayment(issuer = currentNode, recipient = oldNode)
notaries[0].assertTransactionsWereVerified(BOTH, paymentTx.id)
currentNode.assertTransactionsWereVerified(BOTH, issuanceTx.id, paymentTx.id)
}
@Test(timeout=300_000)
fun `notary change transaction`() {
val oldRpc = oldNode.connect(superUser).proxy
val oldNodeInfo = oldRpc.nodeInfo()
val notaryIdentities = oldRpc.notaryIdentities()
for (notary in notaries) {
notary.connect(superUser).use { it.proxy.waitForVisibility(oldNodeInfo) }
}
oldRpc.startFlow(::IssueAndChangeNotaryFlow, notaryIdentities[0], notaryIdentities[1]).returnValue.getOrThrow()
}
private fun cashIssuanceAndPayment(issuer: NodeProcess, recipient: NodeProcess): Pair<SignedTransaction, SignedTransaction> {
val issuerRpc = issuer.connect(superUser).proxy
val recipientRpc = recipient.connect(superUser).proxy
val recipientNodeInfo = recipientRpc.nodeInfo()
val notaryIdentity = issuerRpc.notaryIdentities()[0]
val beforeAmount = recipientRpc.getCashBalance(USD)
val (issuanceTx) = issuerRpc.startFlow(
::CashIssueFlow,
10.DOLLARS,
OpaqueBytes.of(0x01),
notaryIdentity
).returnValue.getOrThrow()
issuerRpc.waitForVisibility(recipientNodeInfo)
recipientRpc.waitForVisibility(issuerRpc.nodeInfo())
val (_, update) = recipientRpc.vaultTrack(Cash.State::class.java)
val cashArrived = update.waitForFirst { true }
val (paymentTx) = issuerRpc.startFlow(
::CashPaymentFlow,
10.DOLLARS,
recipientNodeInfo.legalIdentities[0],
false,
).returnValue.getOrThrow()
cashArrived.getOrThrow()
assertThat(recipientRpc.getCashBalance(USD) - beforeAmount).isEqualTo(10.DOLLARS)
return Pair(issuanceTx, paymentTx)
}
}
class ExternalVerificationUnsignedCordappsTest {
private companion object {
private val factory = NodeProcess.Factory(testNetworkParameters(minimumPlatformVersion = MIGRATE_ATTACHMENT_TO_SIGNATURE_CONSTRAINTS))
private lateinit var notary: NodeProcess
private lateinit var oldNode: NodeProcess
private lateinit var newNode: NodeProcess
@BeforeClass
@JvmStatic
fun startNodes() {
// The 4.11 finance CorDapp jars
val legacyCordapps = listOf(unsignedResourceJar("corda-finance-contracts-4.11.jar"), smokeTestResource("corda-finance-workflows-4.11.jar"))
// The current version finance CorDapp jars
val currentCordapps = listOf(unsignedResourceJar("corda-finance-contracts.jar"), smokeTestResource("corda-finance-workflows.jar"))
notary = factory.createNotaries(nodeParams(DUMMY_NOTARY_NAME, currentCordapps))[0]
oldNode = factory.createNode(nodeParams(
CordaX500Name("Old", "Delhi", "IN"),
legacyCordapps,
clientRpcConfig = CordaRPCClientConfiguration(minimumServerProtocolVersion = 13),
version = "4.11"
))
newNode = factory.createNode(nodeParams(CordaX500Name("New", "York", "US"), currentCordapps))
}
@AfterClass
@JvmStatic
fun close() {
factory.close()
}
private fun unsignedResourceJar(name: String): Path {
val signedJar = smokeTestResource(name)
val copy = signedJar.copyTo(Path("${signedJar.toString().substringBeforeLast(".")}-UNSIGNED.jar"), overwrite = true)
copy.unsignJar()
return copy
}
}
@Test(timeout = 300_000)
fun `transactions can fail verification in external verifier`() {
val issuerRpc = oldNode.connect(superUser).proxy
val recipientRpc = newNode.connect(superUser).proxy
val recipientNodeInfo = recipientRpc.nodeInfo()
val notaryIdentity = issuerRpc.notaryIdentities()[0]
val (issuanceTx) = issuerRpc.startFlow(
::CashIssueFlow,
10.DOLLARS,
OpaqueBytes.of(0x01),
notaryIdentity
).returnValue.getOrThrow()
issuerRpc.waitForVisibility(recipientNodeInfo)
recipientRpc.waitForVisibility(issuerRpc.nodeInfo())
assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy {
issuerRpc.startFlow<AbstractCashFlow.Result, Amount<Currency>, Party, Boolean, CashPaymentFlow>(
::CashPaymentFlow,
10.DOLLARS,
recipientNodeInfo.legalIdentities[0],
false,
).returnValue.getOrThrow()
}
assertThat(newNode.externalVerifierLogs()).contains("WireTransaction(id=${issuanceTx.id}) failed to verify")
}
}
private val superUser = User("superUser", "test", permissions = setOf("ALL"))
private val portCounter = AtomicInteger(15100)
private fun smokeTestResource(name: String): Path = ExternalVerificationSignedCordappsTest::class.java.getResource("/$name")!!.toPath()
private fun nodeParams(
legalName: CordaX500Name,
cordappJars: List<Path> = emptyList(),
legacyContractJars: List<Path> = emptyList(),
clientRpcConfig: CordaRPCClientConfiguration = CordaRPCClientConfiguration.DEFAULT,
version: String? = null
): NodeParams {
return NodeParams(
legalName = legalName,
p2pPort = portCounter.andIncrement,
rpcPort = portCounter.andIncrement,
rpcAdminPort = portCounter.andIncrement,
users = listOf(superUser),
cordappJars = cordappJars,
legacyContractJars = legacyContractJars,
clientRpcConfig = clientRpcConfig,
version = version
)
}
private fun CordaRPCOps.waitForVisibility(other: NodeInfo) {
val (snapshot, updates) = networkMapFeed()
if (other in snapshot) {
updates.notUsed()
} else {
updates.waitForFirst { it.node == other }.getOrThrow()
}
}
private fun <T> Observable<T>.waitForFirst(predicate: (T) -> Boolean): CompletableFuture<Unit> {
val found = CompletableFuture<Unit>()
val subscription = subscribe {
if (predicate(it)) {
found.complete(Unit)
}
}
return found.whenComplete { _, _ -> subscription.unsubscribe() }
}
private fun NodeProcess.assertTransactionsWereVerified(verificationType: VerificationType, vararg txIds: SecureHash) {
val nodeLogs = logContents("node")!!
val externalVerifierLogs = externalVerifierLogs()
for (txId in txIds) {
assertThat(nodeLogs).contains("WireTransaction(id=$txId) will be verified ${verificationType.logStatement}")
if (verificationType != VerificationType.IN_PROCESS) {
assertThat(externalVerifierLogs).describedAs("External verifier was not started").isNotNull()
assertThat(externalVerifierLogs).contains("WireTransaction(id=$txId) verified")
}
}
}
private fun NodeProcess.externalVerifierLogs(): String? = logContents("verifier")
private fun NodeProcess.logFile(name: String): Path? {
return (nodeDir / "logs").listDirectoryEntries("$name-${InetAddress.getLocalHost().hostName}.log").singleOrNull()
}
private fun NodeProcess.logContents(name: String): String? = logFile(name)?.readText()
private enum class VerificationType {
IN_PROCESS, EXTERNAL, BOTH;
val logStatement: String
get() = when (this) {
IN_PROCESS -> "in-process"
EXTERNAL -> "by the external verifer"
BOTH -> "both in-process and by the external verifer"
}
}

View File

@ -56,8 +56,8 @@ class AmountTests {
val splits = baseAmount.splitEvenly(partitionCount) val splits = baseAmount.splitEvenly(partitionCount)
assertEquals(partitionCount, splits.size) assertEquals(partitionCount, splits.size)
assertEquals(baseAmount, splits.sumOrZero(baseAmount.token)) assertEquals(baseAmount, splits.sumOrZero(baseAmount.token))
val min = splits.min()!! val min = splits.min()
val max = splits.max()!! val max = splits.max()
assertTrue(max.quantity - min.quantity <= 1L, "Amount quantities should differ by at most one token") assertTrue(max.quantity - min.quantity <= 1L, "Amount quantities should differ by at most one token")
} }
} }

View File

@ -1,9 +1,17 @@
package net.corda.coretests.contracts package net.corda.coretests.contracts
import com.nhaarman.mockito_kotlin.doReturn import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
import com.nhaarman.mockito_kotlin.mock import net.corda.core.contracts.AutomaticPlaceholderConstraint
import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.BelongsToContract
import net.corda.core.contracts.* import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.HashAttachmentConstraint
import net.corda.core.contracts.NoConstraintPropagation
import net.corda.core.contracts.SignatureAttachmentConstraint
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.WhitelistedByZoneAttachmentConstraint
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.allOnesHash import net.corda.core.crypto.SecureHash.Companion.allOnesHash
@ -14,17 +22,16 @@ import net.corda.core.crypto.sign
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.canBeTransitionedFrom import net.corda.core.internal.canBeTransitionedFrom
import net.corda.core.internal.inputStream import net.corda.core.internal.read
import net.corda.core.internal.requireSupportedHashType import net.corda.core.internal.requireSupportedHashType
import net.corda.core.internal.toPath
import net.corda.core.node.NotaryInfo import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.finance.POUNDS import net.corda.finance.POUNDS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
@ -36,7 +43,15 @@ import net.corda.testing.core.internal.SelfCleaningDir
import net.corda.testing.internal.MockCordappProvider import net.corda.testing.internal.MockCordappProvider
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger import net.corda.testing.node.ledger
import org.junit.* import org.junit.AfterClass
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.security.PublicKey import java.security.PublicKey
import java.util.jar.Attributes import java.util.jar.Attributes
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@ -91,8 +106,8 @@ class ConstraintsPropagationTests {
}, },
networkParameters = testNetworkParameters(minimumPlatformVersion = 4) networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
.copy(whitelistedContractImplementations = mapOf( .copy(whitelistedContractImplementations = mapOf(
Cash.PROGRAM_ID to listOf(SecureHash.zeroHash, SecureHash.allOnesHash), Cash.PROGRAM_ID to listOf(zeroHash, allOnesHash),
noPropagationContractClassName to listOf(SecureHash.zeroHash)), noPropagationContractClassName to listOf(zeroHash)),
notaries = listOf(NotaryInfo(DUMMY_NOTARY, true))) notaries = listOf(NotaryInfo(DUMMY_NOTARY, true)))
) { ) {
override fun loadContractAttachment(stateRef: StateRef) = servicesForResolution.loadContractAttachment(stateRef) override fun loadContractAttachment(stateRef: StateRef) = servicesForResolution.loadContractAttachment(stateRef)
@ -103,13 +118,13 @@ class ConstraintsPropagationTests {
fun `Happy path with the HashConstraint`() { fun `Happy path with the HashConstraint`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash) attachment(Cash.PROGRAM_ID, allOnesHash)
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash) attachment(Cash.PROGRAM_ID, allOnesHash)
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -125,13 +140,13 @@ class ConstraintsPropagationTests {
val cordappAttachmentIds = val cordappAttachmentIds =
cordapps.map { cordapp -> cordapps.map { cordapp ->
val unsignedAttId = val unsignedAttId =
cordapp.jarPath.toPath().inputStream().use { unsignedJarStream -> cordapp.jarPath.openStream().use { unsignedJarStream ->
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", unsignedJarStream, null) ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", unsignedJarStream, null)
} }
val jarAndSigner = ContractJarTestUtils.signContractJar(cordapp.jarPath, copyFirst = true, keyStoreDir = keyStoreDir.path) val jarAndSigner = ContractJarTestUtils.signContractJar(cordapp.jarPath, copyFirst = true, keyStoreDir = keyStoreDir.path)
val signedJar = jarAndSigner.first val signedJar = jarAndSigner.first
val signedAttId = val signedAttId =
signedJar.inputStream().use { signedJarStream -> signedJar.read { signedJarStream ->
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", signedJarStream, null, listOf(jarAndSigner.second)) ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", signedJarStream, null, listOf(jarAndSigner.second))
} }
Pair(unsignedAttId, signedAttId) Pair(unsignedAttId, signedAttId)
@ -163,14 +178,14 @@ class ConstraintsPropagationTests {
fun `Fail early in the TransactionBuilder when attempting to change the hash of the HashConstraint on the spending transaction`() { fun `Fail early in the TransactionBuilder when attempting to change the hash of the HashConstraint on the spending transaction`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
} }
assertFailsWith<IllegalArgumentException> { assertFailsWith<IllegalArgumentException> {
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash) attachment(Cash.PROGRAM_ID, allOnesHash)
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(allOnesHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -184,27 +199,27 @@ class ConstraintsPropagationTests {
fun `Transaction validation fails, when constraints do not propagate correctly`() { fun `Transaction validation fails, when constraints do not propagate correctly`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
failsWith("are not propagated correctly") failsWith("are not propagated correctly")
}) })
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c3", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c3", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
fails() fails()
}) })
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c4", DUMMY_NOTARY, null, AlwaysAcceptAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c4", DUMMY_NOTARY, null, AlwaysAcceptAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -217,13 +232,13 @@ class ConstraintsPropagationTests {
fun `When the constraint of the output state is a valid transition from the input state, transaction validation works`() { fun `When the constraint of the output state is a valid transition from the input state, transaction validation works`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -237,7 +252,7 @@ class ConstraintsPropagationTests {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
@ -245,7 +260,7 @@ class ConstraintsPropagationTests {
// the attachment is signed // the attachment is signed
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey)) attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey))
input("w1") input("w1")
output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -258,14 +273,14 @@ class ConstraintsPropagationTests {
fun `Switching from the WhitelistConstraint to the Signature Constraint fails if the signature constraint does not inherit all jar signatures`() { fun `Switching from the WhitelistConstraint to the Signature Constraint fails if the signature constraint does not inherit all jar signatures`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "w1", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
// the attachment is not signed // the attachment is not signed
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash) attachment(Cash.PROGRAM_ID, zeroHash)
input("w1") input("w1")
output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "w2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(ALICE_PUBKEY), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -279,13 +294,13 @@ class ConstraintsPropagationTests {
fun `On contract annotated with NoConstraintPropagation there is no platform check for propagation, but the transaction builder can't use the AutomaticPlaceholderConstraint`() { fun `On contract annotated with NoConstraintPropagation there is no platform check for propagation, but the transaction builder can't use the AutomaticPlaceholderConstraint`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(noPropagationContractClassName, SecureHash.zeroHash) attachment(noPropagationContractClassName, zeroHash)
output(noPropagationContractClassName, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), NoPropagationContractState()) output(noPropagationContractClassName, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), NoPropagationContractState())
command(ALICE_PUBKEY, NoPropagationContract.Create()) command(ALICE_PUBKEY, NoPropagationContract.Create())
verifies() verifies()
}) })
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(noPropagationContractClassName, SecureHash.zeroHash) attachment(noPropagationContractClassName, zeroHash)
input("c1") input("c1")
output(noPropagationContractClassName, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, NoPropagationContractState()) output(noPropagationContractClassName, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, NoPropagationContractState())
command(ALICE_PUBKEY, NoPropagationContract.Create()) command(ALICE_PUBKEY, NoPropagationContract.Create())
@ -293,7 +308,7 @@ class ConstraintsPropagationTests {
}) })
assertFailsWith<IllegalArgumentException> { assertFailsWith<IllegalArgumentException> {
transaction { transaction {
attachment(noPropagationContractClassName, SecureHash.zeroHash) attachment(noPropagationContractClassName, zeroHash)
input("c1") input("c1")
output(noPropagationContractClassName, "c3", DUMMY_NOTARY, null, AutomaticPlaceholderConstraint, NoPropagationContractState()) output(noPropagationContractClassName, "c3", DUMMY_NOTARY, null, AutomaticPlaceholderConstraint, NoPropagationContractState())
command(ALICE_PUBKEY, NoPropagationContract.Create()) command(ALICE_PUBKEY, NoPropagationContract.Create())
@ -387,13 +402,13 @@ class ConstraintsPropagationTests {
fun `Input state contract version may be incompatible with lower version`() { fun `Input state contract version may be incompatible with lower version`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2")) attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1")) attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -406,13 +421,13 @@ class ConstraintsPropagationTests {
fun `Input state contract version is compatible with the same version`() { fun `Input state contract version is compatible with the same version`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3")) attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3")) attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "3"))
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -425,13 +440,13 @@ class ConstraintsPropagationTests {
fun `Input state contract version is compatible with higher version`() { fun `Input state contract version is compatible with higher version`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1")) attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2")) attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -444,13 +459,13 @@ class ConstraintsPropagationTests {
fun `Input states contract version may be lower that current contract version`() { fun `Input states contract version may be lower that current contract version`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1")) attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "1"))
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2")) attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
@ -469,13 +484,13 @@ class ConstraintsPropagationTests {
fun `Input state with contract version can be downgraded to no version`() { fun `Input state with contract version can be downgraded to no version`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2")) attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), emptyMap()) attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), emptyMap())
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())
@ -488,13 +503,13 @@ class ConstraintsPropagationTests {
fun `Input state without contract version is compatible with any version`() { fun `Input state without contract version is compatible with any version`() {
ledgerServices.ledger(DUMMY_NOTARY) { ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction { ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey), emptyMap()) attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey), emptyMap())
output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY)) output(Cash.PROGRAM_ID, "c1", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), ALICE_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Issue()) command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies() verifies()
}) })
transaction { transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2")) attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), mapOf(Attributes.Name.IMPLEMENTATION_VERSION.toString() to "2"))
input("c1") input("c1")
output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY)) output(Cash.PROGRAM_ID, "c2", DUMMY_NOTARY, null, SignatureAttachmentConstraint(hashToSignatureConstraintsKey), Cash.State(1000.POUNDS `issued by` ALICE_PARTY.ref(1), BOB_PARTY))
command(ALICE_PUBKEY, Cash.Commands.Move()) command(ALICE_PUBKEY, Cash.Commands.Move())

View File

@ -107,4 +107,4 @@ class ContractHierarchyTest {
subFlow(FinalityFlow(tx, otherSideSession)) subFlow(FinalityFlow(tx, otherSideSession))
} }
} }
} }

View File

@ -1,11 +1,16 @@
package net.corda.coretests.contracts package net.corda.coretests.contracts
import net.corda.core.contracts.* import net.corda.core.contracts.CommandData
import net.corda.core.contracts.CommandWithParties
import net.corda.core.contracts.TypeOnlyCommandData
import net.corda.core.contracts.requireSingleCommand
import net.corda.core.contracts.select
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
@ -35,8 +40,8 @@ class RequireSingleCommandTests(private val testFunction: (Collection<CommandWit
@JvmStatic @JvmStatic
@Parameterized.Parameters(name = "{1}") @Parameterized.Parameters(name = "{1}")
fun data(): Collection<Array<Any>> = listOf( fun data(): Collection<Array<Any>> = listOf(
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand<TestCommands>() }, "Inline version"), arrayOf({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand<TestCommands>() }, "Inline version"),
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand(TestCommands::class.java) }, "Interop version") arrayOf({ commands: Collection<CommandWithParties<CommandData>> -> commands.requireSingleCommand(TestCommands::class.java) }, "Interop version")
) )
} }
@ -47,16 +52,18 @@ class RequireSingleCommandTests(private val testFunction: (Collection<CommandWit
assertEquals(returnedCommand, validCommandOne, "they should be the same") assertEquals(returnedCommand, validCommandOne, "they should be the same")
} }
@Test(expected = IllegalArgumentException::class, timeout=300_000) @Test(timeout=300_000)
fun `check error is thrown if more than one valid command`() { fun `check error is thrown if more than one valid command`() {
val commands = listOf(validCommandOne, validCommandTwo) val commands = listOf(validCommandOne, validCommandTwo)
testFunction(commands) assertThatIllegalArgumentException().isThrownBy {
testFunction(commands)
}
} }
@Test(timeout=300_000) @Test(timeout=300_000)
fun `check error is thrown when command is of wrong type`() { fun `check error is thrown when command is of wrong type`() {
val commands = listOf(invalidCommand) val commands = listOf(invalidCommand)
Assertions.assertThatThrownBy { testFunction(commands) } assertThatThrownBy { testFunction(commands) }
.isInstanceOf(IllegalStateException::class.java) .isInstanceOf(IllegalStateException::class.java)
.hasMessage("Required net.corda.coretests.contracts.TestCommands command") .hasMessage("Required net.corda.coretests.contracts.TestCommands command")
} }
@ -69,8 +76,8 @@ class SelectWithSingleInputsTests(private val testFunction: (Collection<CommandW
@JvmStatic @JvmStatic
@Parameterized.Parameters(name = "{1}") @Parameterized.Parameters(name = "{1}")
fun data(): Collection<Array<Any>> = listOf( fun data(): Collection<Array<Any>> = listOf(
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select<TestCommands>(signer, party) }, "Inline version"), arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select<TestCommands>(signer, party) }, "Inline version"),
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select(TestCommands::class.java, signer, party) }, "Interop version") arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signer: PublicKey?, party: AbstractParty? -> commands.select(TestCommands::class.java, signer, party) }, "Interop version")
) )
} }
@ -118,8 +125,8 @@ class SelectWithMultipleInputsTests(private val testFunction: (Collection<Comman
@JvmStatic @JvmStatic
@Parameterized.Parameters(name = "{1}") @Parameterized.Parameters(name = "{1}")
fun data(): Collection<Array<Any>> = listOf( fun data(): Collection<Array<Any>> = listOf(
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select<TestCommands>(signers, party) }, "Inline version"), arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select<TestCommands>(signers, party) }, "Inline version"),
arrayOf<Any>({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select(TestCommands::class.java, signers, party) }, "Interop version") arrayOf({ commands: Collection<CommandWithParties<CommandData>>, signers: Collection<PublicKey>?, party: Collection<Party>? -> commands.select(TestCommands::class.java, signers, party) }, "Interop version")
) )
} }

View File

@ -1,8 +1,8 @@
package net.corda.coretests.contracts package net.corda.coretests.contracts
import com.nhaarman.mockito_kotlin.doReturn import org.mockito.kotlin.doReturn
import com.nhaarman.mockito_kotlin.mock import org.mockito.kotlin.mock
import com.nhaarman.mockito_kotlin.whenever import org.mockito.kotlin.whenever
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -110,4 +110,4 @@ class DummyContract : Contract {
} }
} }
class DummyIssue : TypeOnlyCommandData() class DummyIssue : TypeOnlyCommandData()

View File

@ -3,7 +3,6 @@ package net.corda.coretests.crypto
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey.NodeAndWeight import net.corda.core.crypto.CompositeKey.NodeAndWeight
import net.corda.core.internal.declaredField import net.corda.core.internal.declaredField
import net.corda.core.internal.div
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toBase58String import net.corda.core.utilities.toBase58String
@ -19,6 +18,7 @@ import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.security.PublicKey import java.security.PublicKey
import javax.security.auth.x500.X500Principal import javax.security.auth.x500.X500Principal
import kotlin.io.path.div
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertFalse import kotlin.test.assertFalse
@ -295,21 +295,19 @@ class CompositeKeyTests {
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID))) val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID)))
val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID))) val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID)))
val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID))) val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID)))
val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID))) val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID)))
val SPSignature = keyPairSP.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairSP.public).schemeNumberID)))
val compositeKey = CompositeKey.Builder().addKeys(keyPairRSA.public, keyPairK1.public, keyPairR1.public, keyPairEd.public, keyPairSP.public).build() as CompositeKey val compositeKey = CompositeKey.Builder().addKeys(keyPairRSA.public, keyPairK1.public, keyPairR1.public, keyPairEd.public).build() as CompositeKey
val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature) val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature)
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) } assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
// One signature is missing. // One signature is missing.
val signaturesWithoutRSA = listOf(K1Signature, R1Signature, EdSignature, SPSignature) val signaturesWithoutRSA = listOf(K1Signature, R1Signature, EdSignature)
assertFalse { compositeKey.isFulfilledBy(signaturesWithoutRSA.byKeys()) } assertFalse { compositeKey.isFulfilledBy(signaturesWithoutRSA.byKeys()) }
} }
@ -320,20 +318,18 @@ class CompositeKeyTests {
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256) val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val keyPairEd = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val keyPairSP = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID))) val RSASignature = keyPairRSA.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairRSA.public).schemeNumberID)))
val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID))) val K1Signature = keyPairK1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairK1.public).schemeNumberID)))
val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID))) val R1Signature = keyPairR1.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairR1.public).schemeNumberID)))
val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID))) val EdSignature = keyPairEd.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairEd.public).schemeNumberID)))
val SPSignature = keyPairSP.sign(SignableData(secureHash, SignatureMetadata(1, Crypto.findSignatureScheme(keyPairSP.public).schemeNumberID)))
val compositeKey = CompositeKey.Builder().addKeys(keyPairRSA.public, keyPairK1.public, keyPairR1.public, keyPairEd.public, keyPairSP.public).build() as CompositeKey val compositeKey = CompositeKey.Builder().addKeys(keyPairRSA.public, keyPairK1.public, keyPairR1.public, keyPairEd.public).build() as CompositeKey
val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature, SPSignature) val signatures = listOf(RSASignature, K1Signature, R1Signature, EdSignature)
assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) } assertTrue { compositeKey.isFulfilledBy(signatures.byKeys()) }
// One signature is missing. // One signature is missing.
val signaturesWithoutRSA = listOf(K1Signature, R1Signature, EdSignature, SPSignature) val signaturesWithoutRSA = listOf(K1Signature, R1Signature, EdSignature)
assertFalse { compositeKey.isFulfilledBy(signaturesWithoutRSA.byKeys()) } assertFalse { compositeKey.isFulfilledBy(signaturesWithoutRSA.byKeys()) }
// Create self sign CA. // Create self sign CA.
@ -374,13 +370,12 @@ class CompositeKeyTests {
val (_, pub3) = Crypto.generateKeyPair(Crypto.RSA_SHA256) val (_, pub3) = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val (_, pub4) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512) val (_, pub4) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (_, pub5) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val (_, pub5) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (_, pub6) = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256) val (_, pub6) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (_, pub7) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
// Using default weight = 1, thus all weights are equal. // Using default weight = 1, thus all weights are equal.
val composite1 = CompositeKey.Builder().addKeys(pub1, pub2, pub3, pub4, pub5, pub6, pub7).build() as CompositeKey val composite1 = CompositeKey.Builder().addKeys(pub1, pub2, pub3, pub4, pub5, pub6).build() as CompositeKey
// Store in reverse order. // Store in reverse order.
val composite2 = CompositeKey.Builder().addKeys(pub7, pub6, pub5, pub4, pub3, pub2, pub1).build() as CompositeKey val composite2 = CompositeKey.Builder().addKeys(pub6, pub5, pub4, pub3, pub2, pub1).build() as CompositeKey
// There are 7! = 5040 permutations, but as sorting is deterministic the following should never fail. // There are 7! = 5040 permutations, but as sorting is deterministic the following should never fail.
assertEquals(composite1.children, composite2.children) assertEquals(composite1.children, composite2.children)
} }

View File

@ -1,11 +1,19 @@
package net.corda.coretests.crypto package net.corda.coretests.crypto
import com.nhaarman.mockito_kotlin.doReturn import net.corda.core.contracts.Command
import com.nhaarman.mockito_kotlin.mock import net.corda.core.contracts.PrivacySalt
import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.StateRef
import net.corda.core.contracts.* import net.corda.core.contracts.TimeWindow
import net.corda.core.crypto.* import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.internal.DigestAlgorithmFactory import net.corda.core.crypto.internal.DigestAlgorithmFactory
import net.corda.core.crypto.keys
import net.corda.core.crypto.randomHash
import net.corda.core.crypto.sha256
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.BLAKE2s256DigestAlgorithm import net.corda.core.internal.BLAKE2s256DigestAlgorithm
@ -16,9 +24,10 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.transactions.ReferenceStateRef import net.corda.core.transactions.ReferenceStateRef
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
@ -26,20 +35,29 @@ import net.corda.testing.core.TestIdentity
import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.LedgerDSL
import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.testing.internal.createWireTransaction import net.corda.testing.internal.createWireTransaction
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.security.PublicKey import java.security.PublicKey
import java.util.function.Predicate import java.util.function.Predicate
import java.util.stream.IntStream import java.util.stream.IntStream
import kotlin.streams.toList import kotlin.streams.toList
import kotlin.test.* import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
@RunWith(Parameterized::class) @RunWith(Parameterized::class)
class PartialMerkleTreeTest(private var digestService: DigestService) { class PartialMerkleTreeTest(private var digestService: DigestService) {
@ -204,7 +222,7 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `nothing filtered`() { fun `nothing filtered`() {
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false }) val ftxNothing = testTx.buildFilteredTransaction { false }
assertTrue(ftxNothing.componentGroups.isEmpty()) assertTrue(ftxNothing.componentGroups.isEmpty())
assertTrue(ftxNothing.attachments.isEmpty()) assertTrue(ftxNothing.attachments.isEmpty())
assertTrue(ftxNothing.commands.isEmpty()) assertTrue(ftxNothing.commands.isEmpty())
@ -291,10 +309,12 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
assertFalse(pmt.verify(wrongRoot, inclHashes)) assertFalse(pmt.verify(wrongRoot, inclHashes))
} }
@Test(expected = Exception::class, timeout=300_000) @Test(timeout=300_000)
fun `hash map serialization not allowed`() { fun `hash map serialization not allowed`() {
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4) val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
hm1.serialize() assertThatIllegalArgumentException().isThrownBy {
hm1.serialize()
}
} }
private fun makeSimpleCashWtx( private fun makeSimpleCashWtx(
@ -322,11 +342,11 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
val merkleTree = MerkleTree.getMerkleTree(sampleLeaves, digestService) val merkleTree = MerkleTree.getMerkleTree(sampleLeaves, digestService)
// Provided hashes are not in the tree. // Provided hashes are not in the tree.
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("20"))) } assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf(digestService.hash("20"))) }
// One of the provided hashes is not in the tree. // One of the provided hashes is not in the tree.
assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("20"), digestService.hash("1"), digestService.hash("5"))) } assertFailsWith<MerkleTreeException> { PartialMerkleTree.build(merkleTree, listOf(digestService.hash("20"), digestService.hash("1"), digestService.hash("5"))) }
val pmt = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("1"), digestService.hash("5"), digestService.hash("0"), digestService.hash("19"))) val pmt = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("1"), digestService.hash("5"), digestService.hash("0"), digestService.hash("19")))
// First leaf. // First leaf.
assertEquals(0, pmt.leafIndex(digestService.hash("0"))) assertEquals(0, pmt.leafIndex(digestService.hash("0")))
// Second leaf. // Second leaf.
@ -340,17 +360,17 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
// The provided hash is not in the tree (using a leaf that didn't exist in the original Merkle tree). // The provided hash is not in the tree (using a leaf that didn't exist in the original Merkle tree).
assertFailsWith<MerkleTreeException> { pmt.leafIndex(digestService.hash("30")) } assertFailsWith<MerkleTreeException> { pmt.leafIndex(digestService.hash("30")) }
val pmtFirstElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("0"))) val pmtFirstElementOnly = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("0")))
assertEquals(0, pmtFirstElementOnly.leafIndex(digestService.hash("0"))) assertEquals(0, pmtFirstElementOnly.leafIndex(digestService.hash("0")))
// The provided hash is not in the tree. // The provided hash is not in the tree.
assertFailsWith<MerkleTreeException> { pmtFirstElementOnly.leafIndex(digestService.hash("10")) } assertFailsWith<MerkleTreeException> { pmtFirstElementOnly.leafIndex(digestService.hash("10")) }
val pmtLastElementOnly = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("19"))) val pmtLastElementOnly = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("19")))
assertEquals(19, pmtLastElementOnly.leafIndex(digestService.hash("19"))) assertEquals(19, pmtLastElementOnly.leafIndex(digestService.hash("19")))
// The provided hash is not in the tree. // The provided hash is not in the tree.
assertFailsWith<MerkleTreeException> { pmtLastElementOnly.leafIndex(digestService.hash("10")) } assertFailsWith<MerkleTreeException> { pmtLastElementOnly.leafIndex(digestService.hash("10")) }
val pmtOneElement = PartialMerkleTree.build(merkleTree, listOf<SecureHash>(digestService.hash("5"))) val pmtOneElement = PartialMerkleTree.build(merkleTree, listOf(digestService.hash("5")))
assertEquals(5, pmtOneElement.leafIndex(digestService.hash("5"))) assertEquals(5, pmtOneElement.leafIndex(digestService.hash("5")))
// The provided hash is not in the tree. // The provided hash is not in the tree.
assertFailsWith<MerkleTreeException> { pmtOneElement.leafIndex(digestService.hash("10")) } assertFailsWith<MerkleTreeException> { pmtOneElement.leafIndex(digestService.hash("10")) }

View File

@ -1,17 +1,14 @@
package net.corda.coretests.crypto package net.corda.coretests.crypto
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.PrivacySalt import net.corda.core.contracts.PrivacySalt
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow import net.corda.core.contracts.TimeWindow
import net.corda.core.contracts.TransactionState import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.MerkleTree import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.PartialMerkleTree import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.SHA2_384 import net.corda.core.crypto.SecureHash.Companion.SHA2_384
import net.corda.core.crypto.SecureHash.Companion.hashAs import net.corda.core.crypto.SecureHash.Companion.hashAs
@ -26,9 +23,10 @@ import net.corda.core.serialization.serialize
import net.corda.core.transactions.ReferenceStateRef import net.corda.core.transactions.ReferenceStateRef
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
@ -36,10 +34,10 @@ import net.corda.testing.core.TestIdentity
import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.LedgerDSL
import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.testing.internal.createWireTransaction import net.corda.testing.internal.createWireTransaction
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.Assert.assertFalse import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
@ -49,6 +47,9 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.security.PublicKey import java.security.PublicKey
import java.util.function.Predicate import java.util.function.Predicate
import java.util.stream.IntStream import java.util.stream.IntStream
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `nothing filtered`() { fun `nothing filtered`() {
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false }) val ftxNothing = testTx.buildFilteredTransaction { false }
assertTrue(ftxNothing.componentGroups.isEmpty()) assertTrue(ftxNothing.componentGroups.isEmpty())
assertTrue(ftxNothing.attachments.isEmpty()) assertTrue(ftxNothing.attachments.isEmpty())
assertTrue(ftxNothing.commands.isEmpty()) assertTrue(ftxNothing.commands.isEmpty())
@ -296,10 +297,12 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
assertFalse(pmt.verify(wrongRoot, inclHashes)) assertFalse(pmt.verify(wrongRoot, inclHashes))
} }
@Test(expected = Exception::class, timeout=300_000) @Test(timeout=300_000)
fun `hash map serialization not allowed`() { fun `hash map serialization not allowed`() {
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4) val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
hm1.serialize() assertThatIllegalArgumentException().isThrownBy {
hm1.serialize()
}
} }
private fun makeSimpleCashWtx( private fun makeSimpleCashWtx(
@ -381,4 +384,4 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
assertEquals(1, ftx.references.size) assertEquals(1, ftx.references.size)
ftx.verify() ftx.verify()
} }
} }

View File

@ -1,17 +1,14 @@
package net.corda.coretests.crypto package net.corda.coretests.crypto
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.Command import net.corda.core.contracts.Command
import net.corda.core.contracts.PrivacySalt import net.corda.core.contracts.PrivacySalt
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow import net.corda.core.contracts.TimeWindow
import net.corda.core.contracts.TransactionState import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.MerkleTree import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.MerkleTreeException import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.PartialMerkleTree import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.DigestService
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.SHA2_384 import net.corda.core.crypto.SecureHash.Companion.SHA2_384
import net.corda.core.crypto.SecureHash.Companion.hashAs import net.corda.core.crypto.SecureHash.Companion.hashAs
@ -26,9 +23,10 @@ import net.corda.core.serialization.serialize
import net.corda.core.transactions.ReferenceStateRef import net.corda.core.transactions.ReferenceStateRef
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.finance.DOLLARS import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
@ -36,10 +34,10 @@ import net.corda.testing.core.TestIdentity
import net.corda.testing.dsl.LedgerDSL import net.corda.testing.dsl.LedgerDSL
import net.corda.testing.dsl.TestLedgerDSLInterpreter import net.corda.testing.dsl.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.testing.internal.createWireTransaction import net.corda.testing.internal.createWireTransaction
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.Assert.assertFalse import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull import org.junit.Assert.assertNotNull
@ -49,6 +47,9 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.security.PublicKey import java.security.PublicKey
import java.util.function.Predicate import java.util.function.Predicate
import java.util.stream.IntStream import java.util.stream.IntStream
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashTest {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `nothing filtered`() { fun `nothing filtered`() {
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false }) val ftxNothing = testTx.buildFilteredTransaction { false }
assertTrue(ftxNothing.componentGroups.isEmpty()) assertTrue(ftxNothing.componentGroups.isEmpty())
assertTrue(ftxNothing.attachments.isEmpty()) assertTrue(ftxNothing.attachments.isEmpty())
assertTrue(ftxNothing.commands.isEmpty()) assertTrue(ftxNothing.commands.isEmpty())
@ -296,10 +297,12 @@ class PartialMerkleTreeWithNamedHashTest {
assertFalse(pmt.verify(wrongRoot, inclHashes)) assertFalse(pmt.verify(wrongRoot, inclHashes))
} }
@Test(expected = Exception::class, timeout=300_000) @Test(timeout=300_000)
fun `hash map serialization not allowed`() { fun `hash map serialization not allowed`() {
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4) val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
hm1.serialize() assertThatIllegalArgumentException().isThrownBy {
hm1.serialize()
}
} }
private fun makeSimpleCashWtx( private fun makeSimpleCashWtx(
@ -381,4 +384,4 @@ class PartialMerkleTreeWithNamedHashTest {
assertEquals(1, ftx.references.size) assertEquals(1, ftx.references.size)
ftx.verify() ftx.verify()
} }
} }

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.sign
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -35,12 +36,14 @@ class SignedDataTest {
assertEquals(data, unwrappedData) assertEquals(data, unwrappedData)
} }
@Test(expected = SignatureException::class, timeout=300_000) @Test(timeout=300_000)
fun `make sure incorrectly signed data raises an exception`() { fun `make sure incorrectly signed data raises an exception`() {
val keyPairA = generateKeyPair() val keyPairA = generateKeyPair()
val keyPairB = generateKeyPair() val keyPairB = generateKeyPair()
val sig = keyPairA.private.sign(serialized.bytes, keyPairB.public) val sig = keyPairA.private.sign(serialized.bytes, keyPairB.public)
val wrappedData = SignedData(serialized, sig) val wrappedData = SignedData(serialized, sig)
wrappedData.verified() assertThatExceptionOfType(SignatureException::class.java).isThrownBy {
wrappedData.verified()
}
} }
} }

View File

@ -1,7 +1,17 @@
package net.corda.coretests.crypto package net.corda.coretests.crypto
import net.corda.core.crypto.* import net.corda.core.crypto.Crypto
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.MerkleTreeException
import net.corda.core.crypto.PartialMerkleTree
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureMetadata
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.sha256
import net.corda.core.crypto.sign
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.math.BigInteger import java.math.BigInteger
@ -39,12 +49,14 @@ class TransactionSignatureTest {
} }
/** Verification should fail; corrupted metadata - clearData (Merkle root) has changed. */ /** Verification should fail; corrupted metadata - clearData (Merkle root) has changed. */
@Test(expected = SignatureException::class,timeout=300_000) @Test(timeout=300_000)
fun `Signature metadata full failure clearData has changed`() { fun `Signature metadata full failure clearData has changed`() {
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256") val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID)) val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
val transactionSignature = keyPair.sign(signableData) val transactionSignature = keyPair.sign(signableData)
Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature) assertThatExceptionOfType(SignatureException::class.java).isThrownBy {
Crypto.doVerify((testBytes + testBytes).sha256(), transactionSignature)
}
} }
@Test(timeout=300_000) @Test(timeout=300_000)

View File

@ -1,6 +1,7 @@
package net.corda.coretests.flows package net.corda.coretests.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import co.paralleluniverse.strands.Strand
import net.corda.core.CordaException import net.corda.core.CordaException
import net.corda.core.flows.FlowExternalAsyncOperation import net.corda.core.flows.FlowExternalAsyncOperation
import net.corda.core.flows.FlowExternalOperation import net.corda.core.flows.FlowExternalOperation
@ -133,7 +134,7 @@ abstract class AbstractFlowExternalOperationTest {
fun createFuture(): CompletableFuture<Any> { fun createFuture(): CompletableFuture<Any> {
return CompletableFuture.supplyAsync(Supplier<Any> { return CompletableFuture.supplyAsync(Supplier<Any> {
log.info("Starting sleep inside of future") log.info("Starting sleep inside of future")
Thread.sleep(1000) Strand.sleep(1000)
log.info("Finished sleep inside of future") log.info("Finished sleep inside of future")
"Here is your return value" "Here is your return value"
}, executorService) }, executorService)
@ -255,4 +256,4 @@ abstract class AbstractFlowExternalOperationTest {
return function(serviceHub, deduplicationId) return function(serviceHub, deduplicationId)
} }
} }
} }

View File

@ -23,22 +23,22 @@ import net.corda.core.identity.groupAbstractPartyByWellKnownParty
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.coretesting.internal.matchers.flow.willReturn
import net.corda.coretesting.internal.matchers.flow.willThrow
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.coretesting.internal.matchers.flow.willReturn
import net.corda.coretesting.internal.matchers.flow.willThrow
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
import net.corda.testing.node.internal.InternalMockNetwork import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.TestStartedNode import net.corda.testing.node.internal.TestStartedNode
import net.corda.testing.node.internal.enclosedCordapp import net.corda.testing.node.internal.enclosedCordapp
import org.hamcrest.CoreMatchers.`is` import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.AfterClass import org.junit.AfterClass
import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.security.PublicKey import java.security.PublicKey
@ -92,7 +92,7 @@ class CollectSignaturesFlowTests : WithContracts {
mockNet.runNetwork() mockNet.runNetwork()
val stx = future.get() val stx = future.get()
val missingSigners = stx.getMissingSigners() val missingSigners = stx.getMissingSigners()
Assert.assertThat(missingSigners, `is`(emptySet())) assertThat(missingSigners).isEmpty()
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -122,10 +122,10 @@ class CollectSignaturesFlowTests : WithContracts {
mockNet.runNetwork() mockNet.runNetwork()
val stx = future.get() val stx = future.get()
val missingSigners = stx.getMissingSigners() val missingSigners = stx.getMissingSigners()
Assert.assertThat(missingSigners, `is`(emptySet())) assertThat(missingSigners).isEmpty()
} }
@Test(expected = IllegalArgumentException::class, timeout=300_000) @Test(timeout=300_000)
fun `throws exception when extra sessions are initiated`() { fun `throws exception when extra sessions are initiated`() {
bobNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java) bobNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
charlieNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java) charlieNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
@ -137,7 +137,9 @@ class CollectSignaturesFlowTests : WithContracts {
listOf(bobNode.info.singleIdentity(), alice))) listOf(bobNode.info.singleIdentity(), alice)))
.resultFuture .resultFuture
mockNet.runNetwork() mockNet.runNetwork()
future.getOrThrow() assertThatIllegalArgumentException().isThrownBy {
future.getOrThrow()
}
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -152,7 +154,7 @@ class CollectSignaturesFlowTests : WithContracts {
listOf(bobNode.info.singleIdentity(), alice))).resultFuture listOf(bobNode.info.singleIdentity(), alice))).resultFuture
mockNet.runNetwork() mockNet.runNetwork()
val signedTx = future.getOrThrow() val signedTx = future.getOrThrow()
Assert.assertThat(signedTx.getMissingSigners(), `is`(emptySet())) assertThat(signedTx.getMissingSigners()).isEmpty()
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -216,7 +218,7 @@ class CollectSignaturesFlowTests : WithContracts {
} }
} }
@InitiatedBy(TestFlow.Initiator::class) @InitiatedBy(Initiator::class)
class Responder(private val otherSideSession: FlowSession) : FlowLogic<Unit>() { class Responder(private val otherSideSession: FlowSession) : FlowLogic<Unit>() {
@Suspendable @Suspendable
override fun call() { override fun call() {
@ -251,7 +253,7 @@ class AnonymousSessionTestFlow(private val cis: List<PartyAndCertificate>) : Flo
} }
} }
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) }) val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
val create = net.corda.testing.contracts.DummyContract.Commands.Create() val create = DummyContract.Commands.Create()
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first()) val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
.addOutputState(state) .addOutputState(state)
.addCommand(create, cis.map { it.owningKey }) .addCommand(create, cis.map { it.owningKey })
@ -289,7 +291,7 @@ class MixAndMatchAnonymousSessionTestFlow(private val cis: List<PartyAndCertific
} }
} }
val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) }) val state = DummyContract.MultiOwnerState(owners = cis.map { AnonymousParty(it.owningKey) })
val create = net.corda.testing.contracts.DummyContract.Commands.Create() val create = DummyContract.Commands.Create()
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first()) val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
.addOutputState(state) .addOutputState(state)
.addCommand(create, cis.map { it.owningKey }) .addCommand(create, cis.map { it.owningKey })
@ -324,7 +326,7 @@ class ExtraSessionsFlow(private val openFor: List<Party>, private val involve: L
val sessions = openFor.map { initiateFlow(it) } val sessions = openFor.map { initiateFlow(it) }
val state = DummyContract.MultiOwnerState(owners = involve.map { AnonymousParty(it.owningKey) }) val state = DummyContract.MultiOwnerState(owners = involve.map { AnonymousParty(it.owningKey) })
val create = net.corda.testing.contracts.DummyContract.Commands.Create() val create = DummyContract.Commands.Create()
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first()) val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
.addOutputState(state) .addOutputState(state)
.addCommand(create, involve.map { it.owningKey }) .addCommand(create, involve.map { it.owningKey })

View File

@ -9,6 +9,7 @@ import net.corda.core.CordaRuntimeException
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.flows.ContractUpgradeFlow import net.corda.core.flows.ContractUpgradeFlow
import net.corda.core.internal.getRequiredTransaction
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.transactions.ContractUpgradeLedgerTransaction import net.corda.core.transactions.ContractUpgradeLedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -120,8 +121,7 @@ class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
isUpgrade<FROM, TO>()) isUpgrade<FROM, TO>())
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) = private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
services.validatedTransactions.getTransaction(state.ref.txhash)!! services.getRequiredTransaction(state.ref.txhash).resolveContractUpgradeTransaction(services)
.resolveContractUpgradeTransaction(services)
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() = private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
isUpgradeFrom<FROM>() and isUpgradeTo<TO>() isUpgradeFrom<FROM>() and isUpgradeTo<TO>()

View File

@ -1,31 +1,54 @@
package net.corda.coretests.flows package net.corda.coretests.flows
import com.natpryce.hamkrest.* import com.natpryce.hamkrest.Matcher
import com.natpryce.hamkrest.and
import com.natpryce.hamkrest.anything
import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.assertion.assertThat
import net.corda.core.contracts.* import com.natpryce.hamkrest.equalTo
import com.natpryce.hamkrest.has
import com.natpryce.hamkrest.isA
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
import net.corda.core.contracts.Amount
import net.corda.core.contracts.AttachmentConstraint
import net.corda.core.contracts.BelongsToContract
import net.corda.core.contracts.CommandAndState
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.FungibleAsset
import net.corda.core.contracts.Issued
import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.TypeOnlyCommandData
import net.corda.core.contracts.UpgradedContractWithLegacyConstraint
import net.corda.core.flows.UnexpectedFlowEndException import net.corda.core.flows.UnexpectedFlowEndException
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.internal.Emoji import net.corda.core.internal.Emoji
import net.corda.core.internal.getRequiredTransaction
import net.corda.core.internal.mapToSet
import net.corda.core.transactions.ContractUpgradeLedgerTransaction import net.corda.core.transactions.ContractUpgradeLedgerTransaction
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.coretesting.internal.matchers.flow.willReturn
import net.corda.coretesting.internal.matchers.flow.willThrow
import net.corda.finance.USD import net.corda.finance.USD
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueFlow import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.`issued by`
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyContractV2 import net.corda.testing.contracts.DummyContractV2
import net.corda.testing.contracts.DummyContractV3 import net.corda.testing.contracts.DummyContractV3
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.coretesting.internal.matchers.flow.willReturn import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
import net.corda.coretesting.internal.matchers.flow.willThrow import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
import net.corda.testing.node.internal.* import net.corda.testing.node.internal.FINANCE_WORKFLOWS_CORDAPP
import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.TestStartedNode
import net.corda.testing.node.internal.enclosedCordapp
import net.corda.testing.node.internal.startFlow
import org.junit.AfterClass import org.junit.AfterClass
import org.junit.Test import org.junit.Test
import java.util.* import java.util.Currency
class ContractUpgradeFlowTest : WithContracts, WithFinality { class ContractUpgradeFlowTest : WithContracts, WithFinality {
@ -159,7 +182,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
@BelongsToContract(CashV2::class) @BelongsToContract(CashV2::class)
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> { data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> {
override val owner: AbstractParty = owners.first() override val owner: AbstractParty = owners.first()
override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet() override val exitKeys = (owners + amount.token.issuer.party).mapToSet { it.owningKey }
override val participants = owners override val participants = owners
override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner)) override fun withNewOwnerAndAmount(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner))
@ -180,8 +203,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
isUpgrade<FROM, TO>()) isUpgrade<FROM, TO>())
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) = private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
services.validatedTransactions.getTransaction(state.ref.txhash)!! services.getRequiredTransaction(state.ref.txhash).resolveContractUpgradeTransaction(services)
.resolveContractUpgradeTransaction(services)
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() = private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
isUpgradeFrom<FROM>() and isUpgradeTo<TO>() isUpgradeFrom<FROM>() and isUpgradeTo<TO>()

View File

@ -13,7 +13,7 @@ import net.corda.core.internal.rootCause
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import org.assertj.core.api.Assertions.catchThrowable import org.assertj.core.api.Assertions.catchThrowable
import org.hamcrest.Matchers.lessThanOrEqualTo import org.hamcrest.Matchers.lessThanOrEqualTo
import org.junit.Assert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
@ -85,7 +85,7 @@ class FastThreadLocalTest {
} }
private class UnserializableObj { private class UnserializableObj {
@Suppress("unused") @Suppress("unused", "IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION")
private val fail: Nothing by lazy { throw UnsupportedOperationException("Nice try.") } private val fail: Nothing by lazy { throw UnsupportedOperationException("Nice try.") }
} }

View File

@ -15,6 +15,7 @@ import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.junit.Test import org.junit.Test
import java.io.Serializable
import java.sql.SQLTransientConnectionException import java.sql.SQLTransientConnectionException
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@ -22,6 +23,8 @@ import kotlin.test.assertTrue
class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() { class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
private fun interface SerializableLambda2<S, T, R> : (S, T) -> R, Serializable
@Test(timeout = 300_000) @Test(timeout = 300_000)
fun `external async operation`() { fun `external async operation`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
@ -196,15 +199,15 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
@StartableByRPC @StartableByRPC
class FlowWithExternalAsyncOperationPropagatesException<T>(party: Party, private val exceptionType: Class<T>) : class FlowWithExternalAsyncOperationPropagatesException<T>(party: Party, private val exceptionType: Class<T>) :
FlowWithExternalProcess(party) { FlowWithExternalProcess(party) {
@Suspendable @Suspendable
override fun testCode(): Any { override fun testCode(): Any {
val e = createException() val e = createException()
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
return await(ExternalAsyncOperation(serviceHub, (SerializableLambda2 { _, _ ->
CompletableFuture<Any>().apply { CompletableFuture<Any>().apply {
completeExceptionally(e) completeExceptionally(e)
} }
}) })))
} }
private fun createException() = when (exceptionType) { private fun createException() = when (exceptionType) {
@ -252,7 +255,6 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
@StartableByRPC @StartableByRPC
class FlowWithExternalAsyncOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) { class FlowWithExternalAsyncOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
@Suppress("TooGenericExceptionCaught")
@Suspendable @Suspendable
override fun testCode(): Any { override fun testCode(): Any {
return await(ExternalAsyncOperation(serviceHub) { _, _ -> return await(ExternalAsyncOperation(serviceHub) { _, _ ->
@ -287,4 +289,4 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
serviceHub.cordaService(FutureService::class.java).startMultipleFuturesAndJoin() serviceHub.cordaService(FutureService::class.java).startMultipleFuturesAndJoin()
}.also { log.info("Result - $it") }) }.also { log.info("Result - $it") })
} }
} }

View File

@ -21,12 +21,15 @@ import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.node.internal.cordappsForPackages import net.corda.testing.node.internal.cordappsForPackages
import org.junit.Test import org.junit.Test
import java.io.Serializable
import java.sql.SQLTransientConnectionException import java.sql.SQLTransientConnectionException
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
class FlowExternalOperationTest : AbstractFlowExternalOperationTest() { class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
private fun interface SerializableLambda2<S, T, R> : (S, T) -> R, Serializable
@Test(timeout = 300_000) @Test(timeout = 300_000)
fun `external operation`() { fun `external operation`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
@ -254,7 +257,7 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@Suspendable @Suspendable
override fun testCode() { override fun testCode() {
val e = createException() val e = createException()
await(ExternalOperation(serviceHub) { _, _ -> throw e }) await<Nothing>(ExternalOperation(serviceHub, (SerializableLambda2 { _, _ -> throw e })))
} }
private fun createException() = when (exceptionType) { private fun createException() = when (exceptionType) {
@ -270,7 +273,7 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@Suspendable @Suspendable
override fun testCode(): Any = try { override fun testCode(): Any = try {
await(ExternalOperation(serviceHub) { _, _ -> await<Nothing>(ExternalOperation(serviceHub) { _, _ ->
throw IllegalStateException("threw exception in background process") throw IllegalStateException("threw exception in background process")
}) })
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
@ -284,7 +287,7 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@Suspendable @Suspendable
override fun testCode(): Any = override fun testCode(): Any =
await(ExternalOperation(serviceHub) { serviceHub, _ -> await<Nothing>(ExternalOperation(serviceHub) { serviceHub, _ ->
serviceHub.cordaService(FutureService::class.java).throwHospitalHandledException() serviceHub.cordaService(FutureService::class.java).throwHospitalHandledException()
}) })
} }
@ -292,11 +295,10 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@StartableByRPC @StartableByRPC
class FlowWithExternalOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) { class FlowWithExternalOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
@Suppress("TooGenericExceptionCaught")
@Suspendable @Suspendable
override fun testCode(): Any { override fun testCode(): Any {
try { try {
await(ExternalOperation(serviceHub) { _, _ -> await<Nothing>(ExternalOperation(serviceHub) { _, _ ->
serviceHub.cordaService(FutureService::class.java).throwHospitalHandledException() serviceHub.cordaService(FutureService::class.java).throwHospitalHandledException()
}) })
} catch (e: NullPointerException) { } catch (e: NullPointerException) {
@ -430,4 +432,4 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
}) })
} }
} }
} }

View File

@ -79,7 +79,7 @@ class FlowSleepTest {
@Suspendable @Suspendable
override fun call(): Pair<Instant, Instant> { override fun call(): Pair<Instant, Instant> {
val start = Instant.now() val start = Instant.now()
sleep(5.seconds) sleep(6.seconds)
return start to Instant.now() return start to Instant.now()
} }
} }
@ -90,9 +90,9 @@ class FlowSleepTest {
@Suspendable @Suspendable
override fun call(): Triple<Instant, Instant, Instant> { override fun call(): Triple<Instant, Instant, Instant> {
val start = Instant.now() val start = Instant.now()
sleep(5.seconds) sleep(6.seconds)
val middle = Instant.now() val middle = Instant.now()
sleep(10.seconds) sleep(11.seconds)
return Triple(start, middle, Instant.now()) return Triple(start, middle, Instant.now())
} }
} }
@ -143,4 +143,4 @@ class FlowSleepTest {
session.send("I got you bro") session.send("I got you bro")
} }
} }
} }

View File

@ -58,7 +58,7 @@ class ReceiveMultipleFlowTests : WithMockNet {
assertEquals(message, receivedMessage) assertEquals(message, receivedMessage)
session.send(answer) session.send(answer)
} }
} as FlowLogic<Unit> }
} }
assertThat( assertThat(
@ -139,4 +139,4 @@ private inline fun <reified T> TestStartedNode.registerAnswer(kClass: KClass<out
} }
} }
} }
} }

View File

@ -13,6 +13,7 @@ import net.corda.core.flows.ReceiveFinalityFlow
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.FlowStateMachineHandle import net.corda.core.internal.FlowStateMachineHandle
import net.corda.core.internal.getRequiredTransaction
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.FlowHandle import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
@ -26,9 +27,7 @@ interface WithFinality : WithMockNet {
return startFlowAndRunNetwork(FinalityInvoker(stx, recipients.toSet(), emptySet())) return startFlowAndRunNetwork(FinalityInvoker(stx, recipients.toSet(), emptySet()))
} }
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction { fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction = services.getRequiredTransaction(stx.id)
return services.validatedTransactions.getTransaction(stx.id)!!
}
fun CordaRPCOps.finalise(stx: SignedTransaction, vararg recipients: Party): FlowHandle<SignedTransaction> { fun CordaRPCOps.finalise(stx: SignedTransaction, vararg recipients: Party): FlowHandle<SignedTransaction> {
return startFlow(WithFinality::FinalityInvoker, stx, recipients.toSet(), emptySet()).andRunNetwork() return startFlow(WithFinality::FinalityInvoker, stx, recipients.toSet(), emptySet()).andRunNetwork()

View File

@ -226,4 +226,4 @@ class NetworkParametersResolutionTest {
}.withMessageContaining("The network parameters epoch (${defaultParams.epoch}) of this transaction " + }.withMessageContaining("The network parameters epoch (${defaultParams.epoch}) of this transaction " +
"is older than the epoch (${params2.epoch}) of input state: ${stx2.inputs.first()}") "is older than the epoch (${params2.epoch}) of input state: ${stx2.inputs.first()}")
} }
} }

View File

@ -258,7 +258,7 @@ class ResolveTransactionsFlowTest {
// Used for checking larger chains resolve correctly. Note that this takes a long time to run, and so is not suitable for a CI gate. // Used for checking larger chains resolve correctly. Note that this takes a long time to run, and so is not suitable for a CI gate.
@Test(timeout=300_000) @Test(timeout=300_000)
@Ignore @Ignore
fun `Can resolve large chain of transactions`() { fun `Can resolve large chain of transactions`() {
val txToResolve = makeLargeTransactionChain(2500) val txToResolve = makeLargeTransactionChain(2500)
val p = TestFlow(txToResolve, megaCorp) val p = TestFlow(txToResolve, megaCorp)

View File

@ -0,0 +1,135 @@
package net.corda.coretests.internal.verification
import net.corda.core.internal.verification.AttachmentFixups
import net.corda.core.node.services.AttachmentId
import net.corda.node.internal.cordapp.JarScanningCordappLoader
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.nio.file.Files
import java.nio.file.Path
import java.util.jar.JarOutputStream
import java.util.zip.Deflater
import java.util.zip.ZipEntry
import kotlin.io.path.outputStream
import kotlin.test.assertFailsWith
class AttachmentFixupsTest {
companion object {
@JvmField
val ID1 = AttachmentId.randomSHA256()
@JvmField
val ID2 = AttachmentId.randomSHA256()
@JvmField
val ID3 = AttachmentId.randomSHA256()
@JvmField
val ID4 = AttachmentId.randomSHA256()
}
@Test(timeout=300_000)
fun `test fixup rule that adds attachment`() {
val fixupJar = Files.createTempFile("fixup", ".jar")
.writeFixupRules("$ID1 => $ID2, $ID3")
val fixedIDs = with(newFixupService(fixupJar)) {
fixupAttachmentIds(listOf(ID1))
}
assertThat(fixedIDs).containsExactly(ID2, ID3)
}
@Test(timeout=300_000)
fun `test fixup rule that deletes attachment`() {
val fixupJar = Files.createTempFile("fixup", ".jar")
.writeFixupRules("$ID1 =>")
val fixedIDs = with(newFixupService(fixupJar)) {
fixupAttachmentIds(listOf(ID1))
}
assertThat(fixedIDs).isEmpty()
}
@Test(timeout=300_000)
fun `test fixup rule with blank LHS`() {
val fixupJar = Files.createTempFile("fixup", ".jar")
.writeFixupRules(" => $ID2")
val ex = assertFailsWith<IllegalArgumentException> {
newFixupService(fixupJar)
}
assertThat(ex).hasMessageContaining(
"Forbidden empty list of source attachment IDs in '$fixupJar'"
)
}
@Test(timeout=300_000)
fun `test fixup rule without arrows`() {
val rule = " $ID1 "
val fixupJar = Files.createTempFile("fixup", ".jar")
.writeFixupRules(rule)
val ex = assertFailsWith<IllegalArgumentException> {
newFixupService(fixupJar)
}
assertThat(ex).hasMessageContaining(
"Invalid fix-up line '${rule.trim()}' in '$fixupJar'"
)
}
@Test(timeout=300_000)
fun `test fixup rule with too many arrows`() {
val rule = " $ID1 => $ID2 => $ID3 "
val fixupJar = Files.createTempFile("fixup", ".jar")
.writeFixupRules(rule)
val ex = assertFailsWith<IllegalArgumentException> {
newFixupService(fixupJar)
}
assertThat(ex).hasMessageContaining(
"Invalid fix-up line '${rule.trim()}' in '$fixupJar'"
)
}
@Test(timeout=300_000)
fun `test fixup file containing multiple rules and comments`() {
val fixupJar = Files.createTempFile("fixup", ".jar").writeFixupRules(
"# Whole line comment",
"\t$ID1,$ID2 => $ID2,, $ID3 # EOl comment",
" # Empty line with comment",
"",
"$ID3 => $ID4"
)
val fixedIDs = with(newFixupService(fixupJar)) {
fixupAttachmentIds(listOf(ID2, ID1))
}
assertThat(fixedIDs).containsExactlyInAnyOrder(ID2, ID4)
}
private fun Path.writeFixupRules(vararg lines: String): Path {
JarOutputStream(outputStream()).use { jar ->
jar.setMethod(ZipEntry.DEFLATED)
jar.setLevel(Deflater.NO_COMPRESSION)
jar.putNextEntry(directoryEntry("META-INF"))
jar.putNextEntry(fileEntry("META-INF/Corda-Fixups"))
for (line in lines) {
jar.write(line.toByteArray())
jar.write('\r'.code)
jar.write('\n'.code)
}
}
return this
}
private fun directoryEntry(internalName: String): ZipEntry {
return ZipEntry("$internalName/").apply {
method = ZipEntry.STORED
compressedSize = 0
size = 0
crc = 0
}
}
private fun fileEntry(internalName: String): ZipEntry {
return ZipEntry(internalName).apply {
method = ZipEntry.DEFLATED
}
}
private fun newFixupService(vararg paths: Path): AttachmentFixups {
val loader = JarScanningCordappLoader(paths.toSet())
return AttachmentFixups().apply { load(loader.appClassLoader) }
}
}

View File

@ -1,7 +1,7 @@
package net.corda.coretests.node package net.corda.coretests.node
import com.nhaarman.mockito_kotlin.doReturn import org.mockito.kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever import org.mockito.kotlin.whenever
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.internal.getPackageOwnerOf import net.corda.core.internal.getPackageOwnerOf
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters

View File

@ -15,7 +15,7 @@ class VaultUpdateTests {
private companion object { private companion object {
const val DUMMY_PROGRAM_ID = "net.corda.coretests.node.VaultUpdateTests\$DummyContract" const val DUMMY_PROGRAM_ID = "net.corda.coretests.node.VaultUpdateTests\$DummyContract"
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party
val emptyUpdate = Vault.Update(emptySet(), emptySet(), type = Vault.UpdateType.GENERAL, references = emptySet()) val emptyUpdate = Vault.Update(emptySet<StateAndRef<*>>(), emptySet(), type = Vault.UpdateType.GENERAL, references = emptySet())
} }
object DummyContract : Contract { object DummyContract : Contract {

View File

@ -116,6 +116,7 @@ class AttachmentSerializationTest {
private class CustomAttachment(override val id: SecureHash, internal val customContent: String) : Attachment { private class CustomAttachment(override val id: SecureHash, internal val customContent: String) : Attachment {
override fun open() = throw UnsupportedOperationException("Not implemented.") override fun open() = throw UnsupportedOperationException("Not implemented.")
override val signerKeys get() = throw UnsupportedOperationException() override val signerKeys get() = throw UnsupportedOperationException()
@Suppress("OVERRIDE_DEPRECATION")
override val signers: List<Party> get() = throw UnsupportedOperationException() override val signers: List<Party> get() = throw UnsupportedOperationException()
override val size get() = throw UnsupportedOperationException() override val size get() = throw UnsupportedOperationException()
} }

View File

@ -1,6 +1,6 @@
package net.corda.coretests.serialization package net.corda.coretests.serialization
import com.nhaarman.mockito_kotlin.mock import org.mockito.kotlin.mock
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureMetadata import net.corda.core.crypto.SignatureMetadata

View File

@ -11,13 +11,13 @@ import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.isolated.contracts.DummyContractBackdoor import net.corda.isolated.contracts.DummyContractBackdoor
import net.corda.node.services.attachments.NodeAttachmentTrustCalculator import net.corda.node.services.attachments.NodeAttachmentTrustCalculator
import net.corda.node.services.persistence.toInternal
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.TestingNamedCacheFactory import net.corda.testing.internal.TestingNamedCacheFactory
import net.corda.testing.internal.fakeAttachment import net.corda.testing.internal.fakeAttachment
import net.corda.testing.internal.services.InternalMockAttachmentStorage
import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -30,7 +30,7 @@ import kotlin.test.assertFailsWith
class AttachmentsClassLoaderSerializationTests { class AttachmentsClassLoaderSerializationTests {
companion object { companion object {
val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderSerializationTests::class.java.getResource("/isolated.jar") val ISOLATED_CONTRACTS_JAR_PATH: URL = AttachmentsClassLoaderSerializationTests::class.java.getResource("/isolated.jar")!!
private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.isolated.contracts.AnotherDummyContract" private const val ISOLATED_CONTRACT_CLASS_NAME = "net.corda.isolated.contracts.AnotherDummyContract"
} }
@ -38,20 +38,19 @@ class AttachmentsClassLoaderSerializationTests {
@JvmField @JvmField
val testSerialization = SerializationEnvironmentRule() val testSerialization = SerializationEnvironmentRule()
private val storage = InternalMockAttachmentStorage(MockAttachmentStorage()) private val storage = MockAttachmentStorage().toInternal()
private val attachmentTrustCalculator = NodeAttachmentTrustCalculator(storage, TestingNamedCacheFactory()) private val attachmentTrustCalculator = NodeAttachmentTrustCalculator(storage, TestingNamedCacheFactory())
@Test(timeout=300_000) @Test(timeout=300_000)
fun `Can serialize and deserialize with an attachment classloader`() { fun `Can serialize and deserialize with an attachment classloader`() {
val dummyNotary = TestIdentity(DUMMY_NOTARY_NAME, 20).party
val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party val megaCorp = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party
val isolatedId = storage.importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar") val isolatedId = storage.importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar")
val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream(), "app", "file1.jar") val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream(), "app", "file1.jar")
val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream(), "app", "file2.jar") val att2 = storage.importAttachment(fakeAttachment("file2.txt", "some other data").inputStream(), "app", "file2.jar")
val serialisedState = AttachmentsClassLoaderBuilder.withAttachmentsClassloaderContext( val serialisedState = AttachmentsClassLoaderBuilder.withAttachmentsClassLoaderContext(
arrayOf(isolatedId, att1, att2).map { storage.openAttachment(it)!! }, arrayOf(isolatedId, att1, att2).map { storage.openAttachment(it)!! },
testNetworkParameters(), testNetworkParameters(),
SecureHash.zeroHash, SecureHash.zeroHash,
@ -64,7 +63,7 @@ class AttachmentsClassLoaderSerializationTests {
val txt = IOUtils.toString(classLoader.getResourceAsStream("file1.txt"), Charsets.UTF_8.name()) val txt = IOUtils.toString(classLoader.getResourceAsStream("file1.txt"), Charsets.UTF_8.name())
assertEquals("some data", txt) assertEquals("some data", txt)
val state = (contract as DummyContractBackdoor).generateInitial(MEGA_CORP.ref(1), 1, DUMMY_NOTARY).outputStates().first() val state = (contract as DummyContractBackdoor).generateInitial(megaCorp.ref(1), 1, dummyNotary).outputStates().first()
val serialisedState = state.serialize() val serialisedState = state.serialize()
val state1 = serialisedState.deserialize() val state1 = serialisedState.deserialize()

Some files were not shown because too many files have changed in this diff Show More