Merge branch 'release/os/4.12' into merge-release/os/4.11-release/os/4.12-2024-12-06-478

This commit is contained in:
Chris Cochrane 2024-12-06 09:55:00 +00:00 committed by GitHub
commit d1110e726e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
776 changed files with 15175 additions and 12469 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
* 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
* 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

View File

@ -45,6 +45,8 @@ pipeline {
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
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 {

View File

@ -15,48 +15,32 @@ pipeline {
* List environment variables in alphabetical order
*/
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')
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"
C4_OS_SNYK_ORG_ID = credentials('c4-os-snyk-org-id')
SNYK_API_TOKEN = credentials('c4-os-snyk-api-token-secret')
JAVA_HOME = "/usr/lib/jvm/java-17-amazon-corretto"
}
stages {
stage('Detekt check') {
steps {
authenticateGradleWrapper()
sh "./gradlew --no-daemon --parallel --build-cache clean detekt"
sh "./gradlew --no-daemon clean detekt"
}
}
stage('Compilation warnings check') {
steps {
sh "./gradlew --no-daemon --parallel --build-cache -Pcompilation.warningsAsErrors=true compileAll"
sh "./gradlew --no-daemon -Pcompilation.warningsAsErrors=true compileAll"
}
}
stage('Snyk Delta') {
agent {
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"
}
agent { label 'standard' }
steps {
authenticateGradleWrapper()
sh 'mkdir -p ${GRADLE_USER_HOME}'
authenticateGradleWrapper()
snykDeltaScan(env.SNYK_API_TOKEN, env.C4_OS_SNYK_ORG_ID)
}
@ -64,21 +48,19 @@ pipeline {
stage('No API change check') {
steps {
sh "./gradlew --no-daemon --parallel --build-cache generateApi"
sh "./gradlew --no-daemon generateApi"
sh ".ci/check-api-changes.sh"
}
}
stage('Deploy Nodes') {
steps {
sh "./gradlew --no-daemon --build-cache jar deployNodes"
sh "./gradlew --no-daemon jar deployNodes"
}
}
}
post {
always {
findBuildScans()
}
cleanup {
deleteDir() /* clean up our workspace */
}

View File

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

View File

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

View File

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

View File

@ -70,6 +70,8 @@ pipeline {
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
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 {
@ -327,7 +329,7 @@ pipeline {
'./gradlew',
COMMON_GRADLE_PARAMS,
'docker:pushDockerImage',
'-Pdocker.image.repository=corda/community',
'-Pdocker.image.repository=corda/open-source',
'--image OFFICIAL'
].join(' ')
}

View File

@ -3,9 +3,9 @@
# 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 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 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 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/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! :)

View File

@ -9,6 +9,6 @@ jobs:
steps:
- uses: morrisoncole/pr-lint-action@v1.7.1
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%`"
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@ -2,4 +2,4 @@
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_GRADLE_SCAN_KEY = credentials('gradle-build-scans-key')
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 {
@ -119,6 +121,24 @@ pipeline {
].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') {

View File

@ -1,5 +1,5 @@
<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>
<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
1. Read the [Getting Started](https://docs.corda.net/getting-set-up.html) documentation
2. Run the [Example CorDapp](https://docs.corda.net/tutorial-cordapp.html)
3. Read about Corda's [Key Concepts](https://docs.corda.net/key-concepts.html)
4. Follow the [Hello, World! tutorial](https://docs.corda.net/hello-world-introduction.html)
1. Read the [Getting Started](https://docs.r3.com/getting-set-up.html) documentation
2. Run the [Example CorDapp](https://docs.r3.com/tutorial-cordapp.html)
3. Read about Corda's [Key Concepts](https://docs.r3.com/key-concepts.html)
4. Follow the [Hello, World! tutorial](https://docs.r3.com/hello-world-introduction.html)
## Useful links
* [Project Website](https://corda.net)
* [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)
* [Slack Channel](https://slack.corda.net/)
* [Twitter](https://twitter.com/Cordablockchain)
* [Meetups](https://www.meetup.com/pro/corda/)
* [Training Courses](https://www.corda.net/corda-training/)
* [Twitter](https://twitter.com/inside_r3)
* [Training Courses](https://r3certification.com/)
## Contributing
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

View File

@ -1,8 +1,11 @@
import com.r3.testing.DistributeTestsBy
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_1_8
import static org.gradle.api.JavaVersion.VERSION_17
import static org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
import static org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
buildscript {
// 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_platform_version = constants.getProperty("platformVersion")
ext.corda_shell_version = constants.getProperty("cordaShellVersion")
ext.gradle_plugins_version = constants.getProperty("gradlePluginsVersion")
// Dependency versions. Can run 'gradle dependencyUpdates' to find new versions of things.
//
// 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.quasar_group = 'co.paralleluniverse'
// Set version of Quasar according to version of Java used:
if (JavaVersion.current().isJava8()) {
ext.quasar_version = constants.getProperty("quasarVersion")
ext.quasar_classifier = constants.getProperty("quasarClassifier")
ext.jdkClassifier = constants.getProperty("jdkClassifier")
} else {
ext.quasar_version = constants.getProperty("quasarVersion11")
ext.quasar_classifier = constants.getProperty("quasarClassifier11")
ext.jdkClassifier = constants.getProperty("jdkClassifier11")
}
ext.cordaScanApiClassifier = jdkClassifier
ext.quasar_version = constants.getProperty("quasarVersion")
ext.quasar_classifier = constants.getProperty("quasarClassifier")
ext.quasar_exclusions = [
'co.paralleluniverse**',
'groovy**',
@ -49,14 +44,13 @@ buildscript {
'org.junit**',
'org.slf4j**',
'worker.org.gradle.**',
'com.nhaarman.mockito_kotlin**',
'org.mockito.kotlin**',
'org.assertj**',
'org.hamcrest**',
'org.mockito**',
'org.opentest4j**'
]
ext.capsule_version = constants.getProperty("capsuleVersion")
ext.open_telemetry_version = constants.getProperty("openTelemetryVersion")
ext.open_telemetry_sem_conv_version = constants.getProperty("openTelemetrySemConvVersion")
@ -96,7 +90,6 @@ buildscript {
ext.h2_version = constants.getProperty("h2Version")
ext.rxjava_version = constants.getProperty("rxjavaVersion")
ext.dokka_version = constants.getProperty("dokkaVersion")
ext.eddsa_version = constants.getProperty("eddsaVersion")
ext.dependency_checker_version = constants.getProperty("dependencyCheckerVersion")
ext.commons_collections_version = constants.getProperty("commonsCollectionsVersion")
ext.beanutils_version = constants.getProperty("beanutilsVersion")
@ -117,7 +110,6 @@ buildscript {
ext.class_graph_version = constants.getProperty('classgraphVersion')
ext.jcabi_manifests_version = constants.getProperty("jcabiManifestsVersion")
ext.picocli_version = constants.getProperty("picocliVersion")
ext.commons_lang_version = constants.getProperty("commonsLangVersion")
ext.commons_io_version = constants.getProperty("commonsIoVersion")
ext.controlsfx_version = constants.getProperty("controlsfxVersion")
ext.detekt_version = constants.getProperty('detektVersion')
@ -125,20 +117,10 @@ buildscript {
ext.commons_configuration2_version = constants.getProperty("commonsConfiguration2Version")
ext.commons_text_version = constants.getProperty("commonsTextVersion")
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")
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 = {
try {
"git rev-parse HEAD".execute().text.trim()
@ -172,6 +154,7 @@ buildscript {
content {
includeGroupByRegex 'net\\.corda(\\..*)?'
includeGroupByRegex 'com\\.r3(\\..*)?'
includeGroup 'co.paralleluniverse'
}
}
maven {
@ -181,7 +164,6 @@ buildscript {
includeGroupByRegex 'com\\.r3(\\..*)?'
}
}
gradlePluginPortal()
mavenCentral()
maven {
url "${publicArtifactURL}/jcenter-backup"
@ -189,28 +171,21 @@ buildscript {
}
}
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:cordformation:$gradle_plugins_version"
classpath "net.corda.plugins:cordapp:$gradle_plugins_version"
classpath "net.corda.plugins:api-scanner:$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 "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_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.jetbrains.dokka:dokka-base:$dokka_version"
classpath "org.owasp:dependency-check-gradle:$dependency_checker_version"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$artifactory_plugin_version"
// Capsule gradle plugin forked and maintained locally to support Gradle 5.x
// See https://github.com/corda/gradle-capsule-plugin
classpath "us.kirchmeier:gradle-capsule-plugin:1.0.4_r3"
classpath "us.kirchmeier:gradle-capsule-plugin:1.0.5_r3"
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 "com.gradle:gradle-enterprise-gradle-plugin:$gradleEnterprisePlugin"
classpath "com.gradle:common-custom-user-data-gradle-plugin:$customUserDataGradlePlugin"
}
configurations.classpath {
@ -220,34 +195,20 @@ buildscript {
}
plugins {
// Add the shadow plugin to the plugins classpath for the entire project.
id 'com.github.johnrengelman.shadow' version '2.0.4' apply false
id 'org.jetbrains.kotlin.jvm' 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 'corda.root-publish'
id "org.jetbrains.dokka" version "1.8.20"
}
apply plugin: 'project-report'
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.gradle.build-scan"
apply plugin: "com.gradle.common-custom-user-data-gradle-plugin"
buildScan {
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
// 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
if (project.hasProperty("versionFromGit")){
ext.versionSuffix = "${grgit.head().dateTime.format("yyyyMMdd_HHmmss")}-${grgit.head().abbreviatedId}"
@ -260,26 +221,17 @@ if (ext.versionSuffix != ""){
ext.corda_release_version = "${ext.baseVersion}".toString()
}
// We need the following three lines even though they're inside an allprojects {} block below because otherwise
// 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("JDK: {}", System.getProperty("java.home"))
logger.lifecycle("Quasar version: {}", quasar_version)
logger.lifecycle("Quasar classifier: {}", quasar_classifier.toString())
logger.lifecycle("Building Corda version: {}", corda_release_version)
logger.lifecycle("User home: {}", System.getProperty('user.home'))
allprojects {
apply plugin: 'kotlin'
apply plugin: 'java'
apply plugin: 'kotlin-allopen'
apply plugin: 'jacoco'
apply plugin: 'org.owasp.dependencycheck'
apply plugin: 'kotlin-allopen'
apply plugin: 'org.sonarqube'
allOpen {
@ -290,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 {
suppressionFile = '.ci/dependency-checker/suppressedLibraries.xml'
cveValidForHours = 1
@ -317,12 +256,17 @@ allprojects {
nugetconfEnabled = false
}
}
sourceCompatibility = VERSION_1_8
targetCompatibility = JavaVersion.current().isJava8() ? VERSION_1_8 : VERSION_11
sourceCompatibility = VERSION_17
targetCompatibility = VERSION_17
jacoco {
// JDK11 official support (https://github.com/jacoco/jacoco/releases/tag/v0.8.3)
toolVersion = "0.8.3"
toolVersion = "0.8.7"
}
java {
withSourcesJar()
withJavadocJar()
}
tasks.withType(JavaCompile).configureEach {
@ -337,13 +281,13 @@ allprojects {
options.encoding = 'UTF-8'
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
languageVersion = "1.2"
apiVersion = "1.2"
jvmTarget = VERSION_1_8
tasks.withType(KotlinCompile).configureEach {
compilerOptions {
languageVersion = KOTLIN_1_9
apiVersion = KOTLIN_1_9
jvmTarget = JVM_17
javaParameters = true // Useful for reflection.
freeCompilerArgs = ['-Xjvm-default=compatibility']
freeCompilerArgs = ['-Xjvm-default=all-compatibility']
allWarningsAsErrors = warnings_as_errors
}
}
@ -363,15 +307,14 @@ allprojects {
attributes('Corda-Docs-Link': corda_docs_link)
}
}
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
ignoreFailures = project.hasProperty('tests.ignoreFailures') ? project.property('tests.ignoreFailures').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"
if (project.path.startsWith(':experimental') && System.getProperty("experimental.test.enable") == null) {
@ -381,25 +324,18 @@ allprojects {
// 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
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")) {
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) {
jar {
// JDK11 built and published artifacts to include classifier
archiveClassifier = jdkClassifier
}
// Prevent the project from creating temporary files outside of the build directory.
systemProperty 'java.io.tmpdir', buildDir.absolutePath
systemProperty 'java.security.egd', 'file:/dev/./urandom'
}
group 'net.corda'
@ -440,6 +376,16 @@ allprojects {
includeGroup 'com.github.bft-smart'
includeGroup 'com.github.detro'
}
metadataSources {
mavenPom()
artifact()
}
}
maven {
url "${publicArtifactURL}/corda-dependencies-dev"
content {
includeGroup 'co.paralleluniverse'
}
}
maven {
url "${publicArtifactURL}/corda-dev"
@ -469,12 +415,12 @@ allprojects {
}
configurations {
all {
configureEach {
resolutionStrategy {
// Force dependencies to use the same version of Kotlin as Corda.
force "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
if (pluginManager.hasPlugin("org.jetbrains.kotlin.jvm")) {
// Force dependencies to use the same version of Kotlin as Corda.
force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
// Force dependencies to use the same version of Guava as Corda.
force "com.google.guava:guava:$guava_version"
@ -501,6 +447,9 @@ allprojects {
if (details.requested.group == 'org.yaml' && details.requested.name == 'snakeyaml') {
details.useVersion snake_yaml_version
}
if (details.requested.group == 'commons-io' && details.requested.name == "commons-io") {
details.useVersion commons_io_version
}
}
dependencySubstitution {
@ -509,7 +458,7 @@ allprojects {
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)
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.
// Exclude it to force us to use the individual Netty modules instead.
@ -520,27 +469,32 @@ allprojects {
// Effectively delete this unused and unwanted transitive dependency of Artemis.
substitute module('org.jgroups:jgroups') with module("org.apache.activemq:artemis-commons:$artemis_version")
}
}
}
// 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-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 use of LTS version of BC everywhere
substitute module('org.bouncycastle:bcutil-jdk18on') with module("org.bouncycastle:bcutil-lts8on:$bouncycastle_version")
substitute module('org.bouncycastle:bcprov-jdk18on') with module("org.bouncycastle:bcprov-lts8on:$bouncycastle_version")
substitute module('org.bouncycastle:bcpkix-jdk18on') with module("org.bouncycastle:bcpkix-lts8on:$bouncycastle_version")
}
// FORCE Gradle to use latest SNAPSHOT dependencies.
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")
}
}
}
}
}
}
@ -555,37 +509,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 {
detekt
}
// Required for building out the fat JAR.
dependencies {
compile project(':node')
compile "com.google.guava:guava:$guava_version"
implementation project(':node')
implementation "com.google.guava:guava:$guava_version"
// Set to corda compile to ensure it exists now deploy nodes no longer relies on build
compile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
compile project(path: ":testing:testserver:testcapsule:", configuration: 'runtimeArtifacts')
// Set to corda implementation to ensure it exists now deploy nodes no longer relies on build
implementation project(path: ":node:capsule", configuration: 'runtimeArtifacts')
implementation project(path: ":testing:testserver:testcapsule:", configuration: 'runtimeArtifacts')
// For the buildCordappDependenciesJar task
runtime project(':client:jfx')
runtime project(':client:mock')
runtime project(':client:rpc')
runtime project(':core')
runtime project(':confidential-identities')
runtime project(':finance:workflows')
runtime project(':finance:contracts')
runtime project(':testing:testserver')
testCompile project(':test-utils')
runtimeOnly project(':client:jfx')
runtimeOnly project(':client:mock')
runtimeOnly project(':client:rpc')
runtimeOnly project(':core')
runtimeOnly project(':confidential-identities')
runtimeOnly project(':finance:workflows')
runtimeOnly project(':finance:contracts')
runtimeOnly project(':testing:testserver')
testImplementation project(':test-utils')
detekt 'io.gitlab.arturbosch.detekt:detekt-cli:1.0.1'
}
@ -594,16 +540,16 @@ jar {
enabled = false
}
task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) {
tasks.register('jacocoRootReport', JacocoReport) {
dependsOn = subprojects.test
additionalSourceDirs = files(subprojects.sourceSets.main.allSource.srcDirs)
sourceDirectories = files(subprojects.sourceSets.main.allSource.srcDirs)
classDirectories = files(subprojects.sourceSets.main.output)
executionData = files(subprojects.jacocoTestReport.executionData)
// additionalSourceDirs = files(subprojects.sourceSets.main.allSource.srcDirs)
// sourceDirectories = files(subprojects.sourceSets.main.allSource.srcDirs)
// classDirectories = files(subprojects.sourceSets.main.output)
// executionData = files(subprojects.jacocoTestReport.executionData)
reports {
html.enabled = true
xml.enabled = true
csv.enabled = false
html.required = true
xml.required = true
csv.required = false
}
onlyIf = {
true
@ -623,13 +569,13 @@ tasks.register('detekt', JavaExec) {
def plugins = detektPluginsJar.outputs.files.singleFile
def params = ['-i', input, '-c', config, '-b', baseline, '--plugins', plugins]
inputs.files(detektPluginsJar, config, baseline)
main = "io.gitlab.arturbosch.detekt.cli.Main"
mainClass = "io.gitlab.arturbosch.detekt.cli.Main"
classpath = configurations.detekt
args(params)
}
tasks.register('detektBaseline', JavaExec) {
main = "io.gitlab.arturbosch.detekt.cli.Main"
mainClass = "io.gitlab.arturbosch.detekt.cli.Main"
classpath = configurations.detekt
def input = "$projectDir"
def config = "$projectDir/detekt-config.yml, $projectDir/detekt-baseline-config.yml"
@ -639,103 +585,28 @@ tasks.register('detektBaseline', JavaExec) {
}
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")
// Include the results from the `test` task in all subprojects
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.
// Resulting ZIP can be found in "build/distributions"
task buildCordappDependenciesZip(type: Zip) {
tasks.register('buildCordappDependenciesZip', Zip) {
baseName 'corda-deps'
from configurations.runtime
from configurations.compile
from configurations.testCompile
from configurations.runtimeOnly
from configurations.implementation
from configurations.testImplementation
from buildscript.configurations.classpath
from 'node/capsule/NOTICE' // CDDL notice
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
artifactory {
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) {
tasks.register('generateApi', GenerateApi) {
baseName = "api-corda"
}
@ -771,7 +642,7 @@ if (file('corda-docs-only-build').exists() || (System.getenv('CORDA_DOCS_ONLY_BU
}
wrapper {
gradleVersion = '5.6.4'
gradleVersion = '7.6.4'
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()
file("$rootDir/../constants.properties").withInputStream { constants.load(it) }
def internalPublishVersion = constants.getProperty('internalPublishVersion')
def artifactoryContextUrl = constants.getProperty('artifactoryContextUrl')
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 {
compile group: 'com.github.docker-java', name: 'docker-java', version: constants.dockerJavaVersion
compile group: 'com.github.docker-java', name: 'docker-java-transport-httpclient5', version: constants.dockerJavaVersion
implementation "com.github.docker-java:docker-java:$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,37 @@
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.api-scanner'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'corda.common-publishing'
description 'Corda Jackson module'
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.
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"
}
// 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.
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
compile "com.google.guava:guava:$guava_version"
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jackson_version"
implementation "com.google.guava:guava:$guava_version"
testCompile project(':test-utils')
testCompile project(path: ':core', configuration: 'testArtifacts')
// Bouncy castle support needed for X509 certificate manipulation
implementation "org.bouncycastle:bcprov-lts8on:${bouncycastle_version}"
implementation "org.bouncycastle:bcpkix-lts8on:${bouncycastle_version}"
implementation "org.bouncycastle:bcutil-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 "junit:junit:$junit_version"
@ -28,7 +40,7 @@ dependencies {
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_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 +51,11 @@ jar {
}
}
publish {
name jar.baseName
}
publishing {
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.node.ObjectNode
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.ToStringSerialize
import net.corda.client.jackson.internal.jsonObject
@ -197,7 +197,7 @@ object JacksonSupport {
addSerializer(Date::class.java, DateSerializer)
})
registerModule(CordaModule())
registerModule(KotlinModule().apply {
registerModule(kotlinModule().apply {
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> {
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> {
val kf: KFunction<*>? = ctor.kotlinFunction

View File

@ -95,7 +95,8 @@ import java.math.BigDecimal
import java.security.PublicKey
import java.security.cert.CertPath
import java.time.Instant
import java.util.*
import java.util.Currency
import java.util.UUID
class CordaModule : SimpleModule("corda-core") {
override fun setupModule(context: SetupContext) {
@ -256,6 +257,7 @@ private data class StxJson(
private interface WireTransactionMixin
private class WireTransactionSerializer : JsonSerializer<WireTransaction>() {
@Suppress("INVISIBLE_MEMBER")
override fun serialize(value: WireTransaction, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeObject(WireTransactionJson(
value.digestService,
@ -265,7 +267,7 @@ private class WireTransactionSerializer : JsonSerializer<WireTransaction>() {
value.outputs,
value.commands,
value.timeWindow,
value.attachments,
value.legacyAttachments.map { "$it-legacy" } + value.nonLegacyAttachments.map { it.toString() },
value.references,
value.privacySalt,
value.networkParametersHash
@ -276,15 +278,18 @@ private class WireTransactionSerializer : JsonSerializer<WireTransaction>() {
private class WireTransactionDeserializer : JsonDeserializer<WireTransaction>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): WireTransaction {
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(
wrapper.inputs,
wrapper.outputs,
wrapper.commands,
wrapper.attachments,
newerAttachments.map(SecureHash::parse),
wrapper.notary,
wrapper.timeWindow,
wrapper.references,
wrapper.networkParametersHash
wrapper.networkParametersHash,
legacyAttachments.map { SecureHash.parse(it.removeSuffix("-legacy")) }
)
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 commands: List<Command<*>>,
val timeWindow: TimeWindow?,
val attachments: List<SecureHash>,
val attachments: List<String>,
val references: List<StateRef>,
val privacySalt: PrivacySalt,
val networkParametersHash: SecureHash?)
val networkParametersHash: SecureHash?
)
private interface TransactionStateMixin {
@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.dataformat.yaml.YAMLFactory
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.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.crypto.*
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.identity.*
import net.corda.core.internal.AbstractAttachment
import net.corda.core.crypto.SecureHash
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.node.NodeInfo
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.SerializedBytes
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.TransactionBuilder
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.nodeapi.internal.crypto.x509Certificates
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.contracts.DummyContract
import net.corda.testing.core.*
import net.corda.coretesting.internal.createNodeInfoAndSigned
import net.corda.coretesting.internal.rigorousMock
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.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.assertThatThrownBy
import org.junit.Before
@ -54,15 +76,22 @@ import org.junit.jupiter.api.TestFactory
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
import java.math.BigInteger
import java.nio.charset.StandardCharsets.UTF_8
import java.security.PublicKey
import java.security.cert.CertPath
import java.security.cert.X509Certificate
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 kotlin.collections.ArrayList
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.component3
import kotlin.collections.component4
@RunWith(Parameterized::class)
class JacksonSupportTest(@Suppress("unused") private val name: String, factory: JsonFactory) {
@ -90,23 +119,12 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
@Before
fun setup() {
val unsignedAttachment = object : AbstractAttachment({ byteArrayOf() }, "test") {
override val id: SecureHash get() = throw UnsupportedOperationException()
}
val attachments = rigorousMock<AttachmentStorage>().also {
doReturn(unsignedAttachment).whenever(it).openAttachment(any())
}
services = rigorousMock()
services = MockServices(
listOf("net.corda.testing.contracts"),
MINI_CORP,
testNetworkParameters(minimumPlatformVersion = 4)
)
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)
@ -263,17 +281,6 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory:
@Test(timeout=300_000)
fun `SignedTransaction (WireTransaction)`() {
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(
notary = DUMMY_NOTARY,

View File

@ -26,7 +26,7 @@ class StringToMethodCallParserTest {
"simple" to "simple",
"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",
"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"),
"overload a: A" to "A",
"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()
assertTrue(result is Pair<*,*>)
result as Pair<*,*>
assertEquals(101, result.first)

View File

@ -1,28 +1,27 @@
// JDK 11 JavaFX
plugins {
id 'org.openjfx.javafxplugin' version '0.0.7' apply false
id 'corda.common-publishing'
}
if (JavaVersion.current().isJava9Compatible()) {
apply plugin: 'org.openjfx.javafxplugin'
javafx {
version = "11.0.2"
modules = ['javafx.controls',
'javafx.fxml'
]
}
apply plugin: 'org.openjfx.javafxplugin'
javafx {
version = "11.0.2"
modules = [
'javafx.controls',
'javafx.fxml'
]
}
apply plugin: 'kotlin'
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory'
description 'Corda client JavaFX modules'
//noinspection GroovyAssignabilityCheck
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntime.extendsFrom testRuntimeOnly
}
sourceSets {
@ -39,23 +38,26 @@ sourceSets {
// build/reports/project/dependencies/index.html for green highlighted parts of the tree.
dependencies {
compile project(':core')
compile project(':finance:contracts')
compile project(':finance:workflows')
compile project(':client:rpc')
implementation project(':core')
implementation project(':finance:contracts')
implementation project(':finance:workflows')
implementation project(':client:rpc')
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "com.google.guava:guava:$guava_version"
implementation "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.
compile 'org.reactfx:reactfx:2.0-M5'
compile 'org.fxmisc.easybind:easybind:1.0.3'
implementation 'org.reactfx:reactfx:2.0-M5'
implementation 'org.fxmisc.easybind:easybind:1.0.3'
// 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
compile "org.apache.commons:commons-collections4:${commons_collections_version}"
compile "commons-beanutils:commons-beanutils:${beanutils_version}"
compile("org.apache.activemq:artemis-core-client:${artemis_version}") {
implementation "org.apache.commons:commons-collections4:${commons_collections_version}"
implementation "commons-beanutils:commons-beanutils:${beanutils_version}"
implementation("org.apache.activemq:artemis-core-client:${artemis_version}") {
exclude group: 'org.jgroups', module: 'jgroups'
}
@ -67,13 +69,14 @@ dependencies {
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_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
integrationTestCompile "junit:junit:$junit_version"
integrationTestCompile project(':node-driver')
integrationTestImplementation "junit:junit:$junit_version"
integrationTestImplementation project(':node-driver')
}
task integrationTest(type: Test) {
@ -88,6 +91,11 @@ jar {
}
}
publish {
name jar.baseName
publishing {
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)
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]
*/
object AmountBindings {
@Suppress("SpreadOperator")
fun <T : Any> sum(amounts: ObservableList<Amount<T>>, token: T): MonadicBinding<Amount<T>> = EasyBind.map(
Bindings.createLongBinding({
amounts.stream().collect(Collectors.summingLong {
require(it.token == token)
it.quantity
})
}, arrayOf(amounts))
}, *arrayOf(amounts))
) { sum -> Amount(sum.toLong(), token) }
fun exchange(
@ -35,6 +36,7 @@ object AmountBindings {
}
}
@Suppress("SpreadOperator")
fun sumAmountExchange(
amounts: ObservableList<Amount<Currency>>,
currency: ObservableValue<Currency>,
@ -45,7 +47,7 @@ object AmountBindings {
EasyBind.map(
Bindings.createLongBinding({
amounts.stream().collect(Collectors.summingLong { exchange(it) })
}, arrayOf(amounts))
}, *arrayOf(amounts))
) { 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> {
//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 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> {
return Bindings.createObjectBinding({
var current = initial
@ -135,7 +136,7 @@ fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B,
current = folderFunction(current, it)
}
current
}, arrayOf(this))
}, *arrayOf(this))
}
/**
@ -285,6 +286,7 @@ fun <A> ObservableList<A>.first(): ObservableValue<A?> {
return getValueAt(0)
}
@Suppress("SpreadOperator")
fun <A> ObservableList<A>.last(): ObservableValue<A?> {
return Bindings.createObjectBinding({
if (size > 0) {
@ -292,7 +294,7 @@ fun <A> ObservableList<A>.last(): ObservableValue<A?> {
} else {
null
}
}, arrayOf(this))
}, *arrayOf(this))
}
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] })
}
@Suppress("SpreadOperator")
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 provided default value if the list is empty.
*/
@Suppress("SpreadOperator")
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 ObservableValue(null) if the list is empty.
*/
@Suppress("SpreadOperator")
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.publish-utils'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'corda.common-publishing'
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.
dependencies {
compile project(":core")
compile project(':finance:workflows')
compile project(':finance:contracts')
implementation project(":core")
implementation project(':finance:workflows')
implementation project(':finance:contracts')
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "junit:junit:$junit_version"
@ -21,9 +20,9 @@ dependencies {
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
// 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 {
@ -39,6 +38,11 @@ jar {
}
}
publish {
name jar.baseName
publishing {
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.publish-utils'
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'
//noinspection GroovyAssignabilityCheck
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
smokeTestCompile.extendsFrom compile
smokeTestImplementation.extendsFrom compile
smokeTestRuntimeOnly.extendsFrom runtimeOnly
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
sourceSets {
integrationTest {
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 {
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'
}
from(project(':finance:workflows').tasks['jar']) {
@ -70,52 +112,12 @@ processSmokeTestResources {
}
}
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in
// 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) {
tasks.register('integrationTest', Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
task smokeTest(type: Test) {
tasks.register('smokeTest', Test) {
testClassesDirs = sourceSets.smokeTest.output.classesDirs
classpath = sourceSets.smokeTest.runtimeClasspath
}
@ -127,6 +129,11 @@ jar {
}
}
publish {
name jar.baseName
publishing {
publications {
maven(MavenPublication) {
artifactId jar.baseName
from components.java
}
}
}

View File

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

View File

@ -1,12 +1,12 @@
package net.corda.client.rpc
import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.atLeastOnce
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.never
import com.nhaarman.mockito_kotlin.times
import com.nhaarman.mockito_kotlin.verify
import com.nhaarman.mockito_kotlin.whenever
import org.mockito.kotlin.any
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import net.corda.client.rpc.internal.RPCClient
import net.corda.client.rpc.ext.RPCConnectionListener
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
import com.nhaarman.mockito_kotlin.mock
import org.mockito.kotlin.mock
import net.corda.client.rpc.RPCMultipleInterfacesTests.StringRPCOpsImpl.testPhrase
import net.corda.core.crypto.SecureHash
import net.corda.core.messaging.CordaRPCOps

View File

@ -544,7 +544,7 @@ class RPCStabilityTests {
}
@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`() {
rpcDriver {
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
val myQueue = "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.test.${random63BitValue()}"
val session = startArtemisSession(server.broker.hostAndPort!!)
session.createQueue(QueueConfiguration(myQueue)
session.createQueue(QueueConfiguration.of(myQueue)
.setRoutingType(ActiveMQDefaultConfiguration.getDefaultRoutingType())
.setAddress(myQueue)
.setTemporary(true)
@ -569,7 +569,7 @@ class RPCStabilityTests {
val message = session.createMessage(false)
val request = RPCApi.ClientToServer.RpcRequest(
clientAddress = SimpleString(myQueue),
clientAddress = SimpleString.of(myQueue),
methodName = SlowConsumerRPCOps::streamAtInterval.name,
serialisedArguments = listOf(100.millis, 1234).serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT),
replyId = Trace.InvocationId.newInstance(),
@ -593,7 +593,7 @@ class RPCStabilityTests {
// Construct an RPC client session manually
val myQueue = "${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.test.${random63BitValue()}"
val session = startArtemisSession(server.broker.hostAndPort!!)
session.createQueue(QueueConfiguration(myQueue)
session.createQueue(QueueConfiguration.of(myQueue)
.setRoutingType(ActiveMQDefaultConfiguration.getDefaultRoutingType())
.setAddress(myQueue)
.setTemporary(true)
@ -612,7 +612,7 @@ class RPCStabilityTests {
val message = session.createMessage(false)
val request = RPCApi.ClientToServer.RpcRequest(
clientAddress = SimpleString(myQueue),
clientAddress = SimpleString.of(myQueue),
methodName = DummyOps::protocolVersion.name,
serialisedArguments = emptyList<Any>().serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT),
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) }
clientAddresses.size == expected
}.get()
}
}

View File

@ -110,11 +110,11 @@ class CordaRPCClientReconnectionTest {
assertThatThrownBy {
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)
}
.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")
}
}
}
}

View File

@ -7,12 +7,12 @@ import com.github.benmanes.caffeine.cache.LoadingCache
import net.corda.core.internal.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)
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)
return caffeine.build<K, V>(loader)
}

View File

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

View File

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

View File

@ -1,6 +1,5 @@
package net.corda.java.rpc;
import net.corda.client.rpc.CordaRPCConnection;
import net.corda.core.contracts.Amount;
import net.corda.core.identity.CordaX500Name;
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.CashIssueFlow;
import net.corda.nodeapi.internal.config.User;
import net.corda.smoketesting.NodeConfig;
import net.corda.smoketesting.NodeParams;
import net.corda.smoketesting.NodeProcess;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.Currency;
import java.util.HashSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import static java.util.Collections.singletonList;
import static kotlin.test.AssertionsKt.assertEquals;
import static kotlin.test.AssertionsKt.fail;
import static net.corda.finance.workflows.GetBalances.getCashBalance;
import static net.corda.kotlin.rpc.StandaloneCordaRPClientTest.gatherCordapps;
public class StandaloneCordaRPCJavaClientTest {
private final User superUser = new User("superUser", "test", new HashSet<>(singletonList("ALL")));
public static void copyCordapps(NodeProcess.Factory factory, NodeConfig notaryConfig) {
Path cordappsDir = (factory.baseDirectory(notaryConfig).resolve(NodeProcess.CORDAPPS_DIR_NAME));
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 final AtomicInteger port = new AtomicInteger(15000);
private final NodeProcess.Factory factory = new NodeProcess.Factory();
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 CordaRPCConnection connection;
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
public void setUp() {
NodeProcess.Factory factory = new NodeProcess.Factory();
copyCordapps(factory, notaryConfig);
notary = factory.create(notaryConfig);
connection = notary.connect(superUser);
rpcProxy = connection.getProxy();
NodeProcess notary = factory.createNotaries(new NodeParams(
new CordaX500Name("Notary Service", "Zurich", "CH"),
port.getAndIncrement(),
port.getAndIncrement(),
port.getAndIncrement(),
singletonList(superUser),
gatherCordapps()
)).get(0);
rpcProxy = notary.connect(superUser).getProxy();
notaryNodeIdentity = rpcProxy.nodeInfo().getLegalIdentities().get(0);
}
@After
public void done() {
try {
connection.close();
} finally {
if (notary != null) {
notary.close();
}
}
factory.close();
}
@Test

View File

@ -27,6 +27,7 @@ import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds
import net.corda.finance.DOLLARS
import net.corda.finance.GBP
import net.corda.finance.POUNDS
import net.corda.finance.SWISS_FRANCS
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.workflows.getCashBalance
import net.corda.finance.workflows.getCashBalances
import net.corda.java.rpc.StandaloneCordaRPCJavaClientTest
import net.corda.nodeapi.internal.config.User
import net.corda.sleeping.SleepingFlow
import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeParams
import net.corda.smoketesting.NodeProcess
import org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM
import org.hamcrest.text.MatchesPattern
import org.junit.After
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.junit.rules.ExpectedException
import java.io.FilterInputStream
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.atomic.AtomicInteger
import java.util.regex.Pattern
import kotlin.io.path.Path
import kotlin.io.path.listDirectoryEntries
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
class StandaloneCordaRPClientTest {
private companion object {
companion object {
private val log = contextLogger()
val superUser = User("superUser", "test", permissions = setOf("ALL"))
val nonUser = User("nonUser", "test", permissions = emptySet())
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 port = AtomicInteger(15200)
const val attachmentSize = 2116
const val ATTACHMENT_SIZE = 2116
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 notaryNode: NodeInfo
private lateinit var rpcProxy: CordaRPCOps
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
val exception: ExpectedException = ExpectedException.none()
@Before
fun setUp() {
factory = NodeProcess.Factory()
StandaloneCordaRPCJavaClientTest.copyCordapps(factory, notaryConfig)
notary = factory.create(notaryConfig)
connection = notary.connect(superUser)
rpcProxy = connection.proxy
notaryNode = fetchNotaryIdentity()
notaryNodeIdentity = rpcProxy.nodeInfo().legalIdentitiesAndCerts.first().party
}
@After
fun done() {
connection.use {
notary.close()
}
fun closeConnection() {
connection.close()
}
@Test(timeout=300_000)
fun `test attachments`() {
val attachment = InputStreamAndHash.createInMemoryTestZip(attachmentSize, 1)
val attachment = InputStreamAndHash.createInMemoryTestZip(ATTACHMENT_SIZE, 1)
assertFalse(rpcProxy.attachmentExists(attachment.sha256))
val id = attachment.inputStream.use { rpcProxy.uploadAttachment(it) }
assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash")
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use { it ->
it.copyTo(NULL_OUTPUT_STREAM)
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use {
it.copyTo(nullOutputStream())
SecureHash.createSHA256(it.hash().asBytes())
}
assertEquals(attachment.sha256, hash)
@ -126,13 +141,13 @@ class StandaloneCordaRPClientTest {
@Ignore("CORDA-1520 - After switching from Kryo to AMQP this test won't work")
@Test(timeout=300_000)
fun `test wrapped attachments`() {
val attachment = InputStreamAndHash.createInMemoryTestZip(attachmentSize, 1)
val attachment = InputStreamAndHash.createInMemoryTestZip(ATTACHMENT_SIZE, 1)
assertFalse(rpcProxy.attachmentExists(attachment.sha256))
val id = WrapperStream(attachment.inputStream).use { rpcProxy.uploadAttachment(it) }
assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash")
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use { it ->
it.copyTo(NULL_OUTPUT_STREAM)
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use {
it.copyTo(nullOutputStream())
SecureHash.createSHA256(it.hash().asBytes())
}
assertEquals(attachment.sha256, hash)
@ -168,8 +183,7 @@ class StandaloneCordaRPClientTest {
@Test(timeout=300_000)
fun `test state machines`() {
val (stateMachines, updates) = rpcProxy.stateMachinesFeed()
assertEquals(0, stateMachines.size)
val (_, updates) = rpcProxy.stateMachinesFeed()
val updateLatch = CountDownLatch(1)
val updateCount = AtomicInteger(0)
@ -190,8 +204,9 @@ class StandaloneCordaRPClientTest {
@Test(timeout=300_000)
fun `test vault track by`() {
val (vault, vaultUpdates) = rpcProxy.vaultTrackBy<Cash.State>(paging = PageSpecification(DEFAULT_PAGE_NUM))
assertEquals(0, vault.totalStatesAvailable)
val initialGbpBalance = rpcProxy.getCashBalance(GBP)
val (_, vaultUpdates) = rpcProxy.vaultTrackBy<Cash.State>(paging = PageSpecification(DEFAULT_PAGE_NUM))
val updateLatch = CountDownLatch(1)
vaultUpdates.subscribe { update ->
@ -207,34 +222,35 @@ class StandaloneCordaRPClientTest {
// Check that this cash exists in the vault
val cashBalance = rpcProxy.getCashBalances()
log.info("Cash Balances: $cashBalance")
assertEquals(1, cashBalance.size)
assertEquals(629.POUNDS, cashBalance[Currency.getInstance("GBP")])
assertEquals(629.POUNDS, cashBalance[GBP]!! - initialGbpBalance)
}
@Test(timeout=300_000)
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 paging = PageSpecification(DEFAULT_PAGE_NUM, 10)
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)
assertEquals(1, queryResults.totalStatesAvailable)
assertEquals(1, queryResults.totalStatesAvailable - initialStateCount)
assertEquals(queryResults.states.first().state.data.amount.quantity, 629.POUNDS.quantity)
rpcProxy.startFlow(::CashPaymentFlow, 100.POUNDS, notaryNodeIdentity, true, notaryNodeIdentity).returnValue.getOrThrow()
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
val cashBalances = rpcProxy.getCashBalances()
log.info("Cash Balances: $cashBalances")
assertEquals(1, cashBalances.size)
assertEquals(629.POUNDS, cashBalances[Currency.getInstance("GBP")])
assertEquals(629.POUNDS, cashBalances[GBP]!! - initialGbpBalance)
}
@Test(timeout=300_000)

View File

@ -2,6 +2,7 @@ package net.corda.client.rpc
import net.corda.core.internal.uncheckedCast
import kotlin.reflect.KCallable
import kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
import kotlin.reflect.jvm.reflect
/**
@ -10,15 +11,19 @@ import kotlin.reflect.jvm.reflect
* different combinations of parameters.
*/
@OptIn(ExperimentalReflectionOnLambdas::class)
fun <A : Any, R> measure(a: Iterable<A>, f: (A) -> R) =
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) =
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) =
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) =
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)
fun `kotlin NPE`() = rpc {
assertThatThrownBy { it.kotlinNPE() }.isInstanceOf(CordaRuntimeException::class.java)
.hasMessageContaining("kotlin.KotlinNullPointerException")
.hasMessageContaining("java.lang.NullPointerException")
}
@Test(timeout=300_000)
fun `kotlin NPE async`() = rpc {
val future = it.kotlinNPEAsync()
assertThatThrownBy { future.getOrThrow() }.isInstanceOf(CordaRuntimeException::class.java)
.hasMessageContaining("kotlin.KotlinNullPointerException")
.hasMessageContaining("java.lang.NullPointerException")
}
@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'
apply plugin: 'com.jfrog.artifactory'
description 'Corda common-configuration-parsing module'
dependencies {
compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: kotlin_version
compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
implementation 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 "junit:junit:$junit_version"
@ -18,14 +17,20 @@ dependencies {
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
testCompile group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
testCompile group: "org.assertj", name: "assertj-core", version: assertj_version
testImplementation group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
testImplementation group: "org.assertj", name: "assertj-core", version: assertj_version
}
jar {
baseName 'corda-common-configuration-parsing'
}
publish {
name jar.baseName
}
publishing {
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.Companion.invalid
import java.time.Duration
import java.util.Locale
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 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())

View File

@ -61,7 +61,7 @@ class SpecificationTest {
override fun parseValid(configuration: Config, options: Configuration.Options): Valid<AtomicLong> {
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 }) {
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") {

View File

@ -1,28 +1,28 @@
import org.apache.tools.ant.filters.ReplaceTokens
apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'corda.common-publishing'
description 'Corda common-logging module'
dependencies {
compile group: "org.jetbrains.kotlin", name: "kotlin-stdlib-jdk8", version: kotlin_version
compile group: "org.jetbrains.kotlin", name: "kotlin-reflect", version: kotlin_version
implementation 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
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
testCompile project(":common-validation")
testImplementation project(":common-validation")
// test dependencies
testImplementation "junit:junit:$junit_version"
testCompile group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
testCompile "org.mockito:mockito-core:$mockito_version"
testCompile "com.natpryce:hamkrest:$hamkrest_version"
testImplementation group: "org.jetbrains.kotlin", name: "kotlin-test", version: kotlin_version
testImplementation "org.mockito:mockito-core:$mockito_version"
testImplementation "com.natpryce:hamkrest:$hamkrest_version"
}
@ -33,11 +33,19 @@ task generateSource(type: Copy) {
into 'src/main'
}
compileKotlin.dependsOn generateSource
processResources.dependsOn generateSource
sourcesJar.dependsOn generateSource
jar {
baseName 'corda-common-logging'
}
publish {
name jar.baseName
}
publishing {
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)
*/
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
import org.slf4j.Logger
import java.util.Locale
/**
* Report errors that have occurred.
@ -12,7 +13,7 @@ import org.slf4j.Logger
fun Logger.report(error: ErrorCode<*>) = ErrorReporting().getReporter().report(error, this)
internal fun ErrorCode<*>.formatCode() : String {
val namespaceString = this.code.namespace.toLowerCase().replace("_", "-")
val codeString = this.code.toString().toLowerCase().replace("_", "-")
val namespaceString = this.code.namespace.lowercase(Locale.getDefault()).replace("_", "-")
val codeString = this.code.toString().lowercase(Locale.getDefault()).replace("_", "-")
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'
apply plugin: 'com.jfrog.artifactory'
description 'Corda common-validation module'
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
jar {
baseName 'corda-common-validation'
}
publish {
name jar.baseName
}
publishing {
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.
// 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: 'net.corda.plugins.publish-utils'
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.cordapp'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'corda.common-publishing'
description 'Corda Experimental Confidential Identities'
dependencies {
cordaCompile project(':core')
cordapp {
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 "co.paralleluniverse:quasar-core:$quasar_version"
testImplementation "junit:junit:$junit_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}"
// 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.
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
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 {
baseName 'corda-confidential-identities'
}
publish {
name jar.baseName
publishing {
publications {
maven(MavenPublication) {
artifactId 'corda-confidential-identities'
from components.cordapp
artifact javadocJar
}
}
}

View File

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

View File

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

View File

@ -3,35 +3,32 @@
# their own projects. So don't get fancy with syntax!
# Fancy syntax - multi pass ${whatever} replacement
cordaVersion=4.11
cordaVersion=4.12
versionSuffix=SNAPSHOT
gradlePluginsVersion=5.0.12
kotlinVersion=1.2.71
java8MinUpdateVersion=171
cordaShellVersion=4.12-SNAPSHOT
gradlePluginsVersion=5.1.1
artifactoryContextUrl=https://software.r3.com/artifactory
internalPublishVersion=1.+
# ***************************************************************#
# When incrementing platformVersion make sure to update #
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
# ***************************************************************#
platformVersion=13
platformVersion=140
openTelemetryVersion=1.20.1
openTelemetrySemConvVersion=1.20.1-alpha
guavaVersion=28.0-jre
# Quasar version to use with Java 8:
quasarVersion=0.7.16_r3
# Quasar version to use with Java 11:
quasarVersion11=0.8.1_r3
jdkClassifier11=jdk11
guavaVersion=33.1.0-jre
quasarVersion=0.9.0_r3
dockerJavaVersion=3.2.5
proguardVersion=6.1.1
// bouncy castle version must not be changed on a patch release. Needs a full release test cycle to flush out any issues.
bouncycastleVersion=1.78.1
proguardVersion=7.3.1
# Switch to release version when out
bouncycastleVersion=2.73.6
classgraphVersion=4.8.135
disruptorVersion=3.4.2
typesafeConfigVersion=1.3.4
jsr305Version=3.0.2
artifactoryPluginVersion=4.16.1
snakeYamlVersion=1.33
caffeineVersion=2.9.3
snakeYamlVersion=2.2
caffeineVersion=3.1.8
metricsVersion=4.1.0
metricsNewRelicVersion=1.1.1
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.
# We must configure it manually to use the latest capsule version.
capsuleVersion=1.0.3
asmVersion=7.1
artemisVersion=2.19.1
capsuleVersion=1.0.4_r3
asmVersion=9.5
artemisVersion=2.36.0
# TODO Upgrade Jackson only when corda is using kotlin 1.3.10
jacksonVersion=2.17.2
jacksonKotlinVersion=2.9.7
jettyVersion=9.4.56.v20240826
jerseyVersion=2.25
jacksonKotlinVersion=2.17.0
jettyVersion=12.0.14
jerseyVersion=3.1.6
servletVersion=4.0.1
assertjVersion=3.12.2
slf4JVersion=1.7.30
log4JVersion=2.17.1
okhttpVersion=3.14.9
nettyVersion=4.1.77.Final
fileuploadVersion=1.4
kryoVersion=4.0.2
slf4JVersion=2.0.12
log4JVersion=2.23.1
okhttpVersion=4.12.0
nettyVersion=4.1.109.Final
fileuploadVersion=2.0.0-M1
kryoVersion=5.5.0
kryoSerializerVersion=0.43
# Legacy JUnit 4 version
junitVersion=4.12
@ -71,8 +68,8 @@ junitVersion=4.12
junitVintageVersion=5.5.0-RC1
junitJupiterVersion=5.5.0-RC1
junitPlatformVersion=1.5.0-RC1
mockitoVersion=2.28.2
mockitoKotlinVersion=1.6.0
mockitoVersion=5.5.0
mockitoKotlinVersion=5.2.1
hamkrestVersion=1.7.0.0
joptSimpleVersion=5.0.2
jansiVersion=1.18
@ -80,13 +77,12 @@ hibernateVersion=5.6.14.Final
# h2Version - Update docs if renamed or removed.
h2Version=2.2.224
rxjavaVersion=1.3.8
dokkaVersion=0.10.1
eddsaVersion=0.3.0
dokkaVersion=1.8.20
dependencyCheckerVersion=5.2.0
commonsCollectionsVersion=4.3
beanutilsVersion=1.9.4
shiroVersion=1.10.0
hikariVersion=4.0.3
hikariVersion=5.1.0
liquibaseVersion=4.20.0
dockerComposeRuleVersion=1.5.0
seleniumVersion=3.141.59
@ -97,13 +93,9 @@ protonjVersion=0.33.0
snappyVersion=0.5
jcabiManifestsVersion=1.1
picocliVersion=3.9.6
commonsLangVersion=3.9
commonsIoVersion=2.17.0
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
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,118 +0,0 @@
package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.sha256
import net.corda.core.internal.hash
import net.corda.core.serialization.CordaSerializable
import java.security.PublicKey
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
object CordaRotatedKeys {
val keys = RotatedKeys()
}
// The current development CorDapp code signing public key hash
const val DEV_CORDAPP_CODE_SIGNING_STR = "AA59D829F2CA8FDDF5ABEA40D815F937E3E54E572B65B93B5C216AE6594E7D6B"
// The non production CorDapp code signing public key hash
const val NEW_NON_PROD_CORDAPP_CODE_SIGNING_STR = "B710A80780A12C52DF8A0B4C2247E08907CCA5D0F19AB1E266FE7BAEA9036790"
// The production CorDapp code signing public key hash
const val PROD_CORDAPP_CODE_SIGNING_STR = "EB4989E7F861FEBEC242E6C24CF0B51C41E108D2C4479D296C5570CB8DAD3EE0"
// The new production CorDapp code signing public key hash
const val NEW_PROD_CORDAPP_CODE_SIGNING_STR = "01EFA14B42700794292382C1EEAC9788A26DAFBCCC98992C01D5BC30EEAACD28"
// Rotations used by Corda
private val CORDA_SIGNING_KEY_ROTATIONS = listOf(
listOf(SecureHash.create(DEV_CORDAPP_CODE_SIGNING_STR).sha256(), SecureHash.create(NEW_NON_PROD_CORDAPP_CODE_SIGNING_STR).sha256()),
listOf(SecureHash.create(PROD_CORDAPP_CODE_SIGNING_STR).sha256(), SecureHash.create(NEW_PROD_CORDAPP_CODE_SIGNING_STR).sha256())
)
/**
* This class represents the rotated CorDapp signing keys known by this node.
*
* A public key in this class is identified by its SHA-256 hash of the public key encoded bytes (@see PublicKey.getEncoded()).
* A sequence of rotated keys is represented by a list of hashes of those public keys. The list of those lists represents
* each unrelated set of rotated keys. A key should not appear more than once, either in the same list of in multiple lists.
*
* For the purposes of SignatureConstraints this means we treat all entries in a list of key hashes as equivalent.
* For two keys to be equivalent, they must be equal, or they must appear in the same list of hashes.
*
* @param rotatedSigningKeys A List of rotated keys. With a rotated key being represented by a list of hashes. This list comes from
* node.conf.
*
*/
@CordaSerializable
data class RotatedKeys(val rotatedSigningKeys: List<List<SecureHash>> = emptyList()) {
private val canBeTransitionedMap: ConcurrentMap<Pair<PublicKey, PublicKey>, Boolean> = ConcurrentHashMap()
private val rotateMap: Map<SecureHash, SecureHash> = HashMap<SecureHash, SecureHash>().apply {
(rotatedSigningKeys + CORDA_SIGNING_KEY_ROTATIONS).forEach { rotatedKeyList ->
rotatedKeyList.forEach { key ->
if (this.containsKey(key)) throw IllegalStateException("The key with sha256(hash) $key appears in the rotated keys configuration more than once.")
this[key] = rotatedKeyList.last()
}
}
}
fun canBeTransitioned(inputKey: PublicKey, outputKeys: List<PublicKey>): Boolean {
return canBeTransitioned(inputKey, CompositeKey.Builder().addKeys(outputKeys).build())
}
fun canBeTransitioned(inputKeys: List<PublicKey>, outputKeys: List<PublicKey>): Boolean {
return canBeTransitioned(CompositeKey.Builder().addKeys(inputKeys).build(), CompositeKey.Builder().addKeys(outputKeys).build())
}
fun canBeTransitioned(inputKey: PublicKey, outputKey: PublicKey): Boolean {
// Need to handle if inputKey and outputKey are composite keys. They could be if part of SignatureConstraints
return canBeTransitionedMap.getOrPut(Pair(inputKey, outputKey)) {
when {
(inputKey is CompositeKey && outputKey is CompositeKey) -> compareKeys(inputKey, outputKey)
(inputKey is CompositeKey && outputKey !is CompositeKey) -> compareKeys(inputKey, outputKey)
(inputKey !is CompositeKey && outputKey is CompositeKey) -> compareKeys(inputKey, outputKey)
else -> isRotatedEquals(inputKey, outputKey)
}
}
}
fun rotateToHash(key: PublicKey) = rotate(key.hash.sha256())
private fun rotate(key: SecureHash): SecureHash {
return rotateMap[key] ?: key
}
private fun isRotatedEquals(inputKey: PublicKey, outputKey: PublicKey): Boolean {
return when {
inputKey == outputKey -> true
rotate(inputKey.hash.sha256()) == rotate(outputKey.hash.sha256()) -> true
else -> false
}
}
private fun compareKeys(inputKey: CompositeKey, outputKey: PublicKey): Boolean {
if (inputKey.leafKeys.size == 1) {
return canBeTransitioned(inputKey.leafKeys.first(), outputKey)
}
return false
}
private fun compareKeys(inputKey: PublicKey, outputKey: CompositeKey): Boolean {
if (outputKey.leafKeys.size == 1) {
return canBeTransitioned(inputKey, outputKey.leafKeys.first())
}
return false
}
private fun compareKeys(inputKey: CompositeKey, outputKey: CompositeKey): Boolean {
if (inputKey.leafKeys.size != outputKey.leafKeys.size) {
return false
}
else {
inputKey.leafKeys.forEach { inputLeafKey ->
if (!outputKey.leafKeys.any { outputLeafKey -> canBeTransitioned(inputLeafKey, outputLeafKey) }) {
return false
}
}
return true
}
}
}

View File

@ -1,18 +1,21 @@
apply plugin: 'kotlin'
apply plugin: 'kotlin-jpa'
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'org.jetbrains.kotlin.plugin.jpa'
apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'idea'
description 'Corda core tests'
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
smokeTestCompile.extendsFrom compile
smokeTestImplementation.extendsFrom implementation
smokeTestRuntimeOnly.extendsFrom runtimeOnly
}
evaluationDependsOn(':node:capsule')
testArtifacts.extendsFrom testRuntimeOnlyClasspath
corda4_11
}
sourceSets {
integrationTest {
@ -41,64 +44,110 @@ sourceSets {
processSmokeTestResources {
// 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'
}
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) {
rename 'jackson-core-.*.jar', 'jackson-core.jar'
}
from(tasks.getByPath(":legacy411:jar")) {
rename 'legacy411-.*.jar', 'legacy411.jar'
}
from(tasks.getByPath(":legacy412:jar")) {
rename 'legacy412-.*.jar', 'legacy412.jar'
}
}
dependencies {
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 {
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 "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"
testImplementation "io.dropwizard.metrics:metrics-jmx:$metrics_version"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
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!
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 project(":legacy411")
smokeTestImplementation project(":legacy412")
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 "junit:junit:$junit_version"
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
smokeTestRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
smokeTestRuntimeOnly "org.slf4j:slf4j-simple:$slf4j_version"
smokeTestCompile project(':smoke-test-utils')
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
// used by FinalityFlowTests
testCompile project(':testing:cordapps:cashobservers')
}
configurations {
testArtifacts.extendsFrom testRuntimeClasspath
corda4_11 "net.corda:corda-finance-contracts:4.11"
corda4_11 "net.corda:corda-finance-workflows:4.11"
corda4_11 "net.corda:corda:4.11"
corda4_11 "com.fasterxml.jackson.core:jackson-core:2.17.2"
}
tasks.withType(Test).configureEach {
@ -106,27 +155,36 @@ tasks.withType(Test).configureEach {
forkEvery = 10
}
task testJar(type: Jar) {
tasks.register('testJar', Jar) {
classifier "tests"
from sourceSets.test.output
}
task integrationTest(type: Test) {
tasks.register('integrationTest', Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
task smokeTestJar(type: Jar) {
tasks.register('smokeTestJar', Jar) {
classifier 'smokeTests'
from sourceSets.smokeTest.output
from(sourceSets.smokeTest.output) {
exclude("*.jar")
}
}
task smokeTest(type: Test) {
tasks.register('smokeTest', Test) {
dependsOn smokeTestJar
testClassesDirs = sourceSets.smokeTest.output.classesDirs
classpath = sourceSets.smokeTest.runtimeClasspath
}
idea {
module {
downloadJavadoc = true
downloadSources = true
}
}
// quasar exclusions upon agent code instrumentation at run-time
quasar {
excludePackages.addAll(
@ -141,7 +199,6 @@ quasar {
"io.github.classgraph**",
"io.netty*",
"liquibase**",
"net.i2p.crypto.**",
"nonapi.io.github.classgraph.**",
"org.apiguardian.**",
"org.bouncycastle**",

View File

@ -0,0 +1,212 @@
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.identity.Party
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.BOB_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.core.internal.JarSignatureTestUtils.unsignJar
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.NodeHandle
import net.corda.testing.driver.NodeParameters
import net.corda.testing.node.NotarySpec
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),
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = false))
) {
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()
val nodeBob = startNode(NodeParameters(
BOB_NAME,
additionalCordapps = listOf(currentContracts),
legacyContracts = listOf(legacyContracts,legacyDependency)
)).getOrThrow()
val bobParty = nodeBob.nodeInfo.singleIdentity()
// First make sure the missing dependency causes an issue
assertThatThrownBy {
createTransaction(node, bobParty)
}.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, bobParty)
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, destination: Party = defaultNotaryIdentity): SignedTransaction {
return node.rpc.startFlow(
::CashIssueAndPaymentFlow,
1.DOLLARS,
OpaqueBytes.of(0x00),
destination,
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.StartableByRPC
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.utilities.getOrThrow
import net.corda.nodeapi.internal.config.User
import net.corda.smoketesting.NodeConfig
import net.corda.smoketesting.NodeParams
import net.corda.smoketesting.NodeProcess
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.nio.file.Paths
import java.util.concurrent.atomic.AtomicInteger
import java.util.jar.JarFile
import kotlin.streams.toList
import kotlin.io.path.Path
import kotlin.io.path.listDirectoryEntries
class NodeVersioningTest {
private companion object {
@ -27,58 +27,39 @@ class NodeVersioningTest {
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
@Before
fun setUp() {
notary = factory.create(notaryConfig)
fun startNotary() {
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
fun done() {
notary.close()
factory.close()
}
@Test(timeout=300_000)
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)
}
@Test(timeout=300_000)
fun `platform version from RPC`() {
val cordappsDir = (factory.baseDirectory(aliceConfig) / NodeProcess.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)
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)
}
notary.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 net.corda.core.crypto.Crypto.generateKeyPair
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.Party
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.*
import net.corda.core.messaging.startFlow
import net.corda.core.node.NodeInfo
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.crypto.CertificateType
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.Companion.CORDAPPS_DIR_NAME
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.nio.file.Path
import java.nio.file.Paths
import java.security.KeyPair
import java.security.PrivateKey
import java.security.PublicKey
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 {
private companion object {
@ -44,45 +51,35 @@ class CordappSmokeTest {
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(
private val aliceConfig = NodeParams(
legalName = CordaX500Name(organisation = "Alice Corp", locality = "Madrid", country = "ES"),
p2pPort = port.andIncrement,
rpcPort = 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
fun setUp() {
notary = factory.create(notaryConfig)
fun startNotary() {
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
fun done() {
notary.close()
factory.close()
}
@Test(timeout=300_000)
fun `FlowContent appName returns the filename of the CorDapp jar`() {
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
// 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()
createDummyNodeInfo(additionalNodeInfoDir)
factory.create(aliceConfig).use { alice ->
alice.connect(superUser).use { connectionToAlice ->
val aliceIdentity = connectionToAlice.proxy.nodeInfo().legalIdentitiesAndCerts.first().party
val future = connectionToAlice.proxy.startFlow(CordappSmokeTest::GatherContextsFlow, aliceIdentity).returnValue
val (sessionInitContext, sessionConfirmContext) = future.getOrThrow()
val selfCordappName = selfCordapp.fileName.toString().removeSuffix(".jar")
assertThat(sessionInitContext.appName).isEqualTo(selfCordappName)
assertThat(sessionConfirmContext.appName).isEqualTo(selfCordappName)
}
val alice = factory.createNode(aliceConfig)
alice.connect(superUser).use { connectionToAlice ->
val aliceIdentity = connectionToAlice.proxy.nodeInfo().legalIdentitiesAndCerts.first().party
val future = connectionToAlice.proxy.startFlow(CordappSmokeTest::GatherContextsFlow, aliceIdentity).returnValue
val (sessionInitContext, sessionConfirmContext) = future.getOrThrow()
val selfCordappName = aliceConfig.cordappJars[0].name.removeSuffix(".jar")
assertThat(sessionInitContext.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
@StartableByRPC
class GatherContextsFlow(private val otherParty: Party) : FlowLogic<Pair<FlowInfo, FlowInfo>>() {
@ -139,7 +129,7 @@ class CordappSmokeTest {
val dummyKeyPair = generateKeyPair()
val nodeInfo = createNodeInfoWithSingleIdentity(CordaX500Name(organisation = "Bob Corp", locality = "Madrid", country = "ES"), dummyKeyPair, dummyKeyPair.public)
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 {

View File

@ -0,0 +1,327 @@
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.contracts.StateRef
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.legacy.workflows.LegacyIssuanceFlow
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") }
val legacyJacksonCordapp411 = smokeTestResource("legacy411.jar")
val legacyJacksonCordapp412 = smokeTestResource("legacy412.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(legacyJacksonCordapp412),
listOf(legacyContractsCordapp, legacyJacksonCordapp411)
))
val legacyJars = currentNode.nodeDir / "legacy-jars"
legacyJars.toFile().mkdir()
val jacksonDestination = legacyJars / "jackson-core.jar"
val jacksonSource = smokeTestResource("jackson-core.jar")
jacksonSource.copyTo(jacksonDestination)
}
@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()
}
@Test(timeout = 300_000)
fun `transaction containing 4_11 and 4_12 contract referencing Jackson dependency issued on new node`() {
val issuanceStateRef = legacyJackonIssuance(currentNode)
currentNode.assertTransactionsWereVerified(BOTH, issuanceStateRef.txhash)
}
private fun legacyJackonIssuance(issuer: NodeProcess): StateRef {
val issuerRpc = issuer.connect(superUser).proxy
val issuanceStateRef = issuerRpc.startFlowDynamic(LegacyIssuanceFlow::class.java, 2).returnValue.getOrThrow() as StateRef
return issuanceStateRef
}
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"))
val legacyCordappsWithoutContracts = listOf(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, legacyCordapps))[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, legacyCordappsWithoutContracts))
}
@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)
assertEquals(partitionCount, splits.size)
assertEquals(baseAmount, splits.sumOrZero(baseAmount.token))
val min = splits.min()!!
val max = splits.max()!!
val min = splits.min()
val max = splits.max()
assertTrue(max.quantity - min.quantity <= 1L, "Amount quantities should differ by at most one token")
}
}

View File

@ -1,8 +1,5 @@
package net.corda.coretests.contracts
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.AlwaysAcceptAttachmentConstraint
import net.corda.core.contracts.AutomaticPlaceholderConstraint
import net.corda.core.contracts.BelongsToContract
@ -26,17 +23,16 @@ import net.corda.core.crypto.sign
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.CordaX500Name
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.toPath
import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.IdentityService
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.finance.POUNDS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.DUMMY_NOTARY_NAME
@ -48,7 +44,15 @@ import net.corda.testing.core.internal.SelfCleaningDir
import net.corda.testing.internal.MockCordappProvider
import net.corda.testing.node.MockServices
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.util.jar.Attributes
import kotlin.test.assertFailsWith
@ -103,8 +107,8 @@ class ConstraintsPropagationTests {
},
networkParameters = testNetworkParameters(minimumPlatformVersion = 4)
.copy(whitelistedContractImplementations = mapOf(
Cash.PROGRAM_ID to listOf(SecureHash.zeroHash, SecureHash.allOnesHash),
noPropagationContractClassName to listOf(SecureHash.zeroHash)),
Cash.PROGRAM_ID to listOf(zeroHash, allOnesHash),
noPropagationContractClassName to listOf(zeroHash)),
notaries = listOf(NotaryInfo(DUMMY_NOTARY, true)))
) {
override fun loadContractAttachment(stateRef: StateRef) = servicesForResolution.loadContractAttachment(stateRef)
@ -115,13 +119,13 @@ class ConstraintsPropagationTests {
fun `Happy path with the HashConstraint`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash)
attachment(Cash.PROGRAM_ID, allOnesHash)
input("c1")
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())
@ -137,13 +141,13 @@ class ConstraintsPropagationTests {
val cordappAttachmentIds =
cordapps.map { cordapp ->
val unsignedAttId =
cordapp.jarPath.toPath().inputStream().use { unsignedJarStream ->
cordapp.jarPath.openStream().use { unsignedJarStream ->
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", unsignedJarStream, null)
}
val jarAndSigner = ContractJarTestUtils.signContractJar(cordapp.jarPath, copyFirst = true, keyStoreDir = keyStoreDir.path)
val signedJar = jarAndSigner.first
val signedAttId =
signedJar.inputStream().use { signedJarStream ->
signedJar.read { signedJarStream ->
ledgerServices.attachments.importContractAttachment(cordapp.contractClassNames, "rpc", signedJarStream, null, listOf(jarAndSigner.second))
}
Pair(unsignedAttId, signedAttId)
@ -175,14 +179,14 @@ class ConstraintsPropagationTests {
fun `Fail early in the TransactionBuilder when attempting to change the hash of the HashConstraint on the spending transaction`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
}
assertFailsWith<IllegalArgumentException> {
transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash)
attachment(Cash.PROGRAM_ID, allOnesHash)
input("c1")
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())
@ -196,27 +200,27 @@ class ConstraintsPropagationTests {
fun `Transaction validation fails, when constraints do not propagate correctly`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
attachment(Cash.PROGRAM_ID, zeroHash)
input("c1")
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())
failsWith("are not propagated correctly")
})
ledgerServices.recordTransaction(transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
attachment(Cash.PROGRAM_ID, zeroHash)
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))
command(ALICE_PUBKEY, Cash.Commands.Move())
fails()
})
transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
attachment(Cash.PROGRAM_ID, zeroHash)
input("c1")
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())
@ -229,13 +233,13 @@ class ConstraintsPropagationTests {
fun `When the constraint of the output state is a valid transition from the input state, transaction validation works`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
attachment(Cash.PROGRAM_ID, zeroHash)
input("c1")
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())
@ -249,7 +253,7 @@ class ConstraintsPropagationTests {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
@ -257,7 +261,7 @@ class ConstraintsPropagationTests {
// the attachment is signed
transaction {
attachment(Cash.PROGRAM_ID, SecureHash.allOnesHash, listOf(hashToSignatureConstraintsKey))
attachment(Cash.PROGRAM_ID, allOnesHash, listOf(hashToSignatureConstraintsKey))
input("w1")
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())
@ -270,14 +274,14 @@ class ConstraintsPropagationTests {
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.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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
// the attachment is not signed
transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash)
attachment(Cash.PROGRAM_ID, zeroHash)
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))
command(ALICE_PUBKEY, Cash.Commands.Move())
@ -291,13 +295,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`() {
ledgerServices.ledger(DUMMY_NOTARY) {
ledgerServices.recordTransaction(transaction {
attachment(noPropagationContractClassName, SecureHash.zeroHash)
attachment(noPropagationContractClassName, zeroHash)
output(noPropagationContractClassName, "c1", DUMMY_NOTARY, null, HashAttachmentConstraint(zeroHash), NoPropagationContractState())
command(ALICE_PUBKEY, NoPropagationContract.Create())
verifies()
})
ledgerServices.recordTransaction(transaction {
attachment(noPropagationContractClassName, SecureHash.zeroHash)
attachment(noPropagationContractClassName, zeroHash)
input("c1")
output(noPropagationContractClassName, "c2", DUMMY_NOTARY, null, WhitelistedByZoneAttachmentConstraint, NoPropagationContractState())
command(ALICE_PUBKEY, NoPropagationContract.Create())
@ -305,7 +309,7 @@ class ConstraintsPropagationTests {
})
assertFailsWith<IllegalArgumentException> {
transaction {
attachment(noPropagationContractClassName, SecureHash.zeroHash)
attachment(noPropagationContractClassName, zeroHash)
input("c1")
output(noPropagationContractClassName, "c3", DUMMY_NOTARY, null, AutomaticPlaceholderConstraint, NoPropagationContractState())
command(ALICE_PUBKEY, NoPropagationContract.Create())
@ -400,13 +404,13 @@ class ConstraintsPropagationTests {
fun `Input state contract version may be incompatible with lower version`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
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")
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())
@ -419,13 +423,13 @@ class ConstraintsPropagationTests {
fun `Input state contract version is compatible with the same version`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
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")
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())
@ -438,13 +442,13 @@ class ConstraintsPropagationTests {
fun `Input state contract version is compatible with higher version`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
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")
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())
@ -457,13 +461,13 @@ class ConstraintsPropagationTests {
fun `Input states contract version may be lower that current contract version`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
@ -482,13 +486,13 @@ class ConstraintsPropagationTests {
fun `Input state with contract version can be downgraded to no version`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
transaction {
attachment(Cash.PROGRAM_ID, SecureHash.zeroHash, listOf(hashToSignatureConstraintsKey), emptyMap())
attachment(Cash.PROGRAM_ID, zeroHash, listOf(hashToSignatureConstraintsKey), emptyMap())
input("c1")
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())
@ -501,13 +505,13 @@ class ConstraintsPropagationTests {
fun `Input state without contract version is compatible with any version`() {
ledgerServices.ledger(DUMMY_NOTARY) {
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))
command(ALICE_PUBKEY, Cash.Commands.Issue())
verifies()
})
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")
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())

View File

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

View File

@ -1,11 +1,16 @@
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.CordaX500Name
import net.corda.core.identity.Party
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.runner.RunWith
import org.junit.runners.Parameterized
@ -35,8 +40,8 @@ class RequireSingleCommandTests(private val testFunction: (Collection<CommandWit
@JvmStatic
@Parameterized.Parameters(name = "{1}")
fun data(): Collection<Array<Any>> = listOf(
arrayOf<Any>({ 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>() }, "Inline 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")
}
@Test(expected = IllegalArgumentException::class, timeout=300_000)
@Test(timeout=300_000)
fun `check error is thrown if more than one valid command`() {
val commands = listOf(validCommandOne, validCommandTwo)
testFunction(commands)
assertThatIllegalArgumentException().isThrownBy {
testFunction(commands)
}
}
@Test(timeout=300_000)
fun `check error is thrown when command is of wrong type`() {
val commands = listOf(invalidCommand)
Assertions.assertThatThrownBy { testFunction(commands) }
assertThatThrownBy { testFunction(commands) }
.isInstanceOf(IllegalStateException::class.java)
.hasMessage("Required net.corda.coretests.contracts.TestCommands command")
}
@ -69,8 +76,8 @@ class SelectWithSingleInputsTests(private val testFunction: (Collection<CommandW
@JvmStatic
@Parameterized.Parameters(name = "{1}")
fun data(): Collection<Array<Any>> = listOf(
arrayOf<Any>({ 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>(signer, party) }, "Inline 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
@Parameterized.Parameters(name = "{1}")
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<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>(signers, party) }, "Inline 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
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import net.corda.core.contracts.*
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
@ -110,4 +110,4 @@ class DummyContract : Contract {
}
}
class DummyIssue : TypeOnlyCommandData()
class DummyIssue : TypeOnlyCommandData()

View File

@ -1,31 +1,16 @@
package net.corda.coretests.contracts
import net.corda.core.contracts.CordaRotatedKeys
import net.corda.core.contracts.RotatedKeys
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.sha256
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.hash
import net.corda.core.internal.retrieveRotatedKeys
import net.corda.core.node.ServiceHub
import net.corda.testing.core.TestIdentity
import net.corda.testing.core.internal.JarSignatureTestUtils.generateKey
import net.corda.testing.core.internal.SelfCleaningDir
import net.corda.testing.node.MockServices
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class RotatedKeysTest {
@Test(timeout = 300_000)
fun `validateDefaultRotatedKeysAreRetrievableFromMockServices`() {
val services: ServiceHub = MockServices(TestIdentity(CordaX500Name("MegaCorp", "London", "GB")))
val rotatedKeys = services.retrieveRotatedKeys()
assertEquals( CordaRotatedKeys.keys.rotatedSigningKeys, rotatedKeys.rotatedSigningKeys)
}
@Test(timeout = 300_000)
fun `when input and output keys are the same canBeTransitioned returns true`() {
SelfCleaningDir().use { file ->

View File

@ -3,7 +3,6 @@ package net.corda.coretests.crypto
import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey.NodeAndWeight
import net.corda.core.internal.declaredField
import net.corda.core.internal.div
import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toBase58String
@ -19,6 +18,7 @@ import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.security.PublicKey
import javax.security.auth.x500.X500Principal
import kotlin.io.path.div
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
@ -295,21 +295,19 @@ class CompositeKeyTests {
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
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 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 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()) }
// One signature is missing.
val signaturesWithoutRSA = listOf(K1Signature, R1Signature, EdSignature, SPSignature)
val signaturesWithoutRSA = listOf(K1Signature, R1Signature, EdSignature)
assertFalse { compositeKey.isFulfilledBy(signaturesWithoutRSA.byKeys()) }
}
@ -320,20 +318,18 @@ class CompositeKeyTests {
val keyPairK1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val keyPairR1 = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
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 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 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()) }
// One signature is missing.
val signaturesWithoutRSA = listOf(K1Signature, R1Signature, EdSignature, SPSignature)
val signaturesWithoutRSA = listOf(K1Signature, R1Signature, EdSignature)
assertFalse { compositeKey.isFulfilledBy(signaturesWithoutRSA.byKeys()) }
// Create self sign CA.
@ -374,13 +370,12 @@ class CompositeKeyTests {
val (_, pub3) = Crypto.generateKeyPair(Crypto.RSA_SHA256)
val (_, pub4) = Crypto.generateKeyPair(Crypto.EDDSA_ED25519_SHA512)
val (_, pub5) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val (_, pub6) = Crypto.generateKeyPair(Crypto.SPHINCS256_SHA256)
val (_, pub7) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
val (_, pub6) = Crypto.generateKeyPair(Crypto.ECDSA_SECP256K1_SHA256)
// 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.
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.
assertEquals(composite1.children, composite2.children)
}

View File

@ -1,11 +1,19 @@
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.*
import net.corda.core.crypto.*
import net.corda.core.contracts.Command
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.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.keys
import net.corda.core.crypto.randomHash
import net.corda.core.crypto.sha256
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
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.transactions.ReferenceStateRef
import net.corda.core.transactions.WireTransaction
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME
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.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.testing.internal.createWireTransaction
import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
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.util.function.Predicate
import java.util.stream.IntStream
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)
class PartialMerkleTreeTest(private var digestService: DigestService) {
@ -204,7 +222,7 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
@Test(timeout=300_000)
fun `nothing filtered`() {
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
val ftxNothing = testTx.buildFilteredTransaction { false }
assertTrue(ftxNothing.componentGroups.isEmpty())
assertTrue(ftxNothing.attachments.isEmpty())
assertTrue(ftxNothing.commands.isEmpty())
@ -291,10 +309,12 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
assertFalse(pmt.verify(wrongRoot, inclHashes))
}
@Test(expected = Exception::class, timeout=300_000)
@Test(timeout=300_000)
fun `hash map serialization not allowed`() {
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
hm1.serialize()
assertThatIllegalArgumentException().isThrownBy {
hm1.serialize()
}
}
private fun makeSimpleCashWtx(
@ -322,11 +342,11 @@ class PartialMerkleTreeTest(private var digestService: DigestService) {
val merkleTree = MerkleTree.getMerkleTree(sampleLeaves, digestService)
// 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.
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.
assertEquals(0, pmt.leafIndex(digestService.hash("0")))
// 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).
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")))
// The provided hash is not in the tree.
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")))
// The provided hash is not in the tree.
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")))
// The provided hash is not in the tree.
assertFailsWith<MerkleTreeException> { pmtOneElement.leafIndex(digestService.hash("10")) }

View File

@ -1,17 +1,14 @@
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.PrivacySalt
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow
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.DigestService
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
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.WireTransaction
import net.corda.core.utilities.OpaqueBytes
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME
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.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.testing.internal.createWireTransaction
import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
@ -49,6 +47,9 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
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.util.function.Predicate
import java.util.stream.IntStream
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
@Test(timeout=300_000)
fun `nothing filtered`() {
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
val ftxNothing = testTx.buildFilteredTransaction { false }
assertTrue(ftxNothing.componentGroups.isEmpty())
assertTrue(ftxNothing.attachments.isEmpty())
assertTrue(ftxNothing.commands.isEmpty())
@ -296,10 +297,12 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
assertFalse(pmt.verify(wrongRoot, inclHashes))
}
@Test(expected = Exception::class, timeout=300_000)
@Test(timeout=300_000)
fun `hash map serialization not allowed`() {
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
hm1.serialize()
assertThatIllegalArgumentException().isThrownBy {
hm1.serialize()
}
}
private fun makeSimpleCashWtx(
@ -381,4 +384,4 @@ class PartialMerkleTreeWithNamedHashMultiAlgTreeTest {
assertEquals(1, ftx.references.size)
ftx.verify()
}
}
}

View File

@ -1,17 +1,14 @@
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.PrivacySalt
import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TimeWindow
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.DigestService
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.SecureHash.Companion.SHA2_384
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.WireTransaction
import net.corda.core.utilities.OpaqueBytes
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.finance.DOLLARS
import net.corda.finance.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.`issued by`
import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME
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.TestLedgerDSLInterpreter
import net.corda.testing.dsl.TestTransactionDSLInterpreter
import net.corda.coretesting.internal.TEST_TX_TIME
import net.corda.testing.internal.createWireTransaction
import net.corda.testing.node.MockServices
import net.corda.testing.node.ledger
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
@ -49,6 +47,9 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
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.util.function.Predicate
import java.util.stream.IntStream
@ -209,7 +210,7 @@ class PartialMerkleTreeWithNamedHashTest {
@Test(timeout=300_000)
fun `nothing filtered`() {
val ftxNothing = testTx.buildFilteredTransaction(Predicate { false })
val ftxNothing = testTx.buildFilteredTransaction { false }
assertTrue(ftxNothing.componentGroups.isEmpty())
assertTrue(ftxNothing.attachments.isEmpty())
assertTrue(ftxNothing.commands.isEmpty())
@ -296,10 +297,12 @@ class PartialMerkleTreeWithNamedHashTest {
assertFalse(pmt.verify(wrongRoot, inclHashes))
}
@Test(expected = Exception::class, timeout=300_000)
@Test(timeout=300_000)
fun `hash map serialization not allowed`() {
val hm1 = hashMapOf("a" to 1, "b" to 2, "c" to 3, "e" to 4)
hm1.serialize()
assertThatIllegalArgumentException().isThrownBy {
hm1.serialize()
}
}
private fun makeSimpleCashWtx(
@ -381,4 +384,4 @@ class PartialMerkleTreeWithNamedHashTest {
assertEquals(1, ftx.references.size)
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.serialize
import net.corda.testing.core.SerializationEnvironmentRule
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -35,12 +36,14 @@ class SignedDataTest {
assertEquals(data, unwrappedData)
}
@Test(expected = SignatureException::class, timeout=300_000)
@Test(timeout=300_000)
fun `make sure incorrectly signed data raises an exception`() {
val keyPairA = generateKeyPair()
val keyPairB = generateKeyPair()
val sig = keyPairA.private.sign(serialized.bytes, keyPairB.public)
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
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 org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Rule
import org.junit.Test
import java.math.BigInteger
@ -39,12 +49,14 @@ class TransactionSignatureTest {
}
/** 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`() {
val keyPair = Crypto.generateKeyPair("ECDSA_SECP256K1_SHA256")
val signableData = SignableData(testBytes.sha256(), SignatureMetadata(1, Crypto.findSignatureScheme(keyPair.public).schemeNumberID))
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)

View File

@ -1,6 +1,7 @@
package net.corda.coretests.flows
import co.paralleluniverse.fibers.Suspendable
import co.paralleluniverse.strands.Strand
import net.corda.core.CordaException
import net.corda.core.flows.FlowExternalAsyncOperation
import net.corda.core.flows.FlowExternalOperation
@ -133,7 +134,7 @@ abstract class AbstractFlowExternalOperationTest {
fun createFuture(): CompletableFuture<Any> {
return CompletableFuture.supplyAsync(Supplier<Any> {
log.info("Starting sleep inside of future")
Thread.sleep(1000)
Strand.sleep(1000)
log.info("Finished sleep inside of future")
"Here is your return value"
}, executorService)
@ -255,4 +256,4 @@ abstract class AbstractFlowExternalOperationTest {
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.TransactionBuilder
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.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.TestIdentity
import net.corda.testing.core.singleIdentity
import net.corda.coretesting.internal.matchers.flow.willReturn
import net.corda.coretesting.internal.matchers.flow.willThrow
import net.corda.testing.node.MockServices
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.TestStartedNode
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.Assert
import org.junit.Test
import java.security.PublicKey
@ -92,7 +92,7 @@ class CollectSignaturesFlowTests : WithContracts {
mockNet.runNetwork()
val stx = future.get()
val missingSigners = stx.getMissingSigners()
Assert.assertThat(missingSigners, `is`(emptySet()))
assertThat(missingSigners).isEmpty()
}
@Test(timeout=300_000)
@ -122,10 +122,10 @@ class CollectSignaturesFlowTests : WithContracts {
mockNet.runNetwork()
val stx = future.get()
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`() {
bobNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
charlieNode.registerInitiatedFlow(ExtraSessionsFlowResponder::class.java)
@ -137,7 +137,9 @@ class CollectSignaturesFlowTests : WithContracts {
listOf(bobNode.info.singleIdentity(), alice)))
.resultFuture
mockNet.runNetwork()
future.getOrThrow()
assertThatIllegalArgumentException().isThrownBy {
future.getOrThrow()
}
}
@Test(timeout=300_000)
@ -152,7 +154,7 @@ class CollectSignaturesFlowTests : WithContracts {
listOf(bobNode.info.singleIdentity(), alice))).resultFuture
mockNet.runNetwork()
val signedTx = future.getOrThrow()
Assert.assertThat(signedTx.getMissingSigners(), `is`(emptySet()))
assertThat(signedTx.getMissingSigners()).isEmpty()
}
@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>() {
@Suspendable
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 create = net.corda.testing.contracts.DummyContract.Commands.Create()
val create = DummyContract.Commands.Create()
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
.addOutputState(state)
.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 create = net.corda.testing.contracts.DummyContract.Commands.Create()
val create = DummyContract.Commands.Create()
val txBuilder = TransactionBuilder(notary = serviceHub.networkMapCache.notaryIdentities.first())
.addOutputState(state)
.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 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())
.addOutputState(state)
.addCommand(create, involve.map { it.owningKey })

View File

@ -9,19 +9,27 @@ import net.corda.core.CordaRuntimeException
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.flows.ContractUpgradeFlow
import net.corda.core.internal.getRequiredTransaction
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.transactions.ContractUpgradeLedgerTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.coretesting.internal.matchers.rpc.willReturn
import net.corda.coretesting.internal.matchers.rpc.willThrow
import net.corda.node.services.Permissions.Companion.startFlow
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyContractV2
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity
import net.corda.coretesting.internal.matchers.rpc.willReturn
import net.corda.coretesting.internal.matchers.rpc.willThrow
import net.corda.testing.node.User
import net.corda.testing.node.internal.*
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
import net.corda.testing.node.internal.InternalMockNetwork
import net.corda.testing.node.internal.RPCDriverDSL
import net.corda.testing.node.internal.TestStartedNode
import net.corda.testing.node.internal.enclosedCordapp
import net.corda.testing.node.internal.rpcDriver
import net.corda.testing.node.internal.rpcTestUser
import net.corda.testing.node.internal.startRpcClient
import org.junit.AfterClass
import org.junit.Test
@ -120,8 +128,7 @@ class ContractUpgradeFlowRPCTest : WithContracts, WithFinality {
isUpgrade<FROM, TO>())
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
services.validatedTransactions.getTransaction(state.ref.txhash)!!
.resolveContractUpgradeTransaction(services)
services.getRequiredTransaction(state.ref.txhash).resolveContractUpgradeTransaction(services)
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()

View File

@ -1,31 +1,54 @@
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 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.identity.AbstractParty
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.LedgerTransaction
import net.corda.core.utilities.OpaqueBytes
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.`issued by`
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueFlow
import net.corda.finance.`issued by`
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyContractV2
import net.corda.testing.contracts.DummyContractV3
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity
import net.corda.coretesting.internal.matchers.flow.willReturn
import net.corda.coretesting.internal.matchers.flow.willThrow
import net.corda.testing.node.internal.*
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
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.Test
import java.util.*
import java.util.Currency
class ContractUpgradeFlowTest : WithContracts, WithFinality {
@ -159,7 +182,7 @@ class ContractUpgradeFlowTest : WithContracts, WithFinality {
@BelongsToContract(CashV2::class)
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> {
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 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>())
private fun TestStartedNode.getContractUpgradeTransaction(state: StateAndRef<ContractState>) =
services.validatedTransactions.getTransaction(state.ref.txhash)!!
.resolveContractUpgradeTransaction(services)
services.getRequiredTransaction(state.ref.txhash).resolveContractUpgradeTransaction(services)
private inline fun <reified FROM : Any, reified TO : Any> isUpgrade() =
isUpgradeFrom<FROM>() and isUpgradeTo<TO>()

View File

@ -13,7 +13,7 @@ import net.corda.core.internal.rootCause
import net.corda.core.utilities.getOrThrow
import org.assertj.core.api.Assertions.catchThrowable
import org.hamcrest.Matchers.lessThanOrEqualTo
import org.junit.Assert.assertThat
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Test
import java.util.*
import java.util.concurrent.ExecutorService
@ -85,7 +85,7 @@ class FastThreadLocalTest {
}
private class UnserializableObj {
@Suppress("unused")
@Suppress("unused", "IMPLICIT_NOTHING_TYPE_ARGUMENT_IN_RETURN_POSITION")
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.driver
import org.junit.Test
import java.io.Serializable
import java.sql.SQLTransientConnectionException
import java.util.concurrent.CompletableFuture
import kotlin.test.assertFailsWith
@ -22,6 +23,8 @@ import kotlin.test.assertTrue
class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
private fun interface SerializableLambda2<S, T, R> : (S, T) -> R, Serializable
@Test(timeout = 300_000)
fun `external async operation`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
@ -196,15 +199,15 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
@StartableByRPC
class FlowWithExternalAsyncOperationPropagatesException<T>(party: Party, private val exceptionType: Class<T>) :
FlowWithExternalProcess(party) {
@Suspendable
override fun testCode(): Any {
val e = createException()
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
return await(ExternalAsyncOperation(serviceHub, (SerializableLambda2 { _, _ ->
CompletableFuture<Any>().apply {
completeExceptionally(e)
}
})
})))
}
private fun createException() = when (exceptionType) {
@ -252,7 +255,6 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
@StartableByRPC
class FlowWithExternalAsyncOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
@Suppress("TooGenericExceptionCaught")
@Suspendable
override fun testCode(): Any {
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
@ -287,4 +289,4 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
serviceHub.cordaService(FutureService::class.java).startMultipleFuturesAndJoin()
}.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.node.internal.cordappsForPackages
import org.junit.Test
import java.io.Serializable
import java.sql.SQLTransientConnectionException
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
private fun interface SerializableLambda2<S, T, R> : (S, T) -> R, Serializable
@Test(timeout = 300_000)
fun `external operation`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
@ -254,7 +257,7 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@Suspendable
override fun testCode() {
val e = createException()
await(ExternalOperation(serviceHub) { _, _ -> throw e })
await<Nothing>(ExternalOperation(serviceHub, (SerializableLambda2 { _, _ -> throw e })))
}
private fun createException() = when (exceptionType) {
@ -270,7 +273,7 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@Suspendable
override fun testCode(): Any = try {
await(ExternalOperation(serviceHub) { _, _ ->
await<Nothing>(ExternalOperation(serviceHub) { _, _ ->
throw IllegalStateException("threw exception in background process")
})
} catch (e: IllegalStateException) {
@ -284,7 +287,7 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@Suspendable
override fun testCode(): Any =
await(ExternalOperation(serviceHub) { serviceHub, _ ->
await<Nothing>(ExternalOperation(serviceHub) { serviceHub, _ ->
serviceHub.cordaService(FutureService::class.java).throwHospitalHandledException()
})
}
@ -292,11 +295,10 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@StartableByRPC
class FlowWithExternalOperationThatDirectlyAccessesServiceHubFailsRetry(party: Party) : FlowWithExternalProcess(party) {
@Suppress("TooGenericExceptionCaught")
@Suspendable
override fun testCode(): Any {
try {
await(ExternalOperation(serviceHub) { _, _ ->
await<Nothing>(ExternalOperation(serviceHub) { _, _ ->
serviceHub.cordaService(FutureService::class.java).throwHospitalHandledException()
})
} catch (e: NullPointerException) {
@ -430,4 +432,4 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
})
}
}
}
}

View File

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

View File

@ -58,7 +58,7 @@ class ReceiveMultipleFlowTests : WithMockNet {
assertEquals(message, receivedMessage)
session.send(answer)
}
} as FlowLogic<Unit>
}
}
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.identity.Party
import net.corda.core.internal.FlowStateMachineHandle
import net.corda.core.internal.getRequiredTransaction
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.startFlow
@ -26,9 +27,7 @@ interface WithFinality : WithMockNet {
return startFlowAndRunNetwork(FinalityInvoker(stx, recipients.toSet(), emptySet()))
}
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction {
return services.validatedTransactions.getTransaction(stx.id)!!
}
fun TestStartedNode.getValidatedTransaction(stx: SignedTransaction): SignedTransaction = services.getRequiredTransaction(stx.id)
fun CordaRPCOps.finalise(stx: SignedTransaction, vararg recipients: Party): FlowHandle<SignedTransaction> {
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 " +
"is older than the epoch (${params2.epoch}) of input state: ${stx2.inputs.first()}")
}
}
}

View File

@ -240,6 +240,7 @@ class ResolveTransactionsFlowTest {
}
@Test(timeout=300_000)
@Ignore("Need to pass legacy contracts to internal mock network & need to create a legacy contract for test below")
fun `can resolve a chain of transactions containing a contract upgrade transaction`() {
val tx = contractUpgradeChain()
var numUpdates = 0
@ -258,7 +259,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.
@Test(timeout=300_000)
@Ignore
@Ignore
fun `Can resolve large chain of transactions`() {
val txToResolve = makeLargeTransactionChain(2500)
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
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
import net.corda.core.crypto.generateKeyPair
import net.corda.core.internal.getPackageOwnerOf
import net.corda.core.node.NetworkParameters

View File

@ -15,7 +15,7 @@ class VaultUpdateTests {
private companion object {
const val DUMMY_PROGRAM_ID = "net.corda.coretests.node.VaultUpdateTests\$DummyContract"
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 {

View File

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

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