mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
Merge branch 'release/os/4.12' into merge-release/os/4.11-release/os/4.12-2024-11-28-469
This commit is contained in:
commit
7caafd626f
3958
.ci/api-current.txt
3958
.ci/api-current.txt
File diff suppressed because it is too large
Load Diff
@ -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}
|
@ -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 */
|
||||
}
|
||||
}
|
||||
}
|
@ -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 */
|
||||
}
|
||||
}
|
||||
}
|
4
.ci/dev/forward-merge/Jenkinsfile
vendored
4
.ci/dev/forward-merge/Jenkinsfile
vendored
@ -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
|
||||
|
2
.ci/dev/nightly-regression/Jenkinsfile
vendored
2
.ci/dev/nightly-regression/Jenkinsfile
vendored
@ -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 {
|
||||
|
36
.ci/dev/pr-code-checks/Jenkinsfile
vendored
36
.ci/dev/pr-code-checks/Jenkinsfile
vendored
@ -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 */
|
||||
}
|
||||
|
1
.ci/dev/publish-api-docs/Jenkinsfile
vendored
1
.ci/dev/publish-api-docs/Jenkinsfile
vendored
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
4
.ci/dev/regression/Jenkinsfile
vendored
4
.ci/dev/regression/Jenkinsfile
vendored
@ -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(' ')
|
||||
}
|
||||
|
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -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! :)
|
||||
|
2
.github/workflows/check-pr-title.yml
vendored
2
.github/workflows/check-pr-title.yml
vendored
@ -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 }}"
|
||||
|
@ -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
20
Jenkinsfile
vendored
@ -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') {
|
||||
|
19
README.md
19
README.md
@ -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
|
||||
|
||||
|
370
build.gradle
370
build.gradle
@ -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,7 +44,7 @@ buildscript {
|
||||
'org.junit**',
|
||||
'org.slf4j**',
|
||||
'worker.org.gradle.**',
|
||||
'com.nhaarman.mockito_kotlin**',
|
||||
'org.mockito.kotlin**',
|
||||
'org.assertj**',
|
||||
'org.hamcrest**',
|
||||
'org.mockito**',
|
||||
@ -96,7 +91,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 +111,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 +118,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 +155,7 @@ buildscript {
|
||||
content {
|
||||
includeGroupByRegex 'net\\.corda(\\..*)?'
|
||||
includeGroupByRegex 'com\\.r3(\\..*)?'
|
||||
includeGroup 'co.paralleluniverse'
|
||||
}
|
||||
}
|
||||
maven {
|
||||
@ -181,7 +165,6 @@ buildscript {
|
||||
includeGroupByRegex 'com\\.r3(\\..*)?'
|
||||
}
|
||||
}
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "${publicArtifactURL}/jcenter-backup"
|
||||
@ -189,28 +172,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 +196,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 +222,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 +243,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 +257,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 +282,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 +308,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 +325,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 +377,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 +416,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 +448,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 +459,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 +470,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 +510,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 +541,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 +570,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 +586,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 +643,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
16
buildCacheSettings.gradle
Normal 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
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
92
buildSrc/src/main/groovy/corda.common-publishing.gradle
Normal file
92
buildSrc/src/main/groovy/corda.common-publishing.gradle
Normal 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')
|
||||
}
|
||||
}
|
||||
}
|
91
buildSrc/src/main/groovy/corda.kotlin-1.2.gradle
Normal file
91
buildSrc/src/main/groovy/corda.kotlin-1.2.gradle
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
6
buildSrc/src/main/groovy/corda.root-publish.gradle
Normal file
6
buildSrc/src/main/groovy/corda.root-publish.gradle
Normal 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')
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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])) }
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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") {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
@ -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"
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,4 +129,4 @@ class IdentitySyncFlowTests {
|
||||
otherSideSession.send(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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.13.5
|
||||
jacksonKotlinVersion=2.9.7
|
||||
jettyVersion=9.4.56.v20240826
|
||||
jerseyVersion=2.25
|
||||
jacksonVersion=2.17.2
|
||||
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
4
core-1.2/README.md
Normal 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
34
core-1.2/build.gradle
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
5
core-1.2/src/main/kotlin/kotlin/CharCode1.2.kt
Normal file
5
core-1.2/src/main/kotlin/kotlin/CharCode1.2.kt
Normal 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()
|
@ -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
|
||||
}
|
37
core-1.2/src/main/kotlin/kotlin/io/path/KotlinIoPath1.2.kt
Normal file
37
core-1.2/src/main/kotlin/kotlin/io/path/KotlinIoPath1.2.kt
Normal 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)
|
24
core-1.2/src/main/kotlin/kotlin/text/CharJVM1.2.kt
Normal file
24
core-1.2/src/main/kotlin/kotlin/text/CharJVM1.2.kt
Normal 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()
|
||||
}
|
||||
|
12
core-1.2/src/main/kotlin/kotlin/text/StringBuilder1.2.kt
Normal file
12
core-1.2/src/main/kotlin/kotlin/text/StringBuilder1.2.kt
Normal 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()
|
8
core-1.2/src/main/kotlin/kotlin/text/Strings1.2.kt
Normal file
8
core-1.2/src/main/kotlin/kotlin/text/Strings1.2.kt
Normal 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
|
||||
}
|
12
core-1.2/src/main/kotlin/kotlin/text/StringsJVM1.2.kt
Normal file
12
core-1.2/src/main/kotlin/kotlin/text/StringsJVM1.2.kt
Normal 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)
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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**",
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -107,4 +107,4 @@ class ContractHierarchyTest {
|
||||
subFlow(FinalityFlow(tx, otherSideSession))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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 ->
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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")) }
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 })
|
||||
|
@ -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>()
|
||||
|
@ -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>()
|
||||
|
@ -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.") }
|
||||
}
|
||||
|
||||
|
@ -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") })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user